欢迎光临
我们一直在努力

CPU始终100%,吞吐却只有别人十分之一?——一次高性能网关性能雪崩的完整分析

一、问题出现了

某运营商边缘节点部署了一套用户面网关。

系统采用:

DPDK + 多核Worker模型

主要功能:

GTP-U解封装
↓
Session查找
↓
PDR/FAR匹配
↓
转发

整体架构如下:

N3
|
|
+————-+
| Gateway |
+————-+
|
|
N6

实验室压测结果:

项目数值
Worker 16
CPU频率 3.0GHz
Session 10万
PPS 350万
时延 <80us

结果非常理想。

上线一个月后却收到告警:

流量增长后出现丢包
业务时延明显升高

但查看系统状态时却发现一个奇怪现象:

top

结果:

16个Worker

全部100%

开发人员第一反应:CPU已经满了,需要增加服务器。

但事实证明:

这完全是误判。


二、DPDK为什么CPU永远100%

先解释一个很多传统Linux开发者容易误解的问题。

Linux Socket程序:

while(1)
{
recvfrom(fd,…);
}

没包时:

线程睡眠

CPU利用率下降。

而DPDK:

while(1)
{
nb_rx = rte_eth_rx_burst(…);

process();

rte_eth_tx_burst(…);
}

即使没有任何流量:

Worker仍在轮询RX Ring

因此:

CPU永远100%

这意味着:

CPU利用率
不能作为DPDK程序性能指标

真正重要的是:

PPS
Cycle Per Packet
Cache Miss


三、问题的第一现场

进一步统计:

指标数值
Worker数 16
CPU 100%
PPS 80万
Session 220万

奇怪的地方出现了:

实验室:

10万Session
350万PPS

现网:

220万Session
80万PPS

CPU同样100%。

为什么性能差了4倍?


四、perf告诉了真相

执行:

perf stat -e cycles,instructions,cache-misses

结果:

cycles 3.2e12

instructions 2.1e12

cache-misses 5.8e10

继续:

perf record
perf report

热点函数:

rte_hash_lookup

占比:

42%

这意味着:

CPU大部分时间
都在查Session


五、Session表设计出了问题

Session结构:

struct upf_session
{
uint64_t seid;

uint32_t teid;

pdr_t pdr;

far_t far;

qer_t qer;

statistics_t stat;

timer_t timer;
};

大小:

约256Byte

现网:

220万Session

总内存:2200000 × 256B ≈ 563MB

即:

约560MB

看起来不大。

但问题来了。


六、CPU真正害怕的东西:Cache Miss

现代Xeon:

层级大小
L1 32KB
L2 1MB
L3 30MB~60MB

而Session表:

560MB

远超L3容量。

https://images.openai.com/static-rsc-4/nGwtxhPD4aJN-MoltNNS9z3Pt1cS-FbzrMEmLyZwpi_jQbaoSvFMaRh6OnZprOQQeZMrNuxA5FGZzitYxU01gHcBQ1aKOr3YPPFxVgO5k03rbNyWqLW6dXVN9zcg38G_QH5thgUtUUdo-Lk-MkyZ48DJGhKdLSY07jIBiQLiyMtPVsikTMbHWw2UTkFDdenL?purpose=fullsize

https://images.openai.com/static-rsc-4/sd8Ep07gIvF_ACLe7IwK__rMx7krP1Ei2zMO3FnwC77sR2RFIXZ3vSbCiaFwzFWKE9GpWw6XtnqA4iBFLEpztaCyyRc6FgOoRVnA3fJ7CrzrIEGsV-xETfmQSL0EJJCgCS2ivpnclM4jMXEhrBq5tSPcZkbCws0JcsY9f_GePeZE0x2PpRdTYDla1ouPMk7L?purpose=fullsize

https://images.openai.com/static-rsc-4/3ON_FzGNkyxaVsw6DPa_WD6IDfroDYFxfrA6SwRAAlEB7dvwd71rc3TqVb1K8JwEXpgqxUbBCePRbHxqqR1T3sBX_dPBDs9NNfgbOgiyl08FObnic2bFyEdiEJHtgrKSNsnwc3MlBVh6KA9edRGhcD734eBJ6jYZ0v2MmxprWe4R-BiNp3LWXGTcnOOp6epQ?purpose=fullsize

于是:

每个包到来:

TEID
↓
Hash
↓
Session Lookup
↓
DRAM访问

CPU开始等待内存。


七、CPU其实一直在发呆

很多人认为:

CPU 100%
说明CPU很忙

实际上:

CPU可能正在:

等待内存返回数据

例如:

L1访问:

1ns

L2:

4ns

L3:

10ns

DRAM:

80~120ns

差距超过:

100倍

于是:

CPU看似100%

