Linux低延遲服務器系統調優

最近作了一些系統和網絡調優相關的測試,達到了指望的效果,有些感悟。同時,我也發現知乎上對Linux服務器低延遲技術的討論比較欠缺(滿嘴高並發現象);或者對現今cpu + 網卡的低延遲潛力認識不足(動輒FPGA現象),好比一篇知乎高讚的介紹FPGA的文章寫到「從延遲上講,網卡把數據包收到 CPU,CPU 再發給網卡,即便使用 DPDK 這樣高性能的數據包處理框架,延遲也有 4~5 微秒。更嚴重的問題是,通用 CPU 的延遲不夠穩定。例如當負載較高時,轉發延遲可能升到幾十微秒甚至更高」,恰好我前幾天作過相似的性能測試,發現一個tcp或udp的echo server能夠把網卡到網卡的延遲穩定在1微秒之內,不會比FPGA方案慢不少吧?linux

所以,我以爲有必要分享下本身的看法。總的來講,我打算分兩篇文章討論相關低延遲技術:ios

1)系統調優(本文):一些低延遲相關的Linux系統設置,和一些原則。緩存

2)網絡調優:使用solarflare網卡下降網絡IO延遲性能優化

這裏不打算介紹用戶空間的延遲優化,由於太普遍了,另外我以前的文章也分享一些解決某類問題的低延遲類庫。服務器

 

說到低延遲,關鍵點不在低,而在穩定,穩定便可預期,可掌控,其對於諸如高頻交易領域來講尤其重要。 而說到Linux的低延遲技術,一個不能不提的詞是"kernel bypass",也就是繞過內核,爲何呢?由於內核處理不只慢並且延遲不穩定。能夠把操做系統想象成一個龐大的框架,它和其餘軟件框架並無什麼本質的不一樣,只不過更加底層更加複雜而已。既然是框架,就要考慮到通用性,須要知足各類對類型用戶的需求,有時你只須要20%的功能,卻只能take all。網絡

所以我認爲一個延遲要求很高(好比個位數微秒級延遲)的實時任務是不能觸碰內核的,(固然在程序的啓動初始化和中止階段沒有個要求,That's how linux works)。 這裏的避免觸碰是一個比bypass更高的要求:不能以任何方式進入內核,不能直接或間接的執行系統調用(trap),不能出現page fault(exception),不能被中斷(interrupt)。trap和exception是主動進入內核的方式,能夠在用戶程序中避免,這裏不深刻討論(好比在程序初始化階段分配好全部須要的內存並keep的物理內存中;讓其餘非實時線程寫日誌文件等)。本文的關鍵點在於避免關鍵線程被中斷,這是個比較難達到的要求,可是gain卻不小,由於它是延遲穩定的關鍵點。即便中斷髮生時線程是空閒的,但從新回到用戶態後cpu緩存被污染了,下一次處理請求的延遲也會變得不穩定。併發

不幸的是Linux並無提供一個簡單的選項讓用戶徹底關閉中斷,也不可能這麼作(That's how linux works),咱們只能想法設法避免讓關鍵任務收到中斷。咱們知道,中斷是cpu core收到的,咱們可讓關鍵線程綁定在某個core上,而後避免各類中斷源(IRQ)向這個core發送中斷。綁定能夠經過tasksetsched_setaffinity實現,這裏不贅述。 避免IRQ向某個core發中斷能夠經過改寫/proc/irq/*/smp_affinity來實現。例如整個系統有一塊cpu共8個核,咱們想對core 4~7屏蔽中斷,只需把屏蔽中斷的core(0 ~ 3)的mask "f"寫入smp_affinity文件。這個操做對硬件中斷(好比硬盤和網卡)都是有效的,但對軟中斷無效(好比local timer interrupt和work queue),對於work queue的屏蔽能夠經過改寫/sys/devices/virtual/workqueue/*/cpumask來實現,本例中仍是寫入"f"。框架

那麼剩下的主要就是local timer interrupt(LOC in /proc/interrupts)了。Linux的scheduler time slice是經過LOC實現的,若是咱們讓線程獨佔一個core,就不須要scheduler在這個core上切換線程了,這是能夠作到的:經過isolcpus系統啓動選項隔離一些核,讓它們只能被綁定的線程使用,同時,爲了減小獨佔線程收到的LOC頻率,咱們還須要使用"adaptive-ticks"模式,這能夠經過nohz_fullrcu_nocbs啓動選項實現。本例中須要在系統啓動選項加入isolcpus=4,5,6,7 nohz_full=4,5,6,7 rcu_nocbs=4,5,6,7 來使得4~7核變成adaptive-ticks。adaptive-ticks的效果是:若是core上的running task只有一個時,系統向其發送LOC的頻率會下降成每秒一次,內核文檔解釋了不能徹底屏蔽LOC的緣由:"Some process-handling operations still require the occasional scheduling-clock tick. These operations include calculating CPU load, maintaining sched average, computing CFS entity vruntime, computing avenrun, and carrying out load balancing. They are currently accommodated by scheduling-clock tick every second or so. On-going work will eliminate the need even for these infrequent scheduling-clock ticks."。tcp

至此,經過修改系統設置,咱們可以把中斷頻率下降成每秒一次,這已經不錯了。若是想作的更完美些,讓關鍵線程長時間(好比幾個小時)不收到任何中斷,只能修改內核延長中斷的發送週期。不一樣kernel版本相關代碼有所差別,這裏就不深刻討論。不過你們可能會顧慮:這樣改變系統運行方式會不會致使什麼問題呢?個人經驗是,這有可能會影響某些功能的正常運轉(如內核文檔提到的那些),但我還沒有發現程序和系統發生任何異常,說明這項內核修改至少不會影響我須要的功能,我會繼續使用。高併發

兩個原則:

1)若是一件事情能夠被delay一段時間,那它每每可以被delay的更久,由於它沒那麼重要。

2)不要爲不使用的東西付費,對於性能優化來講尤其如此。

 

如何檢測中斷屏蔽的效果呢?能夠watch/proc/interrupts文件的變化 。更好的方法是用簡單的測試程序來驗證延遲的穩定性:

#include <iostream>  uint64_t now() { return __builtin_ia32_rdtsc(); } int main() { uint64_t last = now(); while (true) { uint64_t cur = now(); uint64_t diff = cur - last; if (diff > 300) { std::cout << "latency: " << diff << " cycles" << std::endl; cur = now(); } last = cur; } return 0; } 

經過taskset綁定一個核運行程序,每進入一次內核會打印一條信息。

 

最後,除了進入內核之外,影響延遲穩定性的因素還有cache misstlb miss

對於減小cache miss,一方面須要優化程序,minimize memory footprint,或者說減小一個操做訪問cache line的個數,一個緩存友好例子是一種能高速查找的自適應哈希表文章中的哈希表的實現方式。另外一方面,能夠經過分(lang)配(fei)硬件資源讓關鍵線程佔有更多的緩存,好比系統有兩塊CPU,每塊8核,咱們能夠把第二塊CPU的全部核都隔離掉,而後把關鍵線程綁定到其中的部分核上,可能系統只有一兩個關鍵線程,但它們卻能擁有整塊CPU的L3 cache。

對於減小tlb miss,可使用huge pages。

連接:https://pan.baidu.com/s/1v5gm7n0L7TGyejCmQrMh2g 提取碼:x2p5

免費分享,可是X度限制嚴重,如若連接失效點擊連接或搜索加羣 羣號744933466

相關文章
相關標籤/搜索