每秒接收百万数据包的实际测试
先说结论, 前文 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
以下将讲解配置和实现过程.
本次实验配置清单如下:
sender:
recever:
switch:
两台机器除了内存不一样以外其他都是一样的. 交换机是MikroTik的一款 16 port 10G 交换机.
首先我们配置网卡的多队列功能, 我们使用的 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
配置完毕, 我们开始测试性能:
接收端设置程序绑定到 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 队列.
CPU利用情况
结果
[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的预期结果.
下面我们做一些优化, 将每个队列的 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都不能很好的利用全部指定的核心. 但数据还是可以支持之前的文章的结论.