TCP缓冲区大小和以太网绑定

3
我正在尝试在Linux上调整TCP缓冲区大小,但是各种结果让我感到困惑。
测试程序包括服务器和客户端。服务器只是监听一个端口,等待客户端从mmaped文件中发送数据。接收到的数据使用recv复制到应用程序缓冲区,然后被丢弃。在发送数据时,客户端使用以mmapped缓冲区的完整大小作为初始参数的send。
这些程序在两个不同数据中心的两个节点上运行,它们之间的ping响应时间约为9毫秒。两个节点都安装了两个千兆以太网控制器。最大吞吐量为256 MB/s,并且适当的send/recv缓冲区大小设置应该约为256 MB/s * 0.09 s ~ 2415919字节。
我进行了几次实验。
第一次运行中,我运行了一个服务器实例和一个客户端实例。我没有设置发送缓冲区或接收缓冲区的大小,让内核自动调整它们。此案例的目的是建立其他实验的基线。
在此设置中,实际吞吐量约为117 MB/s。在这种情况下,单个服务器和客户端只使用了一个以太网控制器。通过ifconfig检查,我发现大多数数据包在eth0和eth1之间的单个接口上传输。
然后我尝试了两个服务器和两个客户端,这次吞吐量提高到约225 MB/s,更接近理想的最大吞吐量。
这是让我感到困惑的第一个问题:
  1. Why do I need more than one process to use up the bandwidth? FWIW, below is part of /proc/net/bonding/bond0:

    Bonding Mode: IEEE 802.3ad Dynamic link aggregation
    Transmit Hash Policy: layer3+4 (1)
    MII Status: up
    MII Polling Interval (ms): 100
    Up Delay (ms): 0
    Down Delay (ms): 0
    

然后我尝试了几种服务器和客户端的发送/接收缓冲区大小组合。以下表格总结了结果:

| send buf size | recv buf size | throughput | comment                   |
|      (client) |      (server) |     (MB/s) |                           |
|       1048576 |             - |       51.5 |                           |
|       2621400 |             - |       48.6 | server uses autotuning    |
|        524288 |             - |       43.3 |                           |
|       8388608 |             - |       36.3 |                           |
|       2621400 |       2621400 |       33.0 | somewhat the theory value |
|             - |       2621400 |       30.4 | client uses autotuning    |
|       4194304 |             - |       30.3 |                           |
|        262144 |             - |       29.1 |                           |
|             - |       1048576 |       27.9 |                           |
|       6291456 |       6291456 |       26.5 |                           |
|       8388608 |       8388608 |       23.9 |                           |
|       6291456 |             - |       22.2 |                           |
|             - |       4194304 |       20.8 |                           |
|       1048576 |       1048576 |       19.8 |                           |
|       4194304 |       4194304 |       19.3 |                           |
|             - |       8388608 |       19.3 |                           |
|             - |       6291456 |       13.8 |                           |

以下是从上表中提出的其他几个问题:

  1. 为什么理论值没有导致最佳吞吐量(117 MB/s)?
  2. 为什么最佳结果(51.5 MB/s)仍然不如内核自动调优的结果(117 MB/s)?
  3. 为什么更大的缓冲区会导致较差的吞吐量?

提前感谢。


您可以在ServerFault.com上提出这个问题,以获得更快的帮助。 - Andriy Tylychko
我已经在服务器故障上重新发布了这个问题。 - user2595776
1个回答

2

我对一些问题进行了分析。

需要注意的是,尽管链接速度为1 Gbits/秒(128MBps),但由于在操作系统上运行,我们从未直接获得相同的吞吐量。应用程序/内核延迟导致链接空闲,因此我们获得的吞吐量更少。

  1. Why do I need more than one process to use up the bandwidth?

    /proc/net/bonding/bond0  
    Bonding Mode: IEEE 802.3ad Dynamic link aggregation  
    Transmit Hash Policy: layer3+4 (1)
    
如前面所述,选择从属取决于L3头(IP src & dst)和L4头(src和dst端口)。在您运行多个客户端应用程序时,在您的情况下,您实际上使用不同的src端口,因此可能会选择不同的从属,而不像单个应用程序的情况。查看wiki以获取传输哈希策略。
为什么理论值不能得到最佳吞吐量(117 MB/s)?
正如早些时候提到的,在操作系统之上运行时很难获得链接速度。尝试使用UDP而不是TCP,你可以看到你获得更接近链接速度。TCP的吞吐量较少,因为TCP是可靠的,因此缓存数据,并且有时依赖于计时器触发(低频计时器)来传输数据包。尝试使用TCP_NODELAY选项要求tcp堆栈在应用程序调用sendmsg()后立即发送数据。您还可以尝试iperf应用程序来测量TCP / UDP吞吐量,它具有在同一个套接字上运行多个线程的选项。
为什么最好的结果(51.5 MB/s)仍不如内核自动调整(117 MB/s)的结果?
不确定,但可能是因为,我发现内核通过基于服务器传播的TCP窗口大小经常调用tcp_sndbuf_expand()来调整sk_sndbuf。因此,它根据指标(例如拥塞,服务器处理时间等)不断更改sndbuf大小。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接