我有两台通过以太网电缆连接的计算机,并安装了 Ubuntu 22.04。我在计算机 A) 上有一个客户端,它正在向计算机 B) 上的服务器发送 UDP 数据包,并且我正在测量这些数据包在不同场景中的延迟和抖动。我使用套接字库用 C 语言编写了客户端和服务器的源代码。
当两台计算机之间在测量延迟的数据包之上存在额外的高带宽流量时,抖动和延迟会比我在没有额外流量的情况下发送数据包时要小:
- 无额外流量的往返时间:0.556 ms
- 额外流量的往返时间:0.105 ms
- 无额外流量时的抖动:0.042 ms
- 额外流量的抖动:0.014 ms
这看起来很有趣,因为我认为更少的数据包意味着更少的延迟和抖动,但结果显示并非如此。有人能告诉我原因是什么吗?我怀疑这与缓冲区优化有关,当更多数据包到达时,缓冲区必须更频繁地清空,但我不确定。如果是这种情况,我该如何配置缓冲区以最大限度地减少延迟?
Edit#1:按照建议,我尝试修改 NIC 配置的参数(ethtool -c):
我只能将 rx-usecs 3 的初始值更改为 rx-usecs 1 us。我无法修改 Adaptive-rx 和 rx-usecs-low,我认为我的网卡不支持它们。
将 rx-usecs 值减小到 1 并不能解决问题,两种场景的延迟差异即使不增加也保持相同。
将其增加到 5 和 10 us 似乎也没有帮助。
答案1
如果你想减少网络延迟和抖动,据说无论流量如何,它总是会增加 cpu 负载,并且在某些情况下,还会降低大流量下的吞吐量:
A/ 终极锤子:忙于投票!(重要警告,你获得的 CPU 数量越少,你在其他方面牺牲的就越多。)
这个想法是,不是解雇并忘记一些阻塞,recvmsg
从而释放你的CPU用于其他工作,最终刷新你的CPU缓存,并最终在几次上下文切换和软中断处理后回到你的任务......你循环进入你的任务,忙于等待来自网卡。
一旦数据在缓冲区中可用......它将被处理,没有任何额外的延迟。
请参阅man recvmsg
并阅读与此相关的部分MSG_不要等待旗帜。另请注意,打开套接字也可以达到类似的效果O_非阻塞另请注意,轮询也可以通过内核来实现,但我个人不喜欢这个想法,因为……我只有 2 个核心……;-)
话虽这么说,您肯定希望将任务固定到一个 CPU,这将防止可能的任务迁移开销并有助于保持缓存热。
这种方法的好处是立竿见影的!将延迟和抖动降至最低,且不影响吞吐量,但是……因为天下没有免费的午餐……尽可能高的 CPU 负载。
B/ 低级网卡调整(中断合并、环形缓冲区、传输队列……来自ethtool
)
- 缓冲器:一般来说,无论什么子系统(网络/声音/…)缓冲区都是延迟/抖动的敌人。因此,您需要将它们减少到最低限度。
严格最小值的值是多少?
当在重负载下,您开始丢包和/或溢出时(如 所报告ifconfig
)
- 中断合并:
中断合并增加了数据包到达时间的延迟,因为数据包位于主机内存中,但主机直到一段时间后才知道该数据包。然而,系统将使用更少的 CPU 周期,因为生成的中断更少,并且主机每个中断处理多个数据包。
因此,可以看出,以牺牲 cpu 时间和吞吐量为代价,将合并减少到尽可能低的程度是很有趣的。
- 当然,在以下情况下不需要这样做忙轮询。
- 当然,如果您不首先确保其关联的 IRQ 均匀分布在所有可用内核上,那么对于多队列网卡,这几乎没有影响,
- 当然,如果你的系统没有运行,它不会有任何效果中断线程因为 IRQ 处理的实际工作不会通过遵循实时调度策略的专用内核线程来实现。
答案2
您的网络驱动程序和堆栈可能会自动切换,从让卡在收到以太网帧或完成传输帧时引发中断,切换到本质上内核以最快速度询问卡的模式因为它会询问是否有什么事情需要处理。
想法是,虽然直观上来说,每次新数据包到达时都获得中断听起来像是可以降低延迟,但处理中断需要一些准备,因此需要时间。如果正在进行高速通信,则几乎可以肯定,在处理完数据包并完成内核的所有内务处理后,会有一个新数据包等待处理。因此,中断实际上只会打断您的工作流程,并减少您实际可用的时间处理数据包(而不是仅仅对存在数据包的信息做出反应)。
您可以使用控制其行为ethtool -C
;检查 ethtool 的手册页以了解它理解的选项。您应该使用sudo ethtool -c ${YOUR_ETHERNET_INTERFACE_NAME}
;检查您当前的选项我期待类似的东西
Coalesce parameters for ${YOUR_ETHERNET_INTERFACE_NAME}:
Adaptive RX: on TX: off
stats-block-usecs: 50
sample-interval: 20
pkt-rate-low: 0
pkt-rate-high: 0
rx-usecs: 0
rx-frames: 1
rx-usecs-irq: n/a
rx-frames-irq: n/a
tx-usecs: 0
tx-frames: 1
tx-usecs-irq: n/a
tx-frames-irq: n/a
rx-usecs-low: n/a
rx-frame-low: n/a
tx-usecs-low: n/a
tx-frame-low: n/a
rx-usecs-high: n/a
rx-frame-high: n/a
tx-usecs-high: n/a
tx-frame-high: n/a
CQE mode RX: n/a TX: n/a
您可以尝试将 设为rx-usecs-low
某个合理的值(以微秒为单位),或设置rx-usecs
并禁用adaptive-rx
。 (对于任何不能 24/7 进行高速网络的情况,拥有自适应 RX 可能是一个好主意,几乎没有任何缺点。)