# Ring Buffer大小对网卡性能的影响实测
很多运维工程师拿到新网卡后做的第一件事,就是把 Ring Buffer 调到最大值。理由听起来无懈可击——缓冲区越大,能缓存的数据包越多,丢包就越少,性能自然更好。
这个逻辑在特定条件下确实成立。但把它当作普遍规律来用,会踩坑。
Ring Buffer(环形缓冲区)是网卡驱动和硬件之间共享的一块内存区域,分为 RX(接收)和 TX(发送)两个队列。网卡收到数据包后,通过 DMA 引擎把数据写入 RX Ring Buffer,然后触发中断通知 CPU,驱动再从缓冲区把包取走交给内核协议栈处理。
整个过程可以类比为一条传送带:网卡硬件往传送带上放包裹,驱动从另一头取包裹。传送带越长,同时能放的包裹越多,但如果取包裹的速度跟不上,包裹堆积在传送带上反而增加了每个包裹的等待时间。
这里有一个容易被忽略的关键参数:Ring Buffer 的每个槽位(descriptor)对应的内存大小是固定的,通常是一个 MTU 大小(1500 字节或开启巨帧后 9000 字节)。所以 Ring Buffer 从 256 调到 4096,占用的连续物理内存会从 384KB 增长到 6MB(按标准 MTU 算)。
这个观念的来源有两个真实依据。
第一个是丢包问题。当突发流量(burst traffic)涌入而 CPU 来不及处理时,RX Ring Buffer 满了就会丢包。把缓冲区从 256 扩到 4096,相当于把"蓄水池"从能装 256 个包扩到 4096 个包,确实能扛住更大的突发。这在流量波动大、CPU 中断处理能力不足的场景下效果明显。
第二个是某些性能测试工具的表现。用 iperf 做吞吐量测试时,更大的 Ring Buffer 有时候能让吞吐量数字好看一些,因为它降低了因缓冲区溢出导致的 TCP 窗口收缩概率。
但这两个依据都有前提条件——CPU 是瓶颈,或者突发流量远超稳态带宽。一旦离开这个前提,盲目调大反而会出问题。
这是最容易被忽视的一点。Ring Buffer 的本质是一个 FIFO 队列。数据包在队列里的等待时间,取决于队列深度和处理速度。
当流量没有打满时,一个数据包进入 RX Ring Buffer 后要等驱动轮询到它才能被处理。缓冲区越大,NAPI 每轮 poll 需要遍历的 descriptor 越多,单个包的排队延迟越高。对于延迟敏感型应用(高频交易、实时音视频、数据库同步),这几十微秒的差距可能就是业务能感知到的。
如果服务器跑的是金融交易、实时流媒体或 VoIP 业务,Ring Buffer 不宜盲目调大。建议从默认值(256 或 512)开始,逐步增加并用 ping -c 1000 监测延迟抖动变化。
还有一层影响:缓存局部性(cache locality)。Ring Buffer 越大,占用的物理内存越多,这些内存页越容易被换出或被 CPU cache 逐出。当驱动去访问一个已经被逐出 L3 cache 的 descriptor 时,内存访问延迟会从几纳秒跳到上百纳秒。
测试环境:Linux 5.15 内核,Intel Xeon 平台,分别在三款不同速率的网卡上测试——LRES1027PF-4SFP28(25G 四口 SFP28)、LREC9812BT(10G 双口 RJ45)和 LREC9714HT(1G 四口 RJ45)。
先看当前网卡支持的 Ring Buffer 范围:
# 查看网卡支持的 Ring Buffer 范围
ethtool -g eth1
# 输出示例(LRES1027PF-4SFP28):
# Ring parameters for eth1:
# Pre-set maximums:
# RX: 4096
# RX Mini: 0
# RX Jumbo: 0
# TX: 4096
# Current hardware settings:
# RX: 256
# RX Mini: 0
# RX Jumbo: 0
# TX: 256
调整 Ring Buffer 大小的命令:
# 将 RX 和 TX Ring Buffer 调到 1024
ethtool -G eth1 rx 1024 tx 1024
# 调到最大值
ethtool -G eth1 rx 4096 tx 4096
# 验证设置生效
ethtool -g eth1 | grep "Current" -A 4
以下是 25G 网卡(LRES1027PF-4SFP28)在不同 Ring Buffer 大小下的 iperf 测试结果:
| Ring Buffer | 吞吐量 (Gbps) | 平均延迟 (us) | P99 延迟 (us) | CPU 占用率 |
|---|---|---|---|---|
| 256(默认) | 23.4 | 18.2 | 42 | 12.3% |
| 512 | 23.8 | 19.5 | 48 | 11.8% |
| 1024 | 24.1 | 22.1 | 61 | 11.2% |
| 2048 | 24.3 | 28.7 | 85 | 10.5% |
| 4096(最大) | 24.4 | 38.4 | 127 | 9.8% |
数据说明了一个很直白的事实:吞吐量从 256 到 4096 只提升了约 4%,但 P99 延迟从 42us 涨到了 127us,翻了 3 倍。
10G 电口网卡(LREC9812BT)的趋势类似,但幅度更小——因为 10G RJ45 的实际线速上限约 9.4Gbps,Ring Buffer 对吞吐量的边际效益更不明显:
| Ring Buffer | 吞吐量 (Gbps) | 平均延迟 (us) | 丢包率 |
|---|---|---|---|
| 256(默认) | 9.38 | 22.1 | 0% |
| 1024 | 9.41 | 26.8 | 0% |
| 4096(最大) | 9.42 | 41.5 | 0% |
千兆网卡(LREC9714HT)在常规负载下无论怎么调 Ring Buffer,吞吐量都稳定在 940Mbps 左右。只有在极端突发场景(瞬间灌入 4Gbps 流量)时,大缓冲区才显出优势——256 时丢包率 0.3%,4096 时降到 0.01%。
服务端:iperf -s;客户端:iperf -c 10.0.0.1 -P 8 -t 60 -i 5(8 条并行流,60 秒,每 5 秒输出一次)。延迟测试配合 qperf 10.0.0.1 tcp_lat 更精确。
根据实测数据和工程经验,可以按以下原则决策:
Ring Buffer 不是越大越好。吞吐量场景可以调到 1024-4096,延迟敏感场景保持默认 256-512。调整前先用 ethtool -S 确认是否有丢包(rx_miss/overflow),没有丢包就不要盲目加大。
误区一:关了中断合并(Interrupt Coalescing)延迟就最低。 关闭中断合确实减少了中断等待时间,但 CPU 会被每个包的中断淹没,总体处理效率反而下降。正确做法是调低 rx-usecs 到 20-50us,不是归零。
误区二:开启巨帧(Jumbo Frame)一定能提升性能。 巨帧把 MTU 从 1500 扩到 9000 字节,减少了包头开销,但需要端到端所有设备(网卡、交换机、对端)都开启巨帧,否则会在中间节点被分片或直接丢弃。局域网内存储网络(如 NVMe-oF)场景值得开,普通业务网不值得冒这个风险。
误区三:网卡越多绑定越好(bonding)。 Bonding 模式 0(轮询)和模式 4(LACP)确实能提高带宽,但模式 1(主备)对吞吐量没有帮助,只是冗余。选型时先搞清楚 bonding 模式,再考虑 Ring Buffer。
网卡性能调优是一个系统工程,Ring Buffer 只是其中一个旋钮。与其纠结这个参数该填 256 还是 4096,不如先用 ethtool -S 看看有没有丢包、用 qperf 测一下真实延迟、用 mpstat 观察 CPU 是不是已经打满。数据会告诉你该往哪个方向拧。
不同网卡芯片的默认值不同,常见的有 256、512、1024。可以用 ethtool -g eth0 查看当前网卡支持的最大值和当前设定值。多数场景下默认值已经够用,不需要调整。
不需要重启系统,但修改瞬间网卡会短暂重置(毫秒级),可能丢失正在传输的少量数据包。建议在业务低峰期操作,或者先在备用网卡上测试效果。
基本策略一样,但 25G 网卡的突发流量更大、单包处理时间更短,Ring Buffer 的边际效果更明显。25G 场景下建议从 512 起步测试,1G 场景下默认 256 通常足够。具体可参考 LRES1027PF-4SFP28 和 LREC9714HT 的实测数据。
因为 Ring Buffer 是一个 FIFO 队列,容量越大,数据包在队列中排队等待处理的时间越长。尤其在低负载场景下,大缓冲区让每个包的平均等待时间增加,虽然吞吐量略有提升,但延迟敏感型业务(如实时音视频、交易)会感受到明显劣化。
无效。DPDK 使用用户态驱动直接管理网卡硬件,绕过了内核驱动层。Ring Buffer 大小在 DPDK 应用初始化时通过 EAL 参数设定(如 --rxq=4 --txq=4),ethtool 的设置在 DPDK 绑定后不生效。