为什么 DPDK VirtIO 只能跑到 40Gbps?—— 一次 KVM 虚拟交换机性能瓶颈定位全过程(下) 📅 2026/7/17 9:25:49 接上文为什么 DPDK VirtIO 只能跑到 40Gbps—— 一次 KVM 虚拟交换机性能瓶颈定位全过程上-CSDN博客七、Split Virtqueue为什么一个队列要维护三张环定位到 Virtqueue 之后我们继续深入分析。很多开发者第一次看到 VirtIO 的数据结构都会疑惑为什么普通网卡只有一个 Descriptor Ring而 VirtIO 却维护三张 Ring实际上在Split Virtqueue中真正的数据结构包括Descriptor Table Available Ring Used Ring三者职责完全不同。整体关系如下Guest Driver │ ▼ Descriptor Table 描述Buffer地址 │ ▼ Available Ring 告诉Host哪些Descriptor可以用了 │ ▼ Host(vhost) 处理数据 │ ▼ Used Ring 告诉Guest哪些Descriptor已经处理完成这里最重要的一点是Descriptor Table 本身不会通知任何人。真正负责发布消息的是avail-idx以及used-idx直到这里。我们才真正理解为什么上一篇 Memory Ordering 会如此重要。VirtIO 的整个同步机制本质上也是先构造对象再发布 Index。八、为什么小包性能下降如此明显继续分析 Perf。我们发现64Byte包CPU大量时间花在virtqueue_notify() ↓ eventfd_write() ↓ KVM ↓ VM Exit为什么假设Burst只有1 Packet流程Guest ↓ Descriptor ↓ avail-idx ↓ Kick ↓ VM Exit ↓ Host 处理 ↓ Notify ↓ VM Entry ↓ 继续一次Packet。一次VM Exit。一次VM Entry。CPU大量时间浪费在虚拟化切换。而1500Byte情况不同。虽然Packet数量减少。但是每个Packet携带的数据更多。最终100Gbps对应PPS大幅下降。例如包长PPS(约)64Byte148.8 Mpps512Byte24 Mpps1500Byte8.1 Mpps也就是说VirtIO真正限制的是PPS。不是Gbps。九、Event IndexVirtIO 最大的优化之一Linux VirtIO 后来增加了Event Index很多人觉得只是一个Flag。实际上它改变了整个Notification模型。传统模式每次 avail ↓ KickHost不停收到Notify。CPU不断VM Exit Event Index则变成Burst 32 Packet ↓ 一次 KickHost收到一次Notification。继续处理32 个Descriptor。CPU VM Exit次数下降几十倍。很多云厂商VirtIO性能能够突破80Gbps。最重要原因之一就是Event Index。十、Packed Virtqueue 为什么越来越流行虽然Split Virtqueue已经使用很多年。但是越来越多的新平台开始默认Packed Virtqueue。原因只有两个字Cache。Split模式需要CPU不停访问Descriptor ↓ Available Ring ↓ Used Ring三个不同区域。CPUCache不断Miss。Packed则把Descriptor状态直接放在同一结构。例如Descriptor Flags Wrap CounterCPU几乎连续访问内存。Cache命中率显著提高。不仅减少Cache Miss。同时减少Memory Barrier。因此新版本VirtIO几乎全部推荐Packed。十一、DPDK vhost-user 为什么还能继续优化很多人认为开启vhost-user性能已经最佳。实际上DPDK还做了很多优化。例如批量处理Descriptor。不是1 Packet ↓ 1 Descriptor而是Burst32 ↓ 32 Descriptor随后统一更新used-idx统一Kick。这样Notification次数再次下降。DPDKlibrte_vhost还会结合批量收发、cache-friendly 的描述符遍历以及尽可能减少 Guest/Host 间同步次数从而降低控制路径开销。十二、问题最终是如何解决的最终整个系统进行了如下优化①开启Packed Virtqueue。②开启Event Index。③Burst由16 ↓ 32④Guest开启Multi Queue。⑤Host每个Queue绑定独立PMD。再次测试结果如下优化前优化后38Gbps91GbpsCPU 48%CPU 63%VM Exit 极高下降约70%Notify 极频繁显著减少可以看到CPU反而更忙。但是吞吐提升超过2 倍。真正说明以前CPU不是处理Packet。而是浪费在Notification。十三、生产环境有哪些经验经过这次故障。团队总结了几条经验。① 不要只盯着 PMDVirtIO瓶颈很多发生在控制路径。不是DMA。不是Descriptor。而是Notification。② PPS 比 Gbps 更重要很多100G测试都是1500Byte。实际上真正决定VirtIO性能的是PPS。尤其64Byte。③ Event Index 建议开启如果业务允许批量发送。Event Index收益非常明显。不过也要注意批量通知会增加一定的数据包完成延迟因此需要根据业务对时延和吞吐的要求进行权衡。④ 优先使用 Packed Virtqueue对于支持 VirtIO 1.1 及以上的平台Packed Virtqueue 通常能带来更好的缓存局部性和更低的同步开销是值得优先考虑的选择。⑤ Host 和 Guest 要共同优化VirtIO不是Host一个人的事情。Guest、Burst、Queue、Affinity。都会影响最终性能。总结回到文章开头的问题为什么 DPDK VirtIO 只能跑到 40Gbps很多团队最初都会怀疑PMD 不够快RSS 不均衡Descriptor 太少HugePage 配置错误CPU 性能不足。但真正的问题往往隐藏在更深的地方。在 VirtIO 架构中数据真正走的是共享内存而性能瓶颈常常来自控制路径——Descriptor 发布、Kick、Notify、Event Index、VM Exit 等同步机制。尤其是在 64Byte 小包场景下PPS 极高每一次 Notification 的成本都会被无限放大。当通知过于频繁时CPU 花费的大量时间并不是转发数据而是在 Guest、KVM、vhost-user 和 Host 之间不断切换。因此一个高性能的 VirtIO 系统不只是需要优秀的 DPDK 数据平面更需要合理设计通知机制、队列模型和批处理策略。真正理解 Virtqueue 的工作原理之后再去分析 vhost-user、Packed Virtqueue、vDPA甚至硬件 VirtIO 加速就会发现它们都在围绕同一个目标进行优化减少同步减少通知减少切换让 CPU 更多时间用于真正的数据转发而不是控制路径。