RPS和RFS
- RPS 全稱是 Receive Packet Steering, 這是Google工程師 Tom Herbert ([email protected] )提交的內核補丁, 在2.6.35進入Linux內核. 這個patch採用軟件模擬的方式,實現了多隊列網卡所提供的功能,分散了在多CPU系統上數據接收時的負載, 把軟中斷分到各個CPU處理,而不需要硬件支持,大大提高了網絡性能。
- RFS 全稱是 Receive Flow Steering, 這也是Tom提交的內核補丁,它是用來配合RPS補丁使用的,是RPS補丁的擴展補丁,它把接收的數據包送達應用所在的CPU上,提高cache的命中率。
- 這兩個補丁往往都是一起設置,來達到最好的優化效果, 主要是針對單隊列網卡多CPU環境(多隊列多重中斷的網卡也可以使用該補丁的功能,但多隊列多重中斷網卡有更好的選擇:SMP IRQ affinity)
原理
RPS: RPS實現了數據流的hash歸類,並把軟中斷的負載均衡分到各個cpu,實現了類似多隊列網卡的功能。由於RPS只是單純的把同一流的數據包分發給同一個CPU核來處理了,但是有可能出現這樣的情況,即給該數據流分發的CPU核和執行處理該數據流的應用程序的CPU核不是同一個:數據包均衡到不同的cpu,這個時候如果應用程序所在的cpu和軟中斷處理的cpu不是同一個,此時對於cpu cache的影響會很大。那麼RFS補丁就是用來確保應用程序處理的cpu跟軟中斷處理的cpu是同一個,這樣就充分利用cpu的cache。
- 應用RPS之前: 所有數據流被分到某個CPU, 多CPU沒有被合理利用, 造成瓶頸
- 應用RPS之後: 同一流的數據包被分到同個CPU核來處理,但可能出現cpu cache遷躍
- 應用RPS+RFS之後: 同一流的數據包被分到應用所在的CPU核
必要條件
使用RPS和RFS功能,需要有大於等於2.6.35版本的Linux kernel.
如何判斷內核版本?
1
2
|
$ uname -r 2.6.38-2-686-bigmem |
對比測試
類別 | 測試客戶端 | 測試服務端 |
---|---|---|
型號 | BladeCenter HS23p | BladeCenter HS23p |
CPU | Xeon E5-2609 | Xeon E5-2630 |
網卡 | Broadcom NetXtreme II BCM5709S Gigabit Ethernet | Emulex Corporation OneConnect 10Gb NIC |
內核 | 3.2.0-2-amd64 | 3.2.0-2-amd64 |
內存 | 62GB | 66GB |
系統 | Debian 6.0.4 | Debian 6.0.5 |
超線程 | 否 | 是 |
CPU核 | 4 | 6 |
驅動 | bnx2 | be2net |
- 客戶端: netperf
- 服務端: netserver
- RPS cpu bitmap測試分類: 0(不開啓rps功能), one cpu per queue(每隊列綁定到1個CPU核上), all cpus per queue(每隊列綁定到所有cpu核上), 不同分類的設置值如下
- 0(不開啓rps功能)
/sys/class/net/eth0/queues/rx-0/rps_cpus 00000000 /sys/class/net/eth0/queues/rx-1/rps_cpus 00000000 /sys/class/net/eth0/queues/rx-2/rps_cpus 00000000 /sys/class/net/eth0/queues/rx-3/rps_cpus 00000000 /sys/class/net/eth0/queues/rx-4/rps_cpus 00000000 /sys/class/net/eth0/queues/rx-5/rps_cpus 00000000 /sys/class/net/eth0/queues/rx-6/rps_cpus 00000000 /sys/class/net/eth0/queues/rx-7/rps_cpus 00000000 /sys/class/net/eth0/queues/rx-0/rps_flow_cnt 0 /sys/class/net/eth0/queues/rx-1/rps_flow_cnt 0 /sys/class/net/eth0/queues/rx-2/rps_flow_cnt 0 /sys/class/net/eth0/queues/rx-3/rps_flow_cnt 0 /sys/class/net/eth0/queues/rx-4/rps_flow_cnt 0 /sys/class/net/eth0/queues/rx-5/rps_flow_cnt 0 /sys/class/net/eth0/queues/rx-6/rps_flow_cnt 0 /sys/class/net/eth0/queues/rx-7/rps_flow_cnt 0 /proc/sys/net/core/rps_sock_flow_entries 0
- one cpu per queue(每隊列綁定到1個CPU核上)
/sys/class/net/eth0/queues/rx-0/rps_cpus 00000001 /sys/class/net/eth0/queues/rx-1/rps_cpus 00000002 /sys/class/net/eth0/queues/rx-2/rps_cpus 00000004 /sys/class/net/eth0/queues/rx-3/rps_cpus 00000008 /sys/class/net/eth0/queues/rx-4/rps_cpus 00000010 /sys/class/net/eth0/queues/rx-5/rps_cpus 00000020 /sys/class/net/eth0/queues/rx-6/rps_cpus 00000040 /sys/class/net/eth0/queues/rx-7/rps_cpus 00000080 /sys/class/net/eth0/queues/rx-0/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-1/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-2/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-3/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-4/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-5/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-6/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-7/rps_flow_cnt 4096 /proc/sys/net/core/rps_sock_flow_entries 32768
- all cpus per queue(每隊列綁定到所有cpu核上)
/sys/class/net/eth0/queues/rx-0/rps_cpus 000000ff /sys/class/net/eth0/queues/rx-1/rps_cpus 000000ff /sys/class/net/eth0/queues/rx-2/rps_cpus 000000ff /sys/class/net/eth0/queues/rx-3/rps_cpus 000000ff /sys/class/net/eth0/queues/rx-4/rps_cpus 000000ff /sys/class/net/eth0/queues/rx-5/rps_cpus 000000ff /sys/class/net/eth0/queues/rx-6/rps_cpus 000000ff /sys/class/net/eth0/queues/rx-7/rps_cpus 000000ff /sys/class/net/eth0/queues/rx-0/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-1/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-2/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-3/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-4/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-5/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-6/rps_flow_cnt 4096 /sys/class/net/eth0/queues/rx-7/rps_flow_cnt 4096 /proc/sys/net/core/rps_sock_flow_entries 32768
測試方法: 每種測試類型執行3次,中間睡眠10秒, 每種測試類型分別執行100、500、1500個實例, 每實例測試時間長度爲60秒
- TCP_RR 1 byte: 測試TCP 小數據包 request/response的性能
1
netperf -t TCP_RR -H $serverip -c -C -l 60
- UDP_RR 1 byte: 測試UDP 小數據包 request/response的性能
1
netperf -t UDP_RR -H $serverip -c -C -l 60
- TCP_RR 256 byte: 測試TCP 大數據包 request/response的性能
1
netperf -t TCP_RR -H $serverip -c -C -l 60 -- -r256,256
- UDP_RR 256 byte: 測試UDP 大數據包 request/response的性能
1
netperf -t UDP_RR -H $serverip -c -C -l 60 -- -r256,256
TPS測試結果
CPU負載變化
在測試過程中,使用mpstat收集各個CPU核的負載變化
- 關閉RPS/RFS: 可以看出關閉RPS/RFS時,軟中斷的負載都在cpu0上,並沒有有效的利用多CPU的特性,導致了性能瓶頸
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle Average: all 3.65 0.00 35.75 0.05 0.01 14.56 0.00 0.00 45.98 Average: 0 0.00 0.00 0.00 0.00 0.00 100.00 0.00 0.00 0.00 Average: 1 4.43 0.00 37.76 0.00 0.11 11.49 0.00 0.00 46.20 Average: 2 5.01 0.00 45.80 0.00 0.00 0.00 0.00 0.00 49.19 Average: 3 5.11 0.00 45.07 0.00 0.00 0.00 0.00 0.00 49.82 Average: 4 3.52 0.00 40.38 0.14 0.00 0.00 0.00 0.00 55.96 Average: 5 3.85 0.00 39.91 0.00 0.00 0.00 0.00 0.00 56.24 Average: 6 3.62 0.00 40.48 0.14 0.00 0.00 0.00 0.00 55.76 Average: 7 3.87 0.00 38.86 0.11 0.00 0.00 0.00 0.00 57.16
- 每隊列關聯到一個CPU TCP_RR: 可以看出軟中斷負載已經能分散到各個CPU核上,有效利用了多CPU的特性,大大提高了系統的網絡性能
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle Average: all 5.58 0.00 59.84 0.01 0.00 22.71 0.00 0.00 11.86 Average: 0 2.16 0.00 20.85 0.00 0.04 72.03 0.00 0.00 4.93 Average: 1 4.68 0.00 46.27 0.00 0.00 42.73 0.00 0.00 6.32 Average: 2 6.76 0.00 63.79 0.00 0.00 11.03 0.00 0.00 18.42 Average: 3 6.61 0.00 65.71 0.00 0.00 11.51 0.00 0.00 16.17 Average: 4 5.94 0.00 67.83 0.07 0.00 11.59 0.00 0.00 14.58 Average: 5 5.99 0.00 69.42 0.04 0.00 12.54 0.00 0.00 12.01 Average: 6 5.94 0.00 69.41 0.00 0.00 12.86 0.00 0.00 11.78 Average: 7 6.13 0.00 69.61 0.00 0.00 14.48 0.00 0.00 9.77
- 每隊列關聯到一個CPU UDP_RR: CPU負載未能均衡的分佈到各個CPU, 這是由於網卡hash計算在UDP包上的不足, 詳細請見本文後記部分
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle Average: all 3.01 0.00 29.84 0.07 0.01 13.35 0.00 0.00 53.71 Average: 0 0.00 0.00 0.08 0.00 0.00 90.01 0.00 0.00 9.91 Average: 1 3.82 0.00 32.87 0.00 0.05 12.81 0.00 0.00 50.46 Average: 2 4.84 0.00 37.53 0.00 0.00 0.14 0.00 0.00 57.49 Average: 3 4.90 0.00 37.92 0.00 0.00 0.16 0.00 0.00 57.02 Average: 4 2.57 0.00 32.72 0.20 0.00 0.09 0.00 0.00 64.42 Average: 5 2.66 0.00 33.54 0.11 0.00 0.08 0.00 0.00 63.60 Average: 6 2.75 0.00 32.81 0.09 0.00 0.06 0.00 0.00 64.30 Average: 7 2.71 0.00 32.66 0.17 0.00 0.06 0.00 0.00 64.40
- 每隊列關聯到所有CPU: 可以看出軟中斷負載已經能分散到各個CPU核上,有效利用了多CPU的特性,大大提高了系統的網絡性能
Average: CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle Average: all 5.39 0.00 59.97 0.00 0.00 22.57 0.00 0.00 12.06 Average: 0 1.46 0.00 21.83 0.04 0.00 72.08 0.00 0.00 4.59 Average: 1 4.45 0.00 46.40 0.00 0.04 43.39 0.00 0.00 5.72 Average: 2 6.84 0.00 65.62 0.00 0.00 11.39 0.00 0.00 16.15 Average: 3 6.71 0.00 67.13 0.00 0.00 12.07 0.00 0.00 14.09 Average: 4 5.73 0.00 66.97 0.00 0.00 10.71 0.00 0.00 16.58 Average: 5 5.74 0.00 68.57 0.00 0.00 13.02 0.00 0.00 12.67 Average: 6 5.79 0.00 69.27 0.00 0.00 12.31 0.00 0.00 12.63 Average: 7 5.96 0.00 68.98 0.00 0.00 12.00 0.00 0.00 13.06
結果分析
以下結果只是針對測試服務器特定硬件及系統的數據,在不同測試對象的RPS/RFS測試結果可能有不同的表現
TCP性能:
- 在沒有打開RPS/RFS的情況下,隨着進程數的增加,TCP tps性能並明顯沒有提升,在184~188k之間。
- 打開RPS/RFS之後,隨着RPS導致軟中斷被分配到所有CPU上和RFS增加的cache命中, 小數據包(1字節)及大數據包(256字節,相對小數據包而言, 而不是實際應用中的大數據包)的tps性能都有顯著提升
- 100個進程提升40%的性能(兩種RPS/RFS設置的性能結果一致), cpu負載升高40%
- 500個進程提升70%的性能(兩種RPS/RFS設置的性能結果一致), cpu負載升高62%
- 1500個進程提升75%的性能(兩種RPS/RFS設置的性能結果一致), cpu負載升高77%
UDP性能:
- 在沒有打開RPS/RFS的情況下,隨着進程數的增加,UDP tps性能並明顯沒有提升,在226~235k之間。
- 打開RPS/RFS之後,,隨着RPS導致軟中斷被分配到所有CPU上和RFS增加的cache命中, 小數據包(1字節)及大數據包(256字節,相對小數據包而言, 而不是實際應用中的大數據包)的TPS性能, 在每隊列關聯到所有CPU的情況下有顯著提升, 而每隊列關聯到一個CPU後反倒是導致了UDP tps性能下降1% (這是bnx2網卡不支持UDP port hash及此次測試的侷限性造成的結果, 詳細分析見: 後記)
- 每隊列關聯到所有CPU的情況下, 在100個進程時小包提升40%的性能, cpu負載升高60%; 大包提升33%, cpu負載升高47%
- 每隊列關聯到所有CPU的情況下, 在500個進程提小包提升62%的性能, cpu負載升高71%; 大包提升60%, cpu負載升高65%
- 每隊列關聯到所有CPU的情況下, 在1500個進程提升65%的性能, cpu負載升高75%; 大包提升64%, cpu負載升高74%
後記
UDP在每隊列綁定到一個CPU時性能下降,而綁定到所有CPU時,卻有性能提升,這一問題涉及到幾個因素,當這幾個因素湊一起時,導致了這種奇特的表現。
- 此次測試的侷限性:本次測試是1對1的網絡測試,產生的數據包的IP地址都是相同的
- bnx2網卡在RSS hash上,不支持UDP Port,也就是說,網卡在對TCP數據流進行隊列選擇時的hash包含了ip和port, 而在UDP上的hash, 只有IP地址,導致了本次測試(上面的侷限性影響)的UDP數據包的hash結果都是一樣的,數據包被轉送到同一條隊列。
- 單單上面兩個因素,還無法表現出UDP在每隊列綁定到一個CPU時性能下降,而綁定到所有CPU時,卻有性能提升的現象。 因爲RPS/RFS本身也有hash計算,也就是進入隊列後的數據包,還需要經過RPS/RFS的hash計算(這裏的hash支持udp port), 然後進行第二次數據包轉送選擇;如果每隊列綁定到一個CPU, 系統直接跳過第二次hash計算,數據包直接分配到該隊列關聯的CPU處理,也就導致了在第一次hash計算後被錯誤轉送到某一隊列的UDP數據包,將直接送到cpu處理,導致了性能的下降; 而如果是每隊列綁定到所有CPU, 那麼進入隊列後的數據包會在第二次hash時被重新分配,修正了第一次hash的錯誤選擇。
相關對比測試
1. SMP IRQ affinity: http://www.igigo.net/archives/231
參考資料
- Software receive packet steering
- Receive Packet Steering
- Receive packet steering
- Receive Flow Steering
- linux kernel 2.6.35中RFS特性詳解
- Linux 2.6.35 新增特性 RPS RFS
- kernel/Documentation/networking/scaling.txt