receive-a-million-packets-practice.md

Table of Contents

Name

每秒接收百万数据包的实际测试

Conclusion

先说结论, 前文 How-to-receive-a-million-packets-per-second 的结果是置信的, 可以复现.
以下是我的实际测试结果:

[root@m02 how-to-receive-a-million-packets]# taskset -c 1,2,3,4 ./udpreceiver1 0.0.0.0:4321 4 1
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
  0.000M pps   0.000MiB /   0.000Mb
  0.062M pps   1.904MiB /  15.970Mb
  1.065M pps  32.513MiB / 272.737Mb
  1.064M pps  32.484MiB / 272.495Mb
  1.064M pps  32.458MiB / 272.275Mb
  1.065M pps  32.503MiB / 272.657Mb
  1.066M pps  32.541MiB / 272.972Mb
  1.075M pps  32.818MiB / 275.298Mb
  1.074M pps  32.777MiB / 274.955Mb
  1.076M pps  32.839MiB / 275.476Mb

以下将讲解配置和实现过程.

Environment

本次实验配置清单如下:

两台机器除了内存不一样以外其他都是一样的. 交换机是MikroTik的一款 16 port 10G 交换机.

Configure Receive Packet Steering

首先我们配置网卡的多队列功能, 我们使用的 Intel X520-2 网卡与前文的 Solarflare 网卡不太一样, 没有那些特有计数器, 但我们也可以通过其他一些方式来观测我们的一些所需指标.

我们的网卡的 Device Name 是 enp2s0f1, 下面是配置信息:

4: enp2s0f1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:16:31:f0:93:67 brd ff:ff:ff:ff:ff:ff
    inet 10.0.6.63/24 brd 10.0.6.255 scope global noprefixroute enp2s0f1
       valid_lft forever preferred_lft forever
    inet 10.0.6.64/24 brd 10.0.6.255 scope global secondary noprefixroute enp2s0f1
       valid_lft forever preferred_lft forever
    inet 10.0.6.65/24 brd 10.0.6.255 scope global secondary noprefixroute enp2s0f1
       valid_lft forever preferred_lft forever
    inet 10.0.6.66/24 brd 10.0.6.255 scope global secondary noprefixroute enp2s0f1
       valid_lft forever preferred_lft forever
    inet 10.0.6.67/24 brd 10.0.6.255 scope global secondary noprefixroute enp2s0f1
       valid_lft forever preferred_lft forever
    inet6 fe80::b6f3:e9e1:f46a:cd5e/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

可以看到我们配置了 10.0.6.63-10.0.6.67 5个IP.

我们通过 ethtool -l 命令来查看当前网卡的队列信息:

[root@m02 ~]# ethtool -l enp2s0f1
Channel parameters for enp2s0f1:
Pre-set maximums:
RX:     0
TX:     0
Other:      1
Combined:   63
Current hardware settings:
RX:     0
TX:     0
Other:      1
Combined:   40

其中 Pre-set maximums 中的 Combined 指的是当前网卡的最大可用队列数量.
Current hardware settings 中的 Combined 指的是当前配置的队列数量. 最大可用数量默认被配置为了机器的 CPU 核心数.

首先, 我们将队列数量配置到实验中所需的4队列.

ethtool -L enp2s0f1 combined 4

可以通过再次运行 ethtool -l 来确认是否生效.

然后我们要开启 Receive Packet Steering (RPS), 让硬件队列 Receive Side Scaling (RSS) 得到很好的效果.
配置 Receive Packet Steering 请参考文档: Improving network performance using Receive Packet Steering (RPS) from SUSE support. 里面详细介绍了如何配置掩码.

我们将 Receive Packet Steering 所需的 CPU 绑定在 CPU 21,23,25,27 上, 这里我们手动换算 CPU 掩码:

echo '00,00100000' >/sys/class/net/enp2s0f1/queues/rx-0/rps_cpus
echo '00,00300000' >/sys/class/net/enp2s0f1/queues/rx-1/rps_cpus
echo '00,01000000' >/sys/class/net/enp2s0f1/queues/rx-2/rps_cpus
echo '00,04000000' >/sys/class/net/enp2s0f1/queues/rx-3/rps_cpus

