继前一篇 #1cWInSDW 分享第三层交换器优先权设定之后,这篇来分享一旦资料进入
网卡后,如何再优化 Linux 作业系统内部的资料流;这里一样是只探讨优化手法,
不讨论音质的改变(YMMV...)
关于 Receive Side Scaling, RSS 相关说明文件在此:
https://tinyurl.com/5d9zuace
RSS 也算是一个古老的存在了,许多高负载服务器也都会运用这个功能去分摊/处理
所接收的资料流。其运作原理是根据来源地址、来源埠、目的位址及目的埠,送入
一个叫 Toeplitz(默认)的矩阵去换算出 hash 值,再用这个 hash 去对应的接受
储列。例如,我的 HQPlayer Embedded 主机接收 NFS 的资料,根据相关地址和埠,
会交给储列 8 来处理:
https://imgur.com/pr9YHN4.jpg
管理者即能用这个机制,用 ethtool 去分配特定服务给特定软件所在的 CPU 接收
资料,来减少资料延迟。
在音讯网络里面,能想到的大概是 Roon -> RoonBridge 或是 Roon -> HQPlayer
-> NAA 这样的传递接收路径;RSS 的设定方式大同小异,这里就用我的 NAA <->
Ravenna bridge 做个例子,好处是内核设定的改动,都能反映在 Ravenna 器材上
的接收状态。
我的 NAA 是 Intel Atom x7425e 四核心的单板电脑,跑 Ubuntu 实时核心,有特别
隔离一颗 CPU 核心做其他的事情。开完机之后我们可以看到由于 CPU 为四核心,
RxTx 储列会有四个(RxTx-0~RxTx-3),但因为我把第四个 CPU 核心独立出来不受
干扰,因此储列的 IRQ 会自动转分布在前三个 CPU 核心。
https://imgur.com/WIDo1Xp.jpg
但 RSS indirection table 的数值仍是指向四个 Rx 储列,这表示仍有一个 CPU
核心要处理两个 Rx 储列:
https://imgur.com/x3pAL70.jpg
如果不去变动 RSS 等设定的话,NAA 交给 Ravenna 的资料,Ravenna 接收端会呈现
一些锯齿状:
https://imgur.com/MMe9OOY.jpg
这些锯齿状都还是在容许范围内,因此不会造成音讯中断/爆音/破音/杂音等问题,
只是看起来不舒服。
不知为何 x7425e 并未支援 hash key,手上工具算不出来资料流会走哪个储列,
因此策略上我个人是采取“机会均等”,让 enp1s0 这个负责 NAA 的网口所接收的
资料平均分给三个 Rx 储列,指令:
ethtool -X enp1s0 equal 3
(enp1s0 是我的网口名称,请自行代入自己机器抓出的网口名称。)
这样就能把 RSS indirection table 强制平均分配到三个 Rx 储列:
https://imgur.com/cjcLxHo.jpg
接下来再用 Receive Packet Steering, RPS 技巧,将 Rx 的储列重新顺给 CPU 第
0, 1, 2 核上,指令:
echo 1 > /sys/class/net/enp1s0/queues/rx-0/rps_cpus
echo 2 > /sys/class/net/enp1s0/queues/rx-1/rps_cpus
echo 4 > /sys/class/net/enp1s0/queues/rx-2/rps_cpus
1,2,4 是二进制遮罩转为十进制数值,二进制最右边是第一颗 CPU 核心,因此我
指定某个 Rx 储列由 CPU 第三颗核心处理的话,二进制遮罩是 0100,换算十进制
是 4,以此类推。
Rx 储列安排好之后,当然最理想是 IRQ 也一并和 Rx 储列调整,前面截图已知
RxTx-0, RxTx-1, RxTx-2 的 IRQ 分别是 128, 129, 130,我们就可以指定 IRQ
给相对的 CPU 核心:
echo 0 > /proc/irq/128/smp_affinity_list
echo 1 > /proc/irq/129/smp_affinity_list
echo 2 > /proc/irq/130/smp_affinity_list
(当然在做这个设定之前,要将 irqbalance 守护程式整个关掉。)
截图呈现三个 RxTx 储列的 IRQ 分别交给 CPU 0, 1, 2
https://imgur.com/QETjXby.jpg
为了配合这个 Rx 储列硬平均,我也指定 networkaudiod 这个 daemon 跑在负责这
三个储列的 CPU 核心上(networkaudiod 的主体 thread 会在这三个 CPU 核心上
随机跑动)。
不过这也还是单位时间内 33% 机率资料流会被 networkaudiod 的主 thread 吃到,
因此还可以再用 Receive Flow Steering, RPS 来辅助资料流导向的命中率,这个
部分因为服务单纯,因此 flow table 大小我并没有设很大,这部分就看个人的
需求而定:
echo 8192 > /proc/sys/net/core/rps_sock_flow_entries
其他 Rx 储列的 flow table 必须小于主 table size,我是设定 1024:
echo 1024 > /sys/class/net/enp1s0/queues/rx-0/rps_flow_cnt
echo 1024 > /sys/class/net/enp1s0/queues/rx-1/rps_flow_cnt
echo 1024 > /sys/class/net/enp1s0/queues/rx-2/rps_flow_cnt
NAA 经过 RSS + RPS + RFS 的调整之后,Ravenna 器材接收端变平顺了些:
https://imgur.com/wZia5OZ.jpg
若您的 endpoint 是小电脑,不妨试着调整看看,或许会有惊喜 :-)
(以上的调整是采 run-time 方式调整,若会写 shell script 就能省很多事,但
这里就不是主题范围了...)