做 DPDK 开发时,我一开始最关注的就是:吞吐。

例如:

  • PPS
  • Gbps
  • 丢包率

于是:程序优化到:20 Mpps

看起来非常漂亮。

但真实业务上线后,却出现一个非常诡异的问题:

现象

吞吐没问题。没有丢包。

但是:时延越来越高。

尤其在高流量时:

  • ping 抖动
  • RTT 增加
  • 小包响应变慢
  • tail latency 非常严重

最奇怪的是:系统没有任何明显错误。

最后一次真实排查,让我真正理解了:DPDK 中 burst 的本质。

一、问题现场

程序结构:

RX core

收包。

worker core

处理业务。

TX core

发包。

代码非常典型:

nb_rx = rte_eth_rx_burst(port, qid, pkts, 32);

处理完成后:

rte_eth_tx_burst(port, qid, pkts, nb_rx);

压测结果

吞吐:

18~20 Mpps

非常稳定。

但:业务 RTT:

50us -> 500us -> 2ms

逐渐恶化。

二、第一反应:是不是 CPU 不够

查看:

top -H

发现:

CPU 甚至没满。

很多核:

60%
70%

说明:

不是算力瓶颈。

三、第二反应:是不是 ring 堵塞

查看:DPDK Ring 长度:

rte_ring_count()

发现:确实偶尔升高。

但没有完全打满。

不像死锁。

四、真正突破口:观察 packet batch

后来打印:

每次 burst 的实际 packet 数量

发现:大量情况:

32 packets

即:burst 总是满。

这里开始暴露问题。

五、什么是 burst

在 DPDK 中:

核心 API:

rte_eth_rx_burst()

并不是:“收一个包”。

而是:批量收包。

例如:

32
64
128

一次处理多个包。

六、为什么 DPDK 喜欢 batch

因为 batch 能显著提升吞吐。

原因包括:

1. 减少函数调用

一次处理 32 个。比:32 次处理 1 个。

效率高得多。

2. 提高 cache locality

连续 mbuf 更容易命中 cache。

3. 减少 descriptor 更新

NIC 可以批量推进 queue。

4. 提高 SIMD 效率

部分 PMD 会向量化处理。

七、为什么 burst 会导致时延升高

这是核心问题。

假设:当前 queue 中:

只有:

1 packet

但程序:

希望凑够:

32 packet

才一起处理。

那么:第一个包必须等待后续包到来。

即:batching delay 批处理延迟。

八、这本质上是“吞吐优先”

burst 的设计哲学:不是最低时延。

而是:最大吞吐。

因为:批量处理更高效。

九、一个非常典型的误区

很多人以为:

burst = 64

一定比:

burst = 16

更好。

其实不一定。

十、吞吐和时延通常是对立的

这是高性能系统中的经典 tradeoff。

大 burst

优点:

  • 吞吐高
  • cache 友好
  • CPU 更省

缺点:

  • 等待时间增加
  • tail latency 上升

小 burst

优点:

  • 响应更快
  • latency 更低

缺点:

  • PPS 下降
  • CPU 开销增加

十一、真实现场中的问题

当时程序:

burst = 64

并且:worker 也采用 batch。

于是:形成:

RX batch
 ↓
worker batch
 ↓
TX batch

三级累积。

十二、结果

每一级都在:“等更多包”。

最终:单包排队时间显著增加。

十三、这就是 tail latency 爆炸的根本原因

平均吞吐很好。

但:最后几个包等待极长。

于是:P99 latency 飙升。

十四、进一步理解 pipeline

DPDK 程序本质上是:packet pipeline

结构:

https://images.openai.com/static-rsc-4/qp0ZmaXYS8nPgDy_b5Bglv88ZDEbdBf_RQeQNpY-FlL0IB2jlguI2_UQ47HQfpPuAT3GBrEqtz_a6Bsy2GxFmv4ZZNxXWJ-CxkDCVe1FBwPjg_KRs_pbSDleBEi7-YuIM83fUfftkkRSw8rGeRq82mYUOxpI4wpEAm36RzJxgw25nKpBvUL-fwNwwKtrk32h?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/QRSHdPKO9pdgnw8Rluq5N5FJVNsPxRbAhebeC9PJFOBEYjvQBgHxNFqWugoyz-OfpDLsRm_VFk0w2LSsdrc69JyZM24W0NTypmvV-6Mr5GcFIoQxun9mIJ9ACyEIA0lr-pDgneWR_ZbCbyHDiGt6Rq-cvILWBl_9U5XoOA-SzLMi8rJA_L4Lga5VM90hbbPo?purpose=fullsize

RX -> ring -> worker -> TX

每一级:都可能积压。

十五、另一个隐藏问题:backpressure

如果 downstream 较慢:burst 会导致:queue rapidly accumulate。

即:背压。

十六、为什么 CPU 不高,但 latency 很高

因为:系统不是算力不足。

而是:packet 正在排队等待 batch。

CPU 甚至还有空闲。但包已经在 pipeline 中堆积。

十七、如何验证

我后来增加:timestamp。

例如:

rx_ts
worker_ts
tx_ts

结果发现:大部分时间消耗并不是:业务处理。

而是:

waiting in queue

十八、如何优化

做了几个关键调整。

1. 减小 burst

从:

64

调整为:

16

2. 小包流量优先

不同流量采用不同 batch 策略。

3. worker 及时 flush

不再无限等待:batch 满。

4. ring 设置 watermark

防止积压。

十九、优化结果

优化前:

指标 数值
Throughput 20 Mpps
P99 latency 2.3 ms

优化后:

指标 数值
Throughput 17 Mpps
P99 latency 110 us

吞吐略降。但业务体验大幅提升。

二十、这次排查真正学到什么

以前我以为:

DPDK 优化就是:提高 PPS。

后来才意识到:

高性能系统真正复杂的是:latency-throughput balance

平衡。

二十一、为什么很多 benchmark 没意义

很多 benchmark 只测:

maximum PPS

但真实业务更关心:

tail latency

例如:

  • P99
  • P999

因为用户感知的是:最慢请求。不是平均值。

二十二、burst 的本质

burst 本质上是:用 latency 换 throughput。

这是所有高性能网络系统都会做的事情。

包括:

  • DPDK
  • VPP
  • XDP
  • SmartNIC
  • GPU networking

二十三、工程经验总结

实际项目中:

延迟敏感业务

推荐:

burst 8~16

吞吐优先业务

推荐:

burst 32~64

超高 PPS

可进一步增大。

但必须监控:tail latency。

二十四、总结

为什么 DPDK 程序吞吐很高,但时延越来越大?

很多时候不是:

  • CPU 不够
  • 网卡问题
  • ring bug

而是:burst 过大导致 pipeline 排队。

通过这次问题,我们真正理解了:

核心概念

  • batch processing
  • burst
  • pipeline
  • backpressure
  • tail latency
  • throughput vs latency tradeoff

这也是高性能网络开发真正困难的地方:

系统优化从来不是:“越快越好”。

而是:在吞吐、时延、资源之间找到平衡。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