Benckmark

配置完毕, 我们开始测试性能:

接收端设置程序绑定到 CPU 11,12,13,14 上启动:

taskset -c 11,12,13,14 ./udpreceiver1 0.0.0.0:4321 4 1

发送端由于性能充裕, 我们直接按如下配置运行:

taskset -c 1,2,3,4 ./udpsender 10.0.6.64:4321 10.0.6.65:4321 10.0.6.66:4321 10.0.6.67:4321

测试结果如下:

[root@m02 ~]#watch -n 1 "ethtool -S enp2s0f1 | grep --color 'rx_queue_.*_packets'"
 Every 1.0s: ethtool -S enp2s0f1 | grep --color 'rx_queue_.*_packets'                                                                           

     rx_queue_0_packets: 201
     rx_queue_1_packets: 43106232
     rx_queue_2_packets: 16
     rx_queue_3_packets: 40834158
     rx_queue_4_packets: 0
     rx_queue_5_packets: 0
     rx_queue_6_packets: 0
     rx_queue_7_packets: 0

可以看到由于hash的问题, 只利用了2个 RX 队列.

[root@m02 how-to-receive-a-million-packets]# taskset -c 11,12,13,14 ./udpreceiver1 0.0.0.0:4321 4 1
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
  0.748M pps  22.830MiB / 191.513Mb
  0.754M pps  23.011MiB / 193.031Mb
  0.754M pps  23.010MiB / 193.025Mb
  0.753M pps  22.971MiB / 192.698Mb
  0.752M pps  22.954MiB / 192.552Mb
  0.754M pps  23.018MiB / 193.092Mb

并没有达到1M的预期结果.

Tunning

下面我们做一些优化, 将每个队列的 RSS 都绑定4个CPU:

绑定CPU 21, 23, 25, 27, 掩码二进制 0101 0101 0000 0000 0000 0000 0000, 掩码16进制 05500000

echo '00,05500000' >/sys/class/net/enp2s0f1/queues/rx-0/rps_cpus
echo '00,05500000' >/sys/class/net/enp2s0f1/queues/rx-1/rps_cpus
echo '00,05500000' >/sys/class/net/enp2s0f1/queues/rx-2/rps_cpus
echo '00,05500000' >/sys/class/net/enp2s0f1/queues/rx-3/rps_cpus
[root@m02 how-to-receive-a-million-packets]# taskset -c 11,12,13,14 ./udpreceiver1 0.0.0.0:4321 4 1
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
[*] Starting udpreceiver on 0.0.0.0:4321, recv buffer 4KiB
  0.000M pps   0.000MiB /   0.000Mb
  0.000M pps   0.000MiB /   0.000Mb
  0.836M pps  25.523MiB / 214.105Mb
  1.007M pps  30.722MiB / 257.718Mb
  1.009M pps  30.782MiB / 258.220Mb
  1.009M pps  30.798MiB / 258.356Mb
  1.009M pps  30.784MiB / 258.234Mb
  1.009M pps  30.797MiB / 258.341Mb

观察队列利用情况, 可以发现多利用了一个队列:

[root@m02 ~]#watch -n 1 "ethtool -S enp2s0f1 | grep --color 'rx_queue_.*_packets'"
 Every 1.0s: ethtool -S enp2s0f1 | grep --color 'rx_queue_.*_packets'     
     rx_queue_0_packets: 8035867
     rx_queue_1_packets: 131965261
     rx_queue_2_packets: 30
     rx_queue_3_packets: 124598494
     rx_queue_4_packets: 0
     rx_queue_5_packets: 0
     rx_queue_6_packets: 0

通过多次试验, 发现还是存在相当大的hash冲突的情况, recever线程和队列RPS都不能很好的利用全部指定的核心. 但数据还是可以支持之前的文章的结论.

Reference