RPS、RFS优化

名词解释

出现RFS/RPS的原因主要是由于网卡不支持多队列,过多的网卡收包和发包中断集中在一个CPU核心,通过软件实现将网卡的中断分散到各个CPU上,模拟网卡多队列。

在单队列网卡的情况下,RPS/RFS相当于在系统层用软件模拟了多队列的情况,以便达到CPU的均衡。

如网卡支持多队列则可使用SMP irq affinity直接绑定硬中断。

  • RPS(Receive Packet Steering,接收数据包引导)
  • RFS(Receive flow steering,接收流引导)
  • RSS(receive side scaling,接收方缩放)

参考文档: https://www.cnblogs.com/yuanqiangfei/p/16689532.html

RPS

RPS(Receive Packet Steering)主要是把软中断的负载均衡到各个cpu,简单来说,是网卡驱动对每个流生成一个hash标识,这个HASH值得计算可以通过四元组来计算(SIP,SPORT,DIP,DPORT),然后由中断处理的地方根据这个hash标识分配到相应的CPU上去,这样就可以比较充分的发挥多核的能力了。

通俗点来说就是在软件层面模拟实现硬件的多队列网卡功能,如果网卡本身支持多队列功能的话RPS就不会有任何的作用。

该功能主要针对单队列网卡多CPU环境,如网卡支持多队列则可使用SMP irq affinity直接绑定硬中断。

RPS配置方法

配置示例如下所示

# eth1
for i in {0..23}; do echo "ffffff" > /sys/class/net/eth1/queues/rx-${i}/rps_cpus; done
for i in {0..23}; do echo "4096" > /sys/class/net/eth1/queues/rx-${i}/rps_flow_cnt; done
# eth2
for i in {0..23}; do echo "ffffff" > /sys/class/net/eth2/queues/rx-${i}/rps_cpus; done
for i in {0..23}; do echo "4096" > /sys/class/net/eth2/queues/rx-${i}/rps_flow_cnt; done
  • 这里的ffffff是CPU的核心掩码,24核心掩码对应2机制为1111 1111 1111 1111 1111 1111,每个数字对应1个cpu核心,数字0表示不开启rps,数字1表示开启rps,转换为16进制为ffffff
  • rps_flow_cnt就是RFS,表示当前网络设备rps队列的流表数,需要设置为2的整数次幂,建议设置为4096。数值越大,同时所能处理的rps流越多。

RFS

RFS是RPS的功能扩展,用来提高CPU缓存命中率,减少网络延迟。RPS仅根据队列长度转发数据包,而RFS使用RPS后端计算最合适的CPU,然后根据数据包的应用程序的位置来转发数据包。(同1个sockect的包,命中为同1个CPU进行处理)

默认情况下禁用RFS。要启用RFS,必须编辑两个文件:

  • /proc/sys/net/core/rps_sock_flow_entries指定并发活动连接的最大预期数量。对于中等服务器负载,建议使用32768。在实践中,所有输入的值都被四舍五入到最接近的2次方。
  • /sys/class/net/ethxx/queues/rx-${i}/rps_flow_cnt:它的值为rps_sock_flow_entries的值除以接收队列的数量。例如,如果rps_flow_条目设置为32768,并且有16个配置的接收队列,则rps_flow_cnt应设置为2048。对于单队列设备,rps_flow_cnt的值与rps_sock_flow_条目的值相同。

    同一个sockect的包,命中为同一个CPU进行处理。如果单个应用数据量大于单个CPU可以处理的数据量,可以通过配置更大的帧大小,来减少中断次数,从而减少CPU的处理工作量。或者,考虑 NIC offload选项或更快的CPU。

附录

自动设置rps脚本

#!/bin/bash
cpu_count=`cat /proc/cpuinfo | grep -c processor`
binary=$(printf "%${cpu_count}s" | tr " " "1")
hex=$(printf "%x\n" "$((2#$binary))")

for j in `ip -br link show | grep -E '^[eth|ens]' | awk '{print $1}'`;do
    for ((i = 0; i < cpu_count; i++));do
        echo ${hex} > /sys/class/net/${j}/queues/rx-${i}/rps_cpus
        echo "4096" > /sys/class/net/${j}/queues/rx-${i}/rps_flow_cnt
        # cat /sys/class/net/${j}/queues/rx-${i}/rps_cpus
        # cat /sys/class/net/${j}/queues/rx-${i}/rps_flow_cnt
    done
done

grep 'net.core.rps_sock_flow_entries' /etc/sysctl.conf > /dev/null
if [ $? == 1 ]; then
    echo "# set rps" >> /etc/sysctl.conf
    echo net.core.rps_sock_flow_entries=$(($cpu_count * 4096)) >> /etc/sysctl.conf
    sysctl -w net.core.rps_sock_flow_entries=$(($cpu_count * 4096))
fi

回退

#!/bin/bash
cpu_count=`cat /proc/cpuinfo | grep -c processor`
binary=$(printf "%${cpu_count}s" | tr " " "1")
hex=$(printf "%x\n" "$((2#$binary))")

for j in `ip -br link show | grep -E '^[eth|ens]' | awk '{print $1}'`;do
    for ((i = 0; i < cpu_count; i++));do
        echo "0" > /sys/class/net/${j}/queues/rx-${i}/rps_cpus
        echo "0" > /sys/class/net/${j}/queues/rx-${i}/rps_flow_cnt
        # cat /sys/class/net/${j}/queues/rx-${i}/rps_cpus
        # cat /sys/class/net/${j}/queues/rx-${i}/rps_flow_cnt
    done
done
上一篇
下一篇