接上文:为什么DPDK轮询DD Bit不会把PCIe读爆?——RX Descriptor Cache深度解析(上)-CSDN博客
八、为什么DPDK还要不断Prefetch Descriptor?
很多开发者看到PMD源码都会发现类似代码:
rte_prefetch0(&rx_ring[rx_id + 4]);第一反应通常是:Descriptor不是一直在Cache里面吗?为什么还要Prefetch?
答案其实很简单:Descriptor在Cache。但是不一定在L1 Cache。
现代Intel Xeon缓存结构通常如下:
CPU ↓ L1 Cache(最快) ↓ L2 Cache ↓ LLC(L3) ↓ DDR如果:
Descriptor刚刚被NIC DMA更新。
对应Cache Line很可能已经不在L1。
甚至已经被其它热点数据替换出去。
因此:
PMD提前Prefetch后面的Descriptor。
目的就是当CPU真正处理下一批Descriptor时Cache Line已经进入L1。
核心知识点五
Prefetch优化的不是PCIe。
而是:
CPU Cache Latency。
九、为什么DD Bit总是一批一批回来?
很多开发者抓包观察。
发现DD不是:
Packet1 DD=1 Packet2 DD=1 Packet3 DD=1而更像:
Packet1 Packet2 Packet3 Packet4 ...... ↓ 全部DD=1为什么?
原因就在于Intel网卡内部存在Descriptor Cache。
收到Packet以后NIC并不会立即DMA更新每一个Descriptor。
而是首先完成Packet DMA。
随后Descriptor进入网卡内部缓存。
满足一定条件以后,统一Write Back。
这样:PCIePosted Write数量明显减少。
总线效率提高。
因此:CPU看到DD往往是一批一批回来。
而不是一个一个回来。
需要说明:不同Intel网卡(如82599、X710、E810)Descriptor Write Back策略存在细节差异,但"批量更新"这一思想是高速网卡普遍采用的优化方式。
十、CPU轮询DD,到底有没有Cache Miss?
答案:有。
但是远远没有大家想象那么严重。
例如:
假设一个Descriptor16 Bytes一个Cache Line64 Bytes。
那么:
一个Cache Line正好包含4个Descriptor。
CPU读取第一个Descriptor发生一次Cache Fill。
随后:Descriptor2、Descriptor3、Descriptor4全部直接命中L1 Cache。
因此:
真正Cache Miss远小于轮询次数。
这也是为什么DPDK总喜欢Burst。
连续访问。连续处理。连续Prefetch。
因为它们天然符合CPUCache访问模式。
核心知识点六
真正发生Cache Miss的不是每一个Descriptor。
而是每一个:Cache Line。
十一、为什么PMD源码总是连续访问Descriptor?
继续观察典型RX Poll Loop:
逻辑可以抽象为:
for (;;) { rxdp = &rx_ring[rx_id]; if (!(rxdp->status & DD)) break; process_packet(); rx_id++; }很多初学者认为:这里只是在遍历数组。
实际上它正好符合CPUHardware Prefetch。
因为Descriptor连续排列。
CPU读取Descriptor0。
Hardware Prefetch已经开始加载Descriptor4Descriptor8Descriptor12。
所以真正进入L1 Cache速度远远快于随机访问。
这也是为什么:Descriptor Ring始终采用连续内存。
而不会采用链表。
十二、真正影响DD轮询性能的是什么?
到这里。
终于可以回答文章标题。
为什么DPDK轮询几十亿次DD,PCIe却没有爆?
答案:
因为CPU几乎没有PCIe Read。
真正影响DD轮询性能的是下面四件事情。
第一:
Descriptor:是否连续。
第二:
Cache Line:是否命中。
第三:
Prefetch:是否及时。
第四:
Descriptor:Write Back是否足够高效。
真正限制RX性能的。
已经不是PCIe。
而是:CPU Memory Hierarchy。
核心知识点七
当Descriptor进入CPU Cache以后。
整个RX Poll Loop几乎已经变成一次普通数组遍历。
这也是DPDK能够做到每秒数亿次DD轮询。
CPU仍然保持极高效率的根本原因。
十三、一次真实优化
后来团队发现:某版本修改了RX处理流程。
导致:Descriptor Prefetch 提前距离由8改成2。
例如原来:
prefetch(rxdp + 8);变成:
prefetch(rxdp + 2);压测:100GbE。
结果:PPS下降约6%。
Perf统计显示L1 Miss明显增加。
重新恢复Prefetch距离。
性能立即恢复。
整个过程中:
没有修改任何协议。
没有修改任何算法。
只是CPU等待Cache的时间变长了。
十四、很多开发者真正应该关注什么?
很多人一直关注DD。
实际上DD只是一个Status Bit。
真正应该关注的是Descriptor:
什么时候Write Back。
什么时候进入CPU Cache。
什么时候完成Prefetch。
什么时候开始处理。
这才是高速RX路径真正决定性能的地方。
十五、全文总结
很多DPDK开发者第一次阅读PMD源码时,都会误以为CPU不停轮询DD Bit意味着不断访问PCIe总线。
事实上,在主流Intel服务器平台上:
- RX Descriptor存放于Host Memory;
- CPU访问Descriptor主要依赖Cache体系;
- PCIe事务主要由NIC发起DMA完成;
- Descriptor连续布局使CPU能够充分利用Hardware Prefetch;
- 软件Prefetch进一步隐藏Cache访问延迟。
因此:真正决定DD轮询性能的。
不是:PCIe。
而是:Cache Line的组织方式、Descriptor布局以及CPU内存层次。
理解这一点以后,再阅读DPDK PMD源码,就会发现大量看似简单的代码,其实都是围绕CPU Cache优化展开的,而不是围绕PCIe优化。
全文核心知识点
- CPU轮询DD Bit时读取的是Host Memory,而不是PCIe设备空间。
- RX Descriptor Ring位于DMA一致性内存,由CPU和NIC共享访问。
- NIC通过DMA Write Back更新Descriptor,CPU通过Cache一致性观察到DD变化。
- Prefetch优化的是Cache访问延迟,而不是PCIe访问。
- Descriptor连续布局能够充分利用Hardware Prefetch。
- 真正发生Cache Miss的是Cache Line,而不是每个Descriptor。
- 高速RX Poll Loop本质上是一段高度Cache友好的连续内存遍历代码。
- 理解Descriptor Cache、Write Back和CPU Cache层次,比理解DD Bit本身更重要。