1. 程式人生 > >大量小包的CPU密集型系統調優案例一則(轉)

大量小包的CPU密集型系統調優案例一則(轉)

我的blog前面有一篇文章描述了軟終端導致單cpu消耗100%,導致機器丟包跟延遲高的問題,文中我只是簡單的說明了一下升級核心進行解決的,這個問題我並沒有進行一個問題解決的說明,經歷了一系列的調整後,單機的併發從單機單網絡卡承受100M流量到160M流量,到現在的最高的230M流量,在程式沒有大規模修改的情況下效果還是十分的明顯,這次這篇文章將完整的說一下我的一個解決方法:
先說說我的場景,我目前負責的一個專案,大量的小資料包,長連線,每個資料包都不大,大概10Kbit左右一個包,但是數量十分之大,目前在生產環境中最大的資料包數量高達15W/s的數量,常見的網遊系統,小圖片cdn系統,這些服務型別都算是這種型別,單網絡卡流量不大,但是資料包數量極大,我目前調優的結果是
在Xeon E5504, BCM5716的網絡卡,8G的dell r410的機器,單網絡卡實現了230MBits大的流量,系統的load為0,8顆cpu每一顆還有10%左右的IDLE,由於我們的系統是資料包的轉發,還有一個網絡卡同期的流量使220M,12.8W的資料包,算上總數,大概可以到450MBits的流量,25.5W的小包,由於人數有限,流量沒有跑上去,預計可以跑到480MBits的流量,生產環境的一臺機器的資料:
流量及機器的網絡卡包數量


機器的cpu消耗

首先機器的選型,由於大量小包的cpu密集的系統,當然cpu越效能越高越好咯,但是成本相應的高。對於這種型別的機器,網絡卡選型也是十分的重要,一定要選擇支援msi-x的網絡卡型別,什麼是msi-x大家可以查詢google資料去了解一下,目前市面出售的大部分最新的網絡卡都有這個功能,檢視方法lspci -v,看到如下圖的內容

再者網絡卡是否支援多佇列,多佇列網絡卡十分的重要,不是多佇列的網絡卡,這篇文章幾乎不需要看了,可以直接忽略掉,檢視方法cat /proc/interrupts,這個方法並不適用所有的作業系統例如在rhel 5.5的os當中,bcm5716的網絡卡就看不到,具體我也沒有查到怎麼檢視的方法,麻煩知道的使用者告知一聲,如果是的話應該可以看到如下圖的內容