实际上大量Cycle被浪费


八、NUMA又补了一刀

服务器配置:

2 Socket

结构:

Socket0
├─CPU 0~15
└─Memory0

Socket1
├─CPU16~31
└─Memory1

https://images.openai.com/static-rsc-4/FKBST4cL7MHnUxtgem0UTIDl30jvE0-XQZQImQSo3m7-S-o2ql4iO_vU3aNUsjV5R1jqD6pqHcq6SG_4MvQUTwbH4eKpOtgLyyVmVxnMT95zdsMgLYHOXpxOaHdlVMe9O3BrlAgccmdpe4tkxzzwiSMfKh7k2h-vlodLT_6nKFk7V3bObzjIibIu8DbN2Efj?purpose=fullsize

https://images.openai.com/static-rsc-4/jQBhB1WKkCileqy7lCsz3yktqt06K0_9eBcSb3rP2sX7pBrHzPo9HmC1xIGiTGPEpAaqbQZ12YsVScItrqeBJAIpypP24EafOHvZuBwxycJcJ7Z2ywPUTZKuehVc_hrstKzD58YOTP8VL7zZ0xQx145AXy5ENVKdfga3vcJWuI2eJ7BSFvYrtm6P7gYStNJw?purpose=fullsize

https://images.openai.com/static-rsc-4/_qjKbIyX2jKy30gHVmaNtXr-X5hwuGyWomWvUqGl0iJ5aGM6xaUwZrcr8MnND85EPm_1oQFiO9tpWpJ6hwf50FQJw-vPKIseMaBnasPS3PCW6KfkPjjk-inFYjRMwFWHBtRWRizxarwZYVJ6wtn45ksbCMXviGWOZzdBY943sckHrOQRNTpYYpXo2UgnEcdN?purpose=fullsize

检查发现:

Worker运行在Socket0

Session分配在Socket1

于是:

跨NUMA访问

延迟从:

90ns

变成:

150ns+

PPS进一步下降。


九、更隐蔽的问题:共享状态

原始架构:

RX
|
—————-
| | |
W0 W1 W2

|
|
Global Session Table

统计信息:

session->pkt_cnt++;

多个Worker同时更新。

于是:

Cache Line竞争

开始出现。

https://images.openai.com/static-rsc-4/Ucc-oXuLyDtNxM-18gGBEko0LYM3PPmPfbb6x47IKz3Zji0wqcIeop6l_0wpxmlOSSYmNauJOOZX0WcJ9uzjLLPEhFUL_U4vxvcpesin66XogkPpIo2oULQ3smgKDx5dP3CAmqTyibGA3S6mJEXPzwuOIfEHv0mngEDT7y8tRFQ7Ancm44S0-Pu1qEPg-W6-?purpose=fullsize

https://images.openai.com/static-rsc-4/aNnvKRG7HntpnUtD41YK6d7psvCGPbAk-xxDTyKcn0ycyM1bcv7ujRFJtwbU7fTjVmjiCCQFx-uvJ-BGUJ_WmIvTyljYicmCD7CqIQOZ8PRgtMb8MK2dyIxVeL6pubegnluY71gaL3M_IzBpzhYPFdzjumBIyRVjMxBFyFzG6rkCTy-dL1UScLbz44O0iOu2?purpose=fullsize

https://images.openai.com/static-rsc-4/c1OQ2VdXVs4pbQMTBbRwRaWaNpmibRgpQ1GcAJ1sUKjukvhF9yYuu2bm8k4nsB5x7AcCNMItdbRJoXE7ZX79l3vzq083EsS11UvDWMG7zPQvZ9WZN8h83Go2mxQT-aSJiR8pDyGQdup7aQrrN31VXiF0E55l_lEbmpAnzCzOjYFgnLpgvgEZw_GYtYcGBJIy?purpose=fullsize

即使没有显式加锁:

MESI协议

也会导致:

Cache Line Ping-Pong

性能进一步恶化。


十、为什么Flow Affinity如此重要

高性能网关最重要原则:

同一Flow永远属于同一个Worker

例如:

worker_id = teid % worker_num;

架构变成:

Dispatcher
|
——————————–
| | | | |
V V V V V

W0 W1 W2 W3 W4

https://images.openai.com/static-rsc-4/4HYXbYtGpg-dBLPSnYqDxmq7j9M1HwMSHlS8k6SSA9Uor84kcR1nKBYXFZoOn0Kn4tl45znl4cL9vSo98dqxB_IzMvdoyO4aGzTgNSaHZQ-75wUFKN0oLwoGGs7tolCsBo_JBdELNpkF7V-JA1yHHWzb9M20YB309dqxubXXNACC65mnvcLgVfP_BUxIskru?purpose=fullsize

