繼前一篇 #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 就能省很多事,但
這裡就不是主題範圍了...)