每一個網絡卡有8個佇列,對於這種大量小包的cpu密集型的系統,多佇列的網絡卡效能至少提供效能50%以上,我們生產環境的有臺非多佇列的Intel 82574的網絡卡調優後只能跑到160M左右流量,跟上圖明顯的一個對比.而同等情況下買一個多佇列的網絡卡明顯要便宜很多。
作業系統的選擇,目前大部分企業使用的是rhel系列的os,包括標準的rhel跟centos作為一個生產環境的os,目前主力的版本還是rhel 5系列的os,而rhel 5系列的核心版本對於軟中斷處理並不是很好,調優的結果不是很理想,在rhel 5系列的os上,我們最高流量單網絡卡也就是160M左右,而且機器的load也很高了,機器已經出現小量的丟包,而且只是使用了4到6個cpu還有幾個cpu沒有利用上,機器效能沒有挖掘完畢,由於我們的機器沒有儲存的壓力,單純的只是消耗cpu資源,沒有io的壓力,於是大膽的啟用剛出的rhel 6.1的系統,看重這個系統的原因是,該os的核心已經加入了google的兩個原本在2.6.35當中才啟用的2個補丁——RPS/RFS,RPS主要是把軟中斷的負載均衡到各個cpu,由於RPS只是單純把資料包均衡到不同的cpu,這個時候如果應用程式所在的cpu和軟中斷處理的cpu不是同一個,此時對於cpu cache的影響會很大,那麼RFS確保應用程式處理的cpu跟軟中斷處理的cpu是同一個,這樣就充分利用cpu的cache,預設情況下,這個功能並沒有開啟,需要手動開啟開啟方法,開啟的前提是多佇列網絡卡才有效果。
echo ff > /sys/class/net/<interface>/queues/rx-<number>/rps_cpus
echo 4096 > /sys/class/net/<interface>/queues/rx-<number>/rps_flow_cnt
echo 30976 > /proc/sys/net/core/rps_sock_flow_entries
對於2個物理cpu,8核的機器為ff,具體計算方法是第一顆cpu是00000001,第二個cpu是00000010,第3個cpu是00000100,依次類推,由於是所有的cpu都負擔,所以所有的cpu數值相加,得到的數值為11111111,十六進位制就剛好是ff。而對於/proc/sys/net/core/rps_sock_flow_entries的數值是根據你的網絡卡多少個通道,計算得出的資料,例如你是8通道的網絡卡,那麼1個網絡卡,每個通道設定4096的數值,8*4096就是/proc/sys/net/core/rps_sock_flow_entries的數值,對於記憶體大的機器可以適當調大rps_flow_cnt,這個時候基本可以把軟中斷均衡到各個cpu上了,而對於cpu的使用,還有其它的例如use,sys等,這個不均衡的話,cpu還是會浪費掉,同時對我們的程式針對多cpu進行小部分的開發跟重新編譯,本身我們程式就是多程序的一個模型,我們採用nginx的程序管理模型,一個master管理work程序,master分配每一個連線給work程序,由work程序處理使用者的請求,這樣每一個程序都能均衡負擔幾乎相同的處理請求,同時在6.1的系統中gcc新增一個openmp的指令,這個指令作用針對多核,增加程式的平行計算的功能,不需要大規模的更改程式碼就能實現多核的並行性計算,具體使用使用方法請見如下url

http://zh.wikipedia.org/zh/OpenMP

針對上面的處理,基本上可以實現cpu按理說可以實現完全的均衡了,但是當我們在實際的使用過程中發現還是cpu還不是100%的均衡,存在1到2個cpu消耗量還是比其它的要大20%左右,導致在高峰期有1到2個cpu的idle使用完畢,導致使用者使用存在卡的情況,這個時候,需要手動調節一下cpu的使用情況,在這操作之前先了解幾個名詞以及其作用
一個是IO-APIC(輸入輸出裝置的高階可程式設計中斷控制器)
為了充分挖掘 SMP 體系結構的並行性,能夠把中斷傳遞給系統中的每個CPU至關重要,基於此理由,Intel 引入了一種名為 I/O-APIC的東西。該元件包含兩大組成部分:一是“本地 APIC”,主要負責傳遞中斷訊號到指定的處理器;舉例來說,一臺具有三個處理器的機器,則它必須相對的要有三個本地 APIC。另外一個重要的部分是 I/O APIC,主要是收集來自 I/O 裝置的 Interrupt 訊號且在當那些裝置需要中斷時傳送訊號到本地 APIC。這樣就能充分利用多cpu的並行性。如果使用者對於IO-APIC更感興趣,請見如下url的中的pdf的說明

http://wenku.baidu.com/view/ccdc114e2e3f5727a5e962e9.html

另外一個就是irqbalance
irqbalance 用於優化中斷分配,它會自動收集系統資料以分析使用模式,並依據系統負載狀況將工作狀態置於 Performance mode 或 Power-save mode.處於 Performance mode時irqbalance 會將中斷儘可能均勻地分發給各個CPU以充分利用 CPU 多核,提升效能.處於 Power-save mode時,irqbalance 會將中斷集中分配給第一個 CPU,以保證其它空閒 CPU 的睡眠時間,降低能耗
通過這我們就發現我們是一個非常繁重的系統,並沒有節能的需求,而是需要充分利用各個cpu的效能,而事實上在一個大量小包的系統上,irqbalance優化幾乎沒有效果,而且還使得cpu消耗不均衡,導致機器效能得不到充分的利用,這個時候需要把它給結束掉
/etc/init.d/irqbalance stop
同時,手動繫結軟中斷到指定的cpu,對於一個8個佇列的網絡卡,8核的機器,可以指定一個cpu處理一個網絡卡佇列的中斷請求,並根據cpu的消耗情況,手動調整單個網絡卡的佇列到資源消耗低的cpu上,實現手動均衡,具體操作方法,執行如下命令
cat /proc/interrupts