https://images.openai.com/static-rsc-4/hhYbGj3DKRxidz0dSxvQQnrR7jlX-2HvpF276Vwuvk1xh6egJ6XNvjR0yAK8ojPw4yg_-m7fM_YoSRvK0Rcq1yBxu2KhHabbZzxSUHp_SpMWwSmqqz6Fil-8D_cYvrtKIoVxyc8qhhHpcPmkYjApPS62OLpVef2sryVgdAeaN0u553cyf7MOEp4g-MPqm2Xs?purpose=fullsize

https://images.openai.com/static-rsc-4/81_GDpIuxKW2K_ppF75xyOmX3yPjIwaRbLF4-nqBHPtPAOfSi3a0pbS-0Er8z-k3bpROv3pVAuHJvFrVzuKV6PTZ4gYuyBaCYXNiqoWhe3npIvV54tlI0IGBFCHY7rLeYpVACViuOWJOAEGsncYiWpeNEqrGewoQFRc_pVodgegxbyYjD7kMguwf213_Ea0L?purpose=fullsize

这样:

同一个TEID
固定进入同一个Worker


十一、状态本地化

进一步优化:

不要:

Global Session Table

改为:

Worker0 Session Pool

Worker1 Session Pool

Worker2 Session Pool

Worker3 Session Pool

即:

State Ownership

原则:

谁处理Flow

谁拥有State

这样:

无锁
无竞争
无跨核同步


十二、Dispatcher + Worker架构

最终架构:

NIC
|
RX Queue
|
Dispatcher
|
———————————–
| | | | |
V V V V V

Worker0 Worker1 Worker2 Worker3 Worker4
| | | | |
| | | | |
Session Session Session Session Session

Dispatcher职责:

解析GTP-U头
提取TEID
Hash分发

Worker职责:

Session Lookup
PDR匹配
FAR执行
QER处理
转发

Session仅属于本Worker。


十三、批处理带来的收益

不要:

process_one_packet();

要:

rte_eth_rx_burst();

例如:

32 Packet

一批处理。

https://images.openai.com/static-rsc-4/aKYRIwdTATXkFMSpqmPc3OuKLVvte_nN0_SsS0OajIPlTKZOg4RjbS4BhVBy3PWe9DTwNDhnZSxRyCFGs5RwAGwTchllBI3BqwLKJoLNv93KRJDwNUrp74GGjl2hhA3dAjHsZ_2COvum9RYYv6xoCk-uoLFTzor7lmGOdtnu_xLJCprRyZPdXHjttlLfbIVc?purpose=fullsize

https://images.openai.com/static-rsc-4/i_xmEiEDGJOysbWHkUUWx-4lBUDBoAXa5tMXxrxKq9GbAkU6SZMbDd86RdF8Pxmkkzb4JZ3K_YijLOXn0XNkjVGWRFxZ7iZaaWbNexcGe7Y3-MQ5iuVYgKBBzKWyOAC1ardPzHMQarJVvA98p5AbcETF5wozmBiCd6h7vKY-TGfi0D-heDRjLcbboZW72Oho?purpose=fullsize

收益:

  • 更高Cache命中率
  • 更少函数调用
  • 更好的流水线利用率
  • 更少Ring访问次数

十四、优化后的结果

优化前:

指标数值
Worker 16
CPU 100%
PPS 80万
Session 220万
Cycle/Packet 6000+

优化后:

指标数值
Worker 16
CPU 100%
PPS 420万
Session 220万
Cycle/Packet 1100

可以看到:

CPU始终100%

但吞吐提升超过5倍

这才是DPDK系统真实的优化效果。


十五、高性能网关设计原则

经过这次故障分析,可以总结出高性能网关设计的五条核心原则:

1. Flow Affinity

同一Flow固定进入同一Worker。

2. State Ownership

状态归属线程。

3. NUMA Awareness

网卡、CPU、内存保持同NUMA。

4. Batch Processing

尽可能批量处理数据包。

5. Cache First

设计首先考虑Cache命中率,而不是代码优雅性。


结语

许多开发者认为高性能网关的瓶颈来自CPU主频、核数或者算法复杂度。但在现代DPDK系统中,更常见的情况是:CPU已经100%运行,却有大量周期浪费在等待内存、跨NUMA访问和缓存一致性维护上。

从本质上说,高性能网关优化的核心并不是让CPU更忙,而是让CPU把每一个Cycle都花在真正的数据处理上。当Flow、State、Cache和NUMA形成统一设计时,系统才能从百万PPS平滑扩展到数百万甚至千万PPS,这也是UPF、CGNAT和下一代边缘网关架构设计的关键所在。

赞(0)
未经允许不得转载:171主机测评 » CPU始终100%,吞吐却只有别人十分之一?——一次高性能网关性能雪崩的完整分析
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址