一、故障背景
某大型云数据中心部署了一批DPDK软件交换机。
主要功能:
- EVPN VXLAN Gateway
- IPv4/IPv6 Routing
- ACL
- QoS
- ERSPAN
硬件:
| 项目 | 配置 |
|---|---|
| CPU | Xeon 8480+ |
| NIC | Intel E810 |
| DPDK | 23.11 |
| PMD | 48核 |
| Queue | 48 RX / 48 TX |
上线初期:
97Mpps
长期稳定。
半年后:
随着业务增长。
开始出现:
97Mpps
↓
91Mpps
↓
84Mpps
↓
69Mpps
同时:
P99 RTT
0.7ms
↓
4.1ms
但:
CPU始终:
100%
二、第一轮排查
检查:
rte_eth_stats_get()
结果:
imissed=0
rx_nombuf=0
ierrors=0
正常。
RSS:
48队列均衡
正常。
ACL:
Lookup稳定
正常。
FIB:
DIR24_8稳定
正常。
NUMA:
Local Memory
98%以上
正常。
问题无法解释。
三、发现一个奇怪现象
通过:
dpdk-proc-info
统计Mempool。
发现:
mempool剩余对象很多
并不存在:
对象耗尽
问题。
但:
mempool_get_bulk()
耗时却越来越高。
这是第一条关键线索。
四、DPDK Mempool架构
很多人认为:
Mempool
=
共享对象池
其实不准确。
真正架构如下:





结构:
Global Pool
↑↓
Per-Lcore Cache
↑↓
PMD Thread
核心思想:
尽量避免访问Global Pool。
五、Per-Core Cache工作机制
假设:
cache_size = 256
Core0申请mbuf:
rte_pktmbuf_alloc()
优先:
Core0 Cache
获取。
只有Cache空了:
才访问:
Global Pool
释放时:
也是一样。
六、为什么这么设计
因为:
Global Pool内部:
共享
意味着:
CAS
Lockless同步
Cache一致性
成本。
而:
Per-Core Cache:
本地访问
几乎零成本。
七、问题开始出现
交换机新增:
ERSPAN
Mirror
Sampling
功能。
出现:
Core0分配
Core17释放
情况。
即:
Allocate Core ≠ Free Core
八、生命周期被破坏
正常情况:
Core0
Alloc
↓
Process
↓
Free
异常:
Core0
Alloc
↓
Core17
Free
如下图:





结果:
Core17 Cache:
越来越大。
Core0 Cache:
越来越空。
九、Mempool Cache失衡
统计:
| Core | Cache对象 |
|---|---|
| Core0 | 8 |
| Core1 | 12 |
| Core2 | 5 |
| Core17 | 913 |
| Core18 | 1004 |
出现明显倾斜。
十、第一层性能损失
Core0:
频繁:
rte_mempool_get_bulk()
访问:
Global Pool
次数增加。
导致:
CAS竞争增加
十一、第二层性能损失
Global Pool:
采用:
ring
实现。
大量核心同时访问:
MP/MC
模式。
结果:
atomic竞争
显著增加。
十二、第三层性能损失
更严重的是:
缓存一致性流量。
Global Pool Head:
成为:
热点Cache Line
如下图:




缓存线:
Core0
↓
Core7
↓
Core12
↓
Core22
不断迁移。
十三、Perf分析
使用:
perf stat
发现:
Instructions
基本不变
但:
lock instructions
增长:
7倍
同时:
LLC Miss
增长:
4倍
十四、为什么CPU仍然100%
因为:
PMD线程:
while (1)
{
rx_burst();
process();
tx_burst();
}
永不休眠。
CPU:
始终:
100%
但:
越来越多时间:
花费在:
mempool同步
而非:
转发逻辑
十五、关键证据
增加统计:
rte_mempool_ops_get_count()
发现:
Global Pool访问次数:
| 时间 | 次数/秒 |
|---|---|
| 初始 | 12万 |
| 异常 | 830万 |
增长:
69倍
与PPS下降趋势完全一致。
十六、根因闭环
完整链路:
Mirror/Sampling
↓
跨核释放
↓
Cache失衡
↓
Global Pool访问增加
↓
Atomic竞争增加
↓
Cache Line Bounce
↓
Cycles/Packet增加
↓
PPS下降
十七、解决方案
方案一
生命周期归属原则。
谁申请:
谁释放
避免:
Cross-Core Free
方案二
引入Recycle Ring。
Core17
↓
Recycle Ring
↓
Core0回收
恢复对象归属。
方案三
调大Cache Size。
原来:
cache_size=256
改:
cache_size=1024
降低Global Pool访问频率。
方案四
独立Mempool。
不同Pipeline:
RX
Worker
Mirror
使用不同Pool。
降低争用。
十八、优化结果
优化前:
| 指标 | 数值 |
|---|---|
| PPS | 69M |
| RTT P99 | 4.1ms |
| Global Access | 830万/s |
| Lock Instr | 7× |
优化后:
| 指标 | 数值 |
|---|---|
| PPS | 96M |
| RTT P99 | 0.8ms |
| Global Access | 18万/s |
| Lock Instr | 1× |
核心知识点总结
1
Mempool不是简单内存池。
本质是:
Global Pool
+
Per-Core Cache
架构。
2
DPDK性能依赖:
对象局部性
而不仅仅是:
CPU局部性
3
跨核释放会破坏:
Cache Ownership
4
Mbuf生命周期设计错误。
即使:
RSS正常
NUMA正常
也能造成巨大损失。
5
CPU 100%并不代表真正转发。
可能在:
Atomic竞争
上浪费大量周期。
6
高性能交换机设计中:
“谁申请,谁释放”不仅是编码规范,更是性能架构原则。
7
很多百万级PPS性能问题,本质上并不发生在收包、查表、发包阶段,而发生在最容易被忽略的:
Mbuf回收路径
这类问题通常只会在几十核、上亿PPS的真实交换机场景中暴露,也是很多DPDK项目从实验室走向生产环境后才出现的典型深层瓶颈。

377

被折叠的 条评论
为什么被折叠?