計算cpu的方法第一顆為00000001換算成16進製為1,第2顆cpu為00000010換算成16進製為2,依次類推得出,第8顆cpu為80,這樣就可以做如下的綁定了
echo 0001 > /proc/irq/<number>/smp_affinity
這樣就可以繫結中斷到指定的cpu了,這個時候有可能會問,我的機器是一個2通道的網絡卡,這個時候如果一個通道一個cpu繫結,這個時候就浪費了6顆cpu了,還是達不到完全均衡負載,為什麼不能像前面rps那樣,
echo ff > /proc/irq/<number>/smp_affinity
設定一個ff達到所有的cpu一起均衡呢,這個因為io-apic工作的2個模式logical/low priority跟fixed/physical模式,這兩個模式的區別在於,前面一個模式能夠把網絡卡中斷傳遞給多核cpu進行處理,後一種模式對應一個網絡卡佇列的中斷只能傳遞給單cpu進行處理,而linux是fixed/physical工作模式,如果你設定上面那個選項,只能第一個cpu進行軟中斷的處理,又回到未優化前了。那麼為什麼不開啟logical/low priority呢,當一個tcp連線發起,當資料包到底網絡卡,網絡卡觸發中斷,中斷請求到其中一個cpu,而logical/lowpriority並不能保證後續的資料包跟前面的包處於同一個cpu,這樣後面的資料包發過來,又可能處於另外一個cpu,這個時候同一個socket都得檢查自己的cpu的cache,這樣就有可能部分cpu取不到資料,因為本身它的cache並沒用資料,這個時候就多了多次的cpu的查詢,不能充分利用cpu的效率。對於部分機器來說並不能開啟logical/low priority模式,一種可能是cpu過多,另外一種是bios不支援。因此對於那種單佇列網絡卡並不能充分發揮cpu的效能。
經過上述的調整基本可以達到幾乎完全均衡的效果,每個cpu都能發揮他的效果。也幾乎可以到達我調優的效果
對於一個完整的系統來說,不僅有資料包傳送的需求還有資料接收的請求,而rps/rfs主要解決資料接收的一箇中斷均衡的問題,rps/rfs的作者提交了一個xps(Transmit Packet Steering), 這個patch主要是針對多佇列的網絡卡傳送時的優化,當傳送一個數據包的時候,它會根據cpu來選擇對應的佇列,目前這個patch已經新增在2.6.38核心版本當中,我們已經在生產環境中,部分機器上已經使用上了,據作者的benchmark,能夠提高20%的效能,具體使用方法
echo ff > /sys/class/net/<interface>/queues/tx-<number>/xps_cpus
由於還是新上的系統,還沒敢大規模放使用者進來,還在測試系統的穩定性,不知道上限具體能到多少,從當前生產環境跑的流量來看,比同等其它的機器,cpu消耗情況,確實要減少一些,流量沒有跑上來,效果不是特別的明顯,還有待繼續測試,得出一個具體的結果。
另外對於intel的網絡卡的使用者,intel有個叫ioat的功能,關於ioat功能大家可以網上查查資料.
而對於centos的使用者來說,目前還只是出了6.0的版本,並沒有上述功能,要大規模的推廣,建議大家編譯2.6.38的核心版本,因為2.6.38的版本已經包含了上述幾個補丁。編譯核心生成核心的rpm包,能快速的在同一批機器上快速部署上去。
以上就是我的對cpu密集型系統的一個優化過程,歡迎大家來討論。