100G交换机吞吐下降20%——一次DPDK Hash Cache Locality优化实战(上)

📅 2026/7/2 20:22:50
100G交换机吞吐下降20%——一次DPDK Hash Cache Locality优化实战(上)
一、一次看起来毫无异常的性能下降某数据中心核心交换机采用DPDK构建高速转发平面。每个数据包进入Worker以后需要完成VLAN解析L2/L3查找Session查找ACL匹配Forward Action其中Session采用DPDKrte_hash管理。系统上线半年一直稳定运行。某次版本升级以后测试部门反馈百万连接场景下整体吞吐下降约20%。P99时延由5.8μs上升至7.9μs。而64字节包下降最明显。查看所有监控。全部正常。指标状态PMD CPU100%RSS正常RX Queue正常TX Queue正常NIC Error0Hash Miss0尤其Hash Miss 始终为0。说明所有Session全部命中。理论上Hash应该不存在任何问题。核心知识点一很多开发者容易陷入一个误区Hash命中率高就意味着Hash性能高。实际上。命中只是说明找到了对象。真正决定性能的是找到对象以后CPU花了多少Cycle才能真正访问到它。二、第一轮排查是不是Hash算法变慢了首先怀疑Hash函数。查看DPDK源码。发现系统仍然采用rte_hash_lookup_data()Hash算法没有修改。继续统计平均Lookup次数。结果完全一致。也就是说每个Packet执行同样次数Hash查找。理论上CPU消耗应该一致。然而实际Cycles明显增加。问题开始变得奇怪。三、Perf开始暴露异常开始使用perf stat \ -e cycles,\ instructions,\ cache-misses,\ branches \ -p PMD_PID得到正常版本指标数值IPC1.94Cache Miss很低Cycles正常升级以后指标数值IPC1.58Cache Miss略增加Cycles↑↑Instructions几乎一致。说明代码没有执行更多。CPU只是执行得更慢。核心知识点二CPU性能下降并不一定意味着执行了更多代码。更多时候意味着CPU在等待。现代CPU真正昂贵的。不是算术运算。而是等待内存。四、热点终于出现继续打开Perf Report。结果热点如下35% rte_hash_lookup_with_hash() ↓ memcmp() ↓ session pointer看到这里。团队第一反应是不是Hash算法退化了继续分析。却发现Hash冲突没有增加。Bucket长度完全正常。Hash本身几乎没有变化。那么CPU到底在等待什么五、重新认识一次Hash查找很多开发者理解的Hash都是Key ↓ Hash ↓ Bucket ↓ Value实际上。DPDK真正的数据路径远没有这么简单。一次Session查找CPU真正经历的是Packet ↓ 五元组 ↓ Hash计算 ↓ Bucket ↓ Signature比较 ↓ Key比较 ↓ Session Pointer ↓ Session ↓ Forward Rule ↓ Action注意这里已经出现多个不同对象。这些对象并不一定连续存放。核心知识点三Hash查找真正昂贵的。通常不是Hash函数。而是Hash之后的数据访问路径。如果每一步都发生Cache Miss。那么Hash即使100%命中。整体依然可能很慢。六、真正的问题开始浮现继续打开PMU事件统计perf stat \ -e LLC-load-misses,\ L1-dcache-load-misses,\ stalled-cycles-backend结果发现升级以后Backend Stall明显增加。而Hash计算耗时几乎没有变化。CPU大量时间停留Backend。说明不是ALU。不是Branch。而是等待数据。七、为什么CPU一直在等待继续查看Session结构。发现新版本为了方便扩展。增加了多个指针成员。例如struct session { struct qos_profile *qos; struct policer *policer; struct stats *stats; struct action *action; };逻辑完全正确。代码更加模块化。但是每增加一个指针。CPU就可能多进行一次随机内存访问。这些访问如果没有命中Cache。CPU只能等待数据从更低层缓存甚至主存返回。此时真正的问题开始指向Cache Locality缓存局部性。