Skip to content

单CPU处理网络IO存在瓶颈, 目前经常使用网卡多队列提高性能.

通常情况下, 每张网卡有一个队列(queue), 所有收到的包从这个队列入, 内核从这个队列里取数据处理. 该队列其实是ring buffer(环形队列), 内核如果取数据不及时, 则会存在丢包的情况.
一个CPU处理一个队列的数据, 这个叫中断. 默认是cpu0(第一个CPU)处理. 一旦流量特别大, 这个CPU负载很高, 性能存在瓶颈. 所以网卡开发了多队列功能, 即一个网卡有多个队列, 收到的包根据TCP四元组信息hash后放入其中一个队列, 后面该链接的所有包都放入该队列. 每个队列对应不同的中断, 使用irqbalance将不同的中断绑定到不同的核. 充分利用了多核并行处理特性. 提高了效率.

多网卡队列实现图例

             普通单队列                                   
   +-----------------------------+                        
   | queue                       |                        
   |                             |                        
   |   +----------+  +----------+|           +---------+  
   |   |  packet  |  |  packet  ||---------->|  CPU 0  |  
   |   +----------+  +----------+|           +---------+  
   +-----------------------------+                        
                                                    
                             开启多网卡队列               
                                                        
    +----------------------------+                       
    | queue                      |                       
    |                            |                       
    |  +----------+ +----------+ |           +---------+ 
    |  |  packet  | |  packet  | |---------> |  CPU 0  | 
    |  +----------+ +----------+ |           +---------+ 
    +----------------------------+           +---------+ 
                                             |  CPU 1  |  
                                             +---------+  
                                             +---------+  
    +----------------------------+           |  CPU 2  |  
    | queue                      |           +---------+  
    |                            |                        
    |  +----------+ +----------+ |           +---------+  
    |  |  packet  | |  packet  | |---------> |  CPU 3  |  
    |  +----------+ +----------+ |           +---------+  
    +----------------------------+

检查中断与对应的CPU关系

如下显示, 第一列是中断号, 后面两列是对应CPU处理该中断的次数, virtio-input和 virtio-output为网卡队列的中断 可见大部分包被CPU1处理

bash
# cat /proc/interrupts | egrep 'CPU|virtio.*(input|output)'
           CPU0       CPU1
 27:          7      89632   PCI-MSI-edge      virtio3-input.0
 30:          2          0   PCI-MSI-edge      virtio3-output.0
 31:          7      23319   PCI-MSI-edge      virtio3-input.1
 32:          2          0   PCI-MSI-edge      virtio3-output.1

查询具体中断所绑定的CPU信息
smp_affinity_list显示CPU序号. 比如 0 代表 CPU0, 2代表 CPU2 smp_affinity 是十六进制显示. 比如 2 为10, 代表 CPU1 (第二个CPU)

bash
# for i in {30..32}; do echo -n "Interrupt $i is allowed on CPUs "; cat /proc/irq/$i/smp_affinity_list; done
Interrupt 30 is allowed on CPUs 0
Interrupt 31 is allowed on CPUs 1
Interrupt 32 is allowed on CPUs 0

RPS, XPS, RFS

之前谈的多网卡队列需要硬件实现, RPS则是软件实现,将包让指定的CPU去处理中断.
配置文件为/sys/class/net/eth*/queues/rx*/rps_cpus. 默认为0, 表示不启动RPS 如果要让该队列被CPU0,1处理, 则设置 echo "3" > /sys/class/net/eth*/queues/rx*/rps_cpus, 3代表十六进制表示11, 即指CPU0和CPU1
在开启多网卡队列RSS时, 已经起到了均衡的作用. RPS则可以在队列数小于CPU数时, 进一步提升性能. 因为进一步利用所有CPU. RFS则进一步扩展RPS的能力, 它会分析并将包发往最合适的CPU(程序运行所在的CPU). 检查当前RPS, RFS开启情况:

bash
# for i in $(ls -1 /sys/class/net/eth*/queues/rx*/rps_*); do echo -n "${i}:  "  ; cat ${i}; done
/sys/class/net/eth0/queues/rx-0/rps_cpus:  3
/sys/class/net/eth0/queues/rx-0/rps_flow_cnt:  4096
/sys/class/net/eth0/queues/rx-1/rps_cpus:  3
/sys/class/net/eth0/queues/rx-1/rps_flow_cnt:  4096
# cat /proc/sys/net/core/rps_sock_flow_entries
8192

XPS是将发送包指定到CPU, 通常和同一队列的rps和xps配置一致.

bash
# for i in $(ls -1 /sys/class/net/eth*/queues/tx*/xps_cpus); do echo -n "${i}:  "  ; cat ${i}; done
/sys/class/net/eth0/queues/tx-0/xps_cpus:  3
/sys/class/net/eth0/queues/tx-1/xps_cpus:  3

根据top输出查看软中断负载

top进入交互式界面后, 按1 显示所有cpu的负载. si 是软中断的CPU使用率. 如果高比如50%, 说明该CPU忙于处理中断, 通常就是收发网络IO

top - 18:58:33 up 16 days, 19:58,  2 users,  load average: 0.00, 0.01, 0.05
Tasks:  89 total,   2 running,  87 sleeping,   0 stopped,   0 zombie
%Cpu0  :  1.3 us,  0.0 sy,  0.0 ni, 98.7 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
%Cpu1  :  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3880032 total,  2911912 free,   199600 used,   768520 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  3411892 avail Mem

参考

https://www.kernel.org/doc/Documentation/IRQ-affinity.txt
https://www.kernel.org/doc/Documentation/networking/scaling.txt
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/7/html/performance_tuning_guide/sect-red_hat_enterprise_linux-performance_tuning_guide-networking-configuration_tools#sect-Red_Hat_Enterprise_Linux-Performance_Tuning_Guide-Configuration_tools-Configuring_Receive_Packet_Steering_RPS
https://lwn.net/Articles/370153/
https://lwn.net/Articles/412062/

Released under the MIT License.