導語 | 本文介紹了部分高效能網路方案,包括RDMA、HARP、io_uring等。從技術原理、落地可行性等方面,簡要地做出分析,希望能對此方面感興趣的開發者提供一些經驗和幫助。
一、背景
業務中經常會有這樣的場景:
隨著網路卡速率的提升(10G/25G/100G),以及部分業務對低延遲的極致追求(1ms/50us),目前的核心協議棧由於協議複雜、流程複雜、設計陳舊等因素,已經逐漸成為業務瓶頸。
業界已經有部分RDMA、DPDK的實踐,但是對於大多數開發者而言,依然比較陌生。
那麼這些方案各自的場景究竟怎樣?是否能夠為更多的業務賦能?以下是階段性簡要總結。
二、RDMA
(一)原理簡介
相對於傳統的網路協議棧,RDMA提供的關鍵特性即為:Kernel Bypass,也即利用專用的NIC(網路卡)進行硬體層面的協議傳輸、編解碼(Offload),通過記憶體對映技術直接與使用者態程式互動,從而避免了複雜低效的核心中介。
基於這種設計,隨之提供幾個額外的重要特性:
- Zero-Copy:基於DMA操作,通訊全程沒有額外的CPU介入拷貝,從而降低CPU消耗。
- 穩定低延遲:由於硬體通路的可靠性,從而保證了穩定的通訊延遲。
- 多種傳輸模式:RC、RD、UC、UD等。基於不同業務的不同可靠性和效能需求,提供類似TCP/UDP的多種傳輸模式。
由於RDMA定位為高效能網路傳輸,同時也為了簡化硬體的設計,一般來說,RDMA會避免如軟體TCP那樣複雜的可靠性設計,而是極其依賴底層傳輸網路的可靠性。
根據不同的傳輸網路,RDMA的具體實現分為幾類:
另外補充說明:
- 雖然RoCE v1/2依賴融合融合乙太網,也即無損傳輸,不過也有部分廠商的優化實現,可以減輕對無損傳輸的依賴。
- Linux kernel 4.9+中,實現了Soft-RoCE,也即軟體版本的RoCE v2,主要用於測試、學習。
(二)RoCE v2 v.s. iWARP
在乙太網環境,主要可選項為RoCE v2和iWARP,相關對比如下:
目前來看,目前的機房網路建設中,對RoCE v2的支援更好,而iWARP卻仍然處於相對空白的狀態。
為此,當前的調研主要針對RoCE v2,而iWARP仍然有待探索。
(三)業務落地
後臺業務主流協議仍然是TCP,具有執行穩定、除錯工具豐富等優勢。不過對於少數期望高效能的業務,RDMA也是值得考慮的。
業務使用RDMA主要面臨兩方面的困難:
- RoCE v2無損網路的要求導致難以跨機房傳輸,當前騰訊機房的支援為module內傳輸(如5跳之內)。
- 全新的開發介面如libverbs、UCX等,業務軟體需要進行適配。
而有些儲存業務依賴多副本,網路傳輸需要能夠跨越MAN,甚至跨城市傳輸。這直接導致RoCE v2難以落地。
三、io_uring/socket
(一)原理簡介
io_uring是Linux 5.1+中支援的非同步IO框架,其核心優勢有:
- 真正的非同步化設計(Proactor),而非如epoll等本質上的同步行為(Reactor)。而其關鍵在於,程式和kernel通過SQ/CQ兩個佇列進行解耦。
- 統一的非同步IO框架,不僅支援儲存、網路。由於良好的擴充套件性,甚至可以支援任何的系統呼叫,如openat、stat等。
如前述,一個io_uring的例項,會建立一對核心和使用者程式共享的佇列,也即提交佇列SQ和完成佇列CQ,兩者皆為SPSC範型: - SQ:使用者態執行緒生產,然後系統呼叫(io_uring_enter)通知核心(io_wq kernel thread)消費。其中元素稱為SQE。
- CQ:核心生產,然後通知(若使用者程式睡眠等待則喚醒)使用者態消費。其中元素稱為CQE。
這其實是最常規也是最經典的非同步模型,在眾多非同步設計中可見。
一般情況下,CQE和SQE一一對應,不過io_uring支援multi-shot模式後則不一定如此。
另外,io_uring支援批量生產和消費,也即連續生產多個SQ後,一次性通知核心,或者持續消費CQ直到其空。
為了進一步優化部分場景的效能,io_uring支援眾多的高階特性:
- File Registration:在反覆操作同一個fd時,加速其查詢對映。
- Buffer Registration:在read/write等反覆需要在核心和使用者程式交換資料的場景,可以重複利用預註冊的一批記憶體。
- Automatic Buffer Selection:為Proactor read預註冊一批記憶體,在就緒後核心自動選擇其中一塊存放資料,從而減少記憶體分配釋放,也節約記憶體資源。
- SQ Polling:使核心(io_wq)輪詢SQ指定時間才睡眠,從而減少通知的系統呼叫。
- IO Polling:開啟子系統(儲存、網路等)的輪詢模式(需要裝置驅動支援),從而加速部分高速裝置。另外可以配合io_uring_enter(flag:IORING_ENTER_GETEVENTS)進行忙等。
- Multi-Shot:一次提交,多次完成,如只要一次提交socket accept,後續連線到來後多次返回。
- io_uring在儲存IO場景,相對之前的阻塞IO、glibc aio、linux aio等,都有不錯的效能提升。
那麼在網路IO場景呢?是否優於epoll等方案呢?
(二)測試資料
經過調研,在知名開源軟體中,暫未發現直接採用io_uring進行網路IO的方案,如seastar/nginx等都沒有官方支援,既然可借鑑較少,那麼就自行測試。
由於io_uring還處於完善階段,而且對於網路IO的支援也有多種方式。目前我們梳理出其中3種:
- Proactor:io_uring直接recv/send。
- Reactor:io_uring接管socket_fd(POLL_ADD)後再recv/send。
- io_uring接管epoll_fd後再epoll_wait再RECV/SEND:路徑繁瑣,推測效能不佳,直接略過。
為此,我們針對前兩種io_uring模型,以及常用的epoll模型,進行測試對比。
為了利用更多的io_uring特性,測試採用當前最新kernel(5.15)。測試模型如下:
- 通訊協議:tcp echo
- 服務模型:單執行緒,非同步併發
- 壓測客戶端:多執行緒,每個執行緒一個連線同步測試
- 資料:包大小為512B
- 測試環境:本機通訊loopback介面
epoll
- io_uring(Proactor)
- io_uring(Reactor)
目前網上的很多程式採用此方式。不過從理論上分析,應該epoll效能接近,故暫未測試。
(三)資料分析
通過對比、分析以上的測試資料,可以得到以下結論:
- io_uring在網路IO方面,並不比epoll效能強大。網路IO的主要瓶頸還是在於核心協議棧的開銷。
io_uring即使開啟核心輪詢,在負載低時可降低延時,而滿載效能提升不明顯,反而浪費了CPU資源。
(四)業務落地
在Linux網路IO場景中,io_uring並不比epoll帶來額外的效能提升。這與儲存IO不同。
不過值得思考的是,如果一個系統中同時存在網路IO和儲存IO,對比以下兩種方式:
- 網路IO採用epoll,儲存IO採用io_uring(可結合eventfd與epoll配合)
- 網路IO、儲存IO都採用io_uring。
從理論上分析,方式2可以依賴io_uring批量提交等優化,從而進一步減少系統呼叫,是否可以帶來效能提升呢?
這部分需要進一步測試分析。
四、總結
以上簡單介紹了RDMA、io_uring/socket等方案,各有優缺點以及場景限制。後續將介紹DPDK的方案,敬請期待。
作者簡介
quintonwang,騰訊後臺開發工程師。