賈斯玻.布魯勒在2015年澳大利亞
Linux研討會(LCA)的有關內核的小型研討會上提到:100GB的網卡即將來臨(見幻燈片,PDF格式的)。對Linux內核來說,要以最大的速度驅動這樣的適配器將是巨大的挑戰。應對這一挑戰是目前和未來一段時間內工作的重心。好消息是Linux網絡通信速度已經有了很大的提高-不過還有一些問題有待解決。
挑戰
由於網絡適配器的速度越來越快,那麼發送數據包的時間間隔(也就是內核處理一個包的時間)就會越來越短。就當前正在使用的10GB適配器而言,發送兩個1538字節數據包的時間間隔是1230納秒,40GB的網絡通信會把這個時間間隔大幅縮減到307納秒。很自然,100GB的網絡適配器會急劇縮減這個時間間隔,即縮減一個包的處理時間大約為120納秒。此時,網卡每秒就要處理八百一十五萬個數據包。這樣就不會有太多時間來進行數據包的處理了。
那麼,如果像大多數人一樣沒有100GB的網絡適配器把玩一下,你該怎麼做呢? 你可以替代性地使用10GB的網絡適配器發送很小的幀。以太網所能發送的最小幀長度是84字節;賈斯玻說使用10G網絡適配器發送最小尺寸的以太網幀的時間間隔是67.2納秒。能夠處理最小尺寸以太網幀負載的系統就適宜於處理100GB的網絡通信,不過目前還未實現。而且對這種負載的處理也非常困難:對一個3GHz的CPU來說,處理一個包只需要200個CPU周期。賈斯玻還提醒說:這點時間本身就不算多。
以往,內核就沒有在處理密集型網絡通信的工作負載方面做太多工作。這也使得大量次要的網絡通信實現完全不包含在內核網絡協議棧中。如此的系統需求就表明內核沒有充分利用上硬件;由單個CPU執行的次要實現就可以以最大速度驅動適配器,這就是內核主線開發犯難的地方。
賈斯玻說現在的問題是:內核開發人員過去曾經集中力量添加多核支持。在此情形下,他們就不會關心單核運行效率的衰退情況。由此而產生的結果就是:現今的網絡通信協議棧可以很好地處理許多種工作負載,然而對於哪些對時延特別敏感的工作負載則無能為力了。現今的內核每秒每核心只能處理一百萬到兩百萬之間個數據包,而某些不使用內核的方法每秒每核心可處理的數據包數可達一千五百萬個。
預算所需時間
如果你打算解決這個問題,那麼你就需要仔細地看看處理一個包的每一個步驟所花費的時間。例如,賈斯玻提到的3GHz的處理器上出現一次高速緩沖未命中需要32納秒才找到對應的數據。因此,只要有兩次未命中就可以占完處理一個數據包所需要的時間。假如套接字緩沖("SKB")在64位系統上占有四個高速緩沖行,而且許多套接字緩沖(“SKB”)都是在包處理期間寫入的,那麼問題的第一部分就非常明顯了-四次高速緩沖未命中需要的時間將會比可使用的時間更長。
除上述外,X86架構上的鎖的原子級別的操作大概需要費時8.25ns. 也就是說,在實踐中一次最短的自旋鎖的上鎖/解鎖的過程就要耗時16ns還多一點。 所以盡管有大量的鎖操作,但是在這方面卻沒有多少空間可以降低延時。
接下來是與系統進行一次通信的時間消耗。在安裝有SELinux及審計功能(auditing enabled)的系統上,一次系統的調用就要耗時75ns — 超過了幀處理的整個時間。 禁用審計和SELinux可以減少時間,使系統通信降到42ns以下。這看上去好多了,但是還是很大的時間消耗。有許多方法可以在處理多個包的時候降低時間消耗,包括調用sendmmsg(),recvmmsg(),send
file()和splice()這樣的函數。但是在實際中,有開發者覺得這些函數的表現並不如預期的那麼好,但是他們又找不出原因。Chirs
toph Lameter站在旁觀者的角度指出來說,那些對延時敏感的用戶可以傾向於使用InfiniBand架構的“IB verbs”機制。
賈斯玻詢問:如果上面所要花費的時間已經確定,那麼不需要網絡通信的方案是如何獲得更高的性能呢?關鍵在於批量進行操作處理和資源的預分配和預獲取。這樣的操作始終在CPU范圍內運行,而且不需要上鎖。這些因素對於縮減包的元數據和減少系統調用的次數來說也非常重要。要想在速度上再提高一點,哪些可充分利用高速緩沖的數據結構可以幫一些忙。在上面所述的所有技術中,批量進行操作處理是最最重要的。一個包所需要的處理時間可能無法接受,但如果是一次性執行多個包的話,你就很容易接受了。一個包加鎖需要16納秒讓人很受傷,如果一次性加鎖16個包,那麼每個包所需要的處理時間就縮減到1納秒了。
對批量操作的改進
因此,毫不奇怪,賈斯玻的工作過去曾經集中精力改進網絡層的批量操作。其中包括 TCP 層的批量傳輸工作,這裡十月份的文章中有所介紹;有關它是如何運行的詳細情況請參閱上面鏈接的那篇文章。簡要的來說,機制是這樣的:通知網絡驅動還有更多的數據包等著傳輸,讓驅動延遲花費時間較多的操作,直到所有的包都填滿隊列。如果把這樣的修改放在適當的地方,那麼在同樣小的數據包一個接著一個傳輸的情況下,他編寫的系統每秒至少能傳輸一千四百八十萬個數據包。
他說做到這些技巧在於給網絡通信層添加了進行批量操作處理的應用程序接口(API),同時不會增加系統延遲時間。通常,延遲和吞吐量之間必須要進行某種權衡;然而,在這裡,延遲和吞吐量都有所改進。特別難於處理的技巧是對傳輸延遲的推測-這就好比賭博後續的包馬上就到來。這樣的技巧常常用在改進基准測試的結果上,在現實世界中的工作負載上極少用到。
批量操作可以並且應當在通信協議棧的的多個層實現。例如,排隊子系統("qdisc")就是非常適合進行批量操作;畢竟,排隊就意味著延遲。目前,在最理想的情況下,排隊子系統("qdisc") 編碼部分處理一個包需要六個鎖操作-僅鎖操作就需要48納秒。而對一個包進行排隊處理的全部時間是58-68納秒,可見大量的時間都用在鎖操作上了。賈斯玻已經添加了批量操作,這樣就可以把所消耗的時間分散在對多個包的處理上了,不過,這段代碼實只在對包進行排隊的情況下才運行。
排隊子系統("qdisc")代碼名義上的最快運行路徑只有在不進行排隊的情況下才運行;此時,這些包通常直接傳送給網絡接口,根本就不需要進行排隊處理。不過目前,處理這些包依然需要完完整整地進行6次鎖操作。他說,改進這樣的處理過程是可能的。無鎖的排隊子系統幾乎不需要花費時間對包進行排隊處理,賈斯玻對現有的實現進行了測試,確定了哪些地方可以優化,他說,至少剔除48納秒的鎖操作就值得一試。
他說,傳輸的性能現在看起來已經夠好了,不過接收的處理過程仍然可以進行改進。一個調整的非常好的接收系統每秒可接收的最大包數目大約是六百五十萬個-不過這種情況只在接收到包後即刻就丟棄的情況下才發生。優化接收過程的一些工作一直在進行著,最大處理速度可提升到每秒處理九百多萬個數據包。然而,對優化後的基准測試也暴露出了問題,它沒有給出與內存管理子系統交互所花費的時間。
內存管理
有證據表明與內存管理系統的交互很花時間。網絡通信棧的接收過程似乎采用了這樣的行為:使用slab分配器時其性能不是最佳。接收代碼每次可給高達64個包進行內存空間分配,而傳輸過程的批處理操作中可釋放內存的包數目達256個。這樣的模式似乎特意把SLUB分配器用到了相對處理較慢的代碼段上了。賈斯玻對其進行了一些小型基准測試,他發現在kmam_cache_free()後只有調用kemem_cache_alloc()一次需要大約19納秒。然而,當完成256次分配和釋放時,所需要的時間就會增加到40納秒。實際的網絡通信中,內存分配和釋放是與其他操作同時進行的,這樣內存分配和釋放所花費的時間就會更多,高達77納秒-超過預計分配給它的時間。
因此,賈斯玻得出這樣的結論:要麼必須對內存管理代碼部分進行改進,要麼必須通過某鐘方式完全繞過這段代碼。為了看看後一種方法是否可行,他實現了qmempool子系統;這個子系統可以以無鎖的方式批量進行內存分配和釋放。通過使用qmempool,他在簡單測試中節省了12納秒,而在包轉發測試中則節省高達40納秒。qmempool為了使自身運行更快采用了許多技術,然而殺手級技術是批量處理操作。
賈斯玻平靜地說:qmempool的實現是一種激勵。他想去表明哪些地方可以進行修改,同時鼓勵內存管理系統的開發人員在這方面做些工作。內存管理團隊的回應將在下一次談話中介紹,我們將單獨對其進行報導。
原文:http://www.oschina.net/translate/improving-linux-network-performance