RM新时代网站-首页

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Redis RDMA改造方案分析

OSC開源社區(qū) ? 來源:安第斯智能云 ? 2023-08-16 10:22 ? 次閱讀

01

業(yè)務(wù)適配RDMA類型

RDMA傳輸?shù)倪m配,從業(yè)務(wù)場景的使用角度來看,大致可分為如下幾種類型。

場景一:機器學(xué)習(xí)、分布式存儲等場景,使用社區(qū)成熟的方案,如在機器學(xué)習(xí)場景中使用的NCCL、Tensorflow等框架中都適配了多種傳輸方式(包含tcp、rdma等),塊存儲Ceph中也同時支持tcp及rdma兩種通信模式,這種業(yè)務(wù)場景下業(yè)務(wù)側(cè)更多關(guān)注的是配置及使用,在IAAS基礎(chǔ)設(shè)施側(cè)將RDMA環(huán)境準備好后,使能框架使用rdma的傳輸模式即可。

場景二:業(yè)務(wù)程序使用類似于RPC遠程調(diào)用的通信方式,業(yè)務(wù)側(cè)需要將原有使用的RPC(大部分是GRPC)調(diào)用改為ORPC調(diào)用,在這種場景下業(yè)務(wù)和傳輸更像是兩個獨立的模塊,通過SDK的方式進行調(diào)用,所以適配起來改造的代碼并不多,通常是業(yè)務(wù)層面修改調(diào)用RPC的接口方式。但由于業(yè)務(wù)方可能使用多種編程語言,RPC over RDMA需要進行編程語言進行適配。

場景三:業(yè)務(wù)程序通信是私有化通信,比如使用socket套接字結(jié)合epoll完全自有實現(xiàn)的一套通信機制。這種場景下其實改造也區(qū)分情況,即業(yè)務(wù)IO與網(wǎng)絡(luò)IO是否耦合,若比較解耦,代碼中抽象出一層類似于最新Redis代碼中ConnectionType這樣的架構(gòu)[2],那么只需要實現(xiàn)一套基于RDMA通信且符合Redis ConnectionType接口定義的新傳輸類型即可,改造量相對可控并且架構(gòu)上也比較穩(wěn)定;而若業(yè)務(wù)IO與網(wǎng)絡(luò)IO結(jié)合的較為緊密的情況下,這種場景下往往改造起來會比較復(fù)雜,改造的時候需要抽絲剝繭的找出業(yè)務(wù)與網(wǎng)絡(luò)之間的邊界,再進行網(wǎng)絡(luò)部分的改造。

02

Redis RDMA改造方案分析

首先,以Redis改造為RDMA傳輸為例,分析基于RDMA傳輸?shù)膽?yīng)用程序改造邏輯與流程。

第一步是需要梳理出來Redis中與網(wǎng)絡(luò)傳輸相關(guān)的邏輯,這部分有比較多的參考資料,這里簡單總結(jié)一下。

07ec4c1e-3b5d-11ee-9e74-dac502259ad0.png

Redis中實現(xiàn)了一套Reactor模式的事件處理邏輯名為AE,其主要流程為:

1、使用epoll等機制監(jiān)聽各文件句柄,包括新建連接、以及已建立的連接等;

2、根據(jù)事件的不同調(diào)用對應(yīng)的事件回調(diào)處理;

3、循環(huán)進行epoll loop并進行處理。

參考[2]中分析了當(dāng)前redis的連接管理是圍繞connection這個對象進行管理(可類比socket套接字的管理),抽象一層高于socket的connection layer,以便兼容不同的傳輸層,各個字段解釋如下。

07f9c9a2-3b5d-11ee-9e74-dac502259ad0.png

type:各種連接類型的回調(diào)接口,定義了諸如事件回調(diào)、listen、accept、read、write等接口,類比tcp socket實現(xiàn)的proto_ops。

state:當(dāng)前連接的狀態(tài),如CONNECTING/ACCEPTING/CONNECTED/CLOSED等狀態(tài),類比TCP的狀態(tài)管理。

fd:連接對應(yīng)的文件句柄。

iovcnt:進行iov操作的最大值。

private_data:保存私有數(shù)據(jù),當(dāng)前存放的是redis中client的指針。

conn_handler/write_handler/read_handler:分別對應(yīng)連接connect、write、read時的處理接口。

0807dc5e-3b5d-11ee-9e74-dac502259ad0.png

get_type: connection的連接類型,當(dāng)前redis已支持tcp、unix、tls類型,返回字符串。

init:在每種網(wǎng)絡(luò)連接模塊注冊時調(diào)用,各模塊私有初始化,如tcp、unix類型當(dāng)前未實現(xiàn),tls注冊時做了一些ssl初始化的前置工作。

ae_handler: redis中的網(wǎng)絡(luò)事件處理回調(diào)函數(shù),redis中使用aeCreateFileEvent為某個fd及事件注冊處理函數(shù)為ae_handler,當(dāng)redis的主循環(huán)aeMain中發(fā)現(xiàn)有響應(yīng)的事件時會調(diào)用ae_handler進行處理,如在tcp連接類型中ae_handler為connSocketEventHandler,該函數(shù)分別處理了鏈接建立、鏈接可讀、鏈接可寫三種事件。

listen: 監(jiān)聽于某個IP地址和端口,在tcp連接類型中對應(yīng)的函數(shù)為connSocketListen,該函數(shù)主要調(diào)用bind、listen。

accept_handler: redis作為一個服務(wù)端,當(dāng)接收到客戶端新建連接的請求時候的處理函數(shù),一般會被.accept函數(shù)調(diào)用,比如在tcp連接類型中,connSocketAccept調(diào)用accept_handler,該方法被注冊為connSocketAcceptHandler,主要是使用accept函數(shù)接收客戶端請求,并調(diào)用acceptCommonHandler創(chuàng)建client。

addr: 返回連接的地址信息,主要用于一些連接信息的debug日志。

is_local:返回連接是否為本地連接,redis在protected模式下時,調(diào)用該接口判斷是否為本地連接進行校驗。

conn_create/conn_create_accepted:創(chuàng)建connection,對于tcp連接類型,主要是申請connection的內(nèi)存,以及connection初始化工作。

shutdown/close:釋放connection的資源,關(guān)閉連接,當(dāng)某個redis客戶端移除時調(diào)用。

connect/blocking_connect:實現(xiàn)connection的非阻塞和阻塞連接方法,在tcp連接類型中,非阻塞連接調(diào)用aeCreateFileEvent注冊連接的可寫事件,繼而由后續(xù)的ae_handler進行處理,實現(xiàn)非阻塞的連接;而阻塞連接則在實現(xiàn)時會等待連接建立完成。

accept:該方法在redis源碼中有明確的定義,可直接調(diào)用上述accept_handler,tcp連接類型中,該方法被注冊為connScoketAccept。

write/writev/read:和linux下系統(tǒng)調(diào)用write、writev、read行為一致,將數(shù)據(jù)發(fā)送至connection中,或者從connection中讀取數(shù)據(jù)至相應(yīng)緩沖區(qū)。

set_write_handler:注冊一個寫處理函數(shù),tcp連接類型中,該方法會注冊connection可寫事件,回調(diào)函數(shù)為tcp的ae_handler。

set_read_handler:注冊一個讀處理函數(shù),tcp連接類型中,該方法會注冊connection可讀事件,回調(diào)函數(shù)為tcp的ae_handler。

sync_write/sync_read/sync_readline:同步讀寫接口,在tcp連接類型中實現(xiàn)邏輯是使用循環(huán)讀寫。

has_pending_data:檢查connection中是否有尚未處理的數(shù)據(jù),tcp連接類型中該方法未實現(xiàn),tls連接類型中該方法被注冊為tlsHasPendingData,tls在處理connection讀事件時,會調(diào)用SSL_read讀取數(shù)據(jù),但無法保證數(shù)據(jù)已經(jīng)讀取完成[3],所以在tlsHasPendingData函數(shù)中使用SSL_pending檢查緩沖區(qū)是否有未處理數(shù)據(jù),若有的話則交由下面的process_pending_data進行處理。has_pending_data方法主要在事件主循環(huán)beforesleep中調(diào)用,當(dāng)有pending data時,事件主循環(huán)時不進行wait,以便快速進行下一次的循環(huán)處理。

process_pending_data:處理檢查connection中是否有尚未處理的數(shù)據(jù),tcp連接類型中該方法未實現(xiàn),tls連接類型中該方法被注冊為tlsProcessPendingData,主要是對ssl緩沖區(qū)里面的數(shù)據(jù)進行讀取。process_pending_data方法主要在事件主循環(huán)beforesleep中調(diào)用。

get_peer_cert:TLS連接特殊方法。

結(jié)合當(dāng)前代碼中tcp及tls實現(xiàn)方法,梳理出和redis connection網(wǎng)絡(luò)傳輸相關(guān)的流程:

0859d518-3b5d-11ee-9e74-dac502259ad0.png

圖:Redis Connection Call Graph

對于redis來說新增一個RDMA方式的傳輸方式,即是要將connection中的各種方法按照上述定義去使用RDMA編程接口去實現(xiàn)。RDMA編程一般采用CM管理連接加Verbs數(shù)據(jù)收發(fā)的模式,客戶端與服務(wù)端的交互邏輯大致如下圖所示,參考[16]。

08652d8c-3b5d-11ee-9e74-dac502259ad0.png

圖:RDMA C/S Workflow

字節(jié)跳動的pizhenwei同學(xué)目前在redis社區(qū)中已經(jīng)提交了redis over rdma的PR,參見[4],具體的代碼均在rdma.c這一個文件中。由于RDMA在做遠程內(nèi)存訪問時,需要使用對端的內(nèi)存地址,所以作者實現(xiàn)了一套RDMA客戶端與服務(wù)端的交互機制,用于通告對端進行遠程內(nèi)存寫入的內(nèi)存地址,參見[5]。

08742d46-3b5d-11ee-9e74-dac502259ad0.png

交互邏輯及說明如下:

1、增加了RedisRdmaCmd,用于Redis客戶端與服務(wù)端的控制面交互,如特性交換、Keepalive、內(nèi)存地址交換等;

2、在客戶端及服務(wù)端建立完成RDMA連接后,需要先進行控制面的交互,當(dāng)內(nèi)存地址交換完成后,方可以進行Redis實際數(shù)據(jù)的交互及處理;

3、控制面消息通過IBV_WR_SEND方式發(fā)送,Redis數(shù)據(jù)交互通過IBV_WR_RDMA_WRITE_WITH_IMM發(fā)送,通過方法的不同來區(qū)分是控制面消息還是Redis的實際數(shù)據(jù);

08830122-3b5d-11ee-9e74-dac502259ad0.png

4、客戶端及服務(wù)端共享了一片內(nèi)存,則需要對內(nèi)存的使用管理,目前有三個變量用戶協(xié)同讀寫雙方的內(nèi)存使用。

tx.offset為RDMA發(fā)送側(cè)已經(jīng)對內(nèi)存寫入的偏移地址,從發(fā)送端角度看內(nèi)存已經(jīng)使用到了tx.offset位置,下次發(fā)送端再進行RDMA寫入時,內(nèi)存地址只能為tx.offset + 1;

rx.offset為RDMA接收側(cè)已經(jīng)收到的內(nèi)存偏移地址,雖然數(shù)據(jù)可能實際上已經(jīng)到了tx.offset的位置,但由于接收側(cè)需要去處理CQ的事件,才能獲取到當(dāng)前數(shù)據(jù)的位置,rx.offset是通過IMM中的立即數(shù)進行傳遞的,發(fā)送側(cè)每次寫入數(shù)據(jù)時,會將數(shù)據(jù)長度,所以rx.offset <= tx.offset;

rx.pos 為接收方上層業(yè)務(wù)內(nèi)存的偏移地址,rx.pos <= rx.offset。

5、當(dāng)rx.pos等于memory.len時,說明接收側(cè)內(nèi)存已滿,通過內(nèi)存地址交換這個RedisRdmaCmd進行控制面交互,將tx.offset、rx.offset、rx.pos同時置零,重新對這片共享內(nèi)存協(xié)同讀寫。

Connection各方法的主要實現(xiàn)邏輯及分析如下:

listen:主要涉及RDMA編程圖示中l(wèi)isten、bind的流程,結(jié)合redis的.init相關(guān)調(diào)用流程,會將cm_channel中的fd返回給網(wǎng)絡(luò)框架AE,當(dāng)后續(xù)客戶端連接該fd時,由AE進行事件回調(diào),即后續(xù)的accepHandler。

accept_handler:該函數(shù)作為上述listen fd的事件回調(diào)函數(shù),會處理客戶端的連接事件,主要調(diào)用.accept方法進行接收請求,并使用acceptCommonHandler調(diào)用后續(xù)的.set_read_handler注冊已連接的讀事件,參見圖Redis Connection Call Graph。

accept:要涉及RDMA編程圖示中accept的流程,處理RDMA_CM_EVENT_CONNECT_REQUEST、RDMA_CM_EVENT_ESTABLISHED等cm event,并進行cm event的ack。

set_read_handler:設(shè)置連接可讀事件的回調(diào)為.ae_handler。

read_handler:實際處理中會被設(shè)置為readQueryFromClient。

read:從本地緩沖區(qū)中讀取數(shù)據(jù),該數(shù)據(jù)是客戶端通過遠程DMA能力寫入。

set_write_handler:將write_handler設(shè)置為回調(diào)處理函數(shù),這里和tcp、tls實現(xiàn)的方式有所區(qū)別,并沒有注冊connection的可寫事件回調(diào),是因為RDMA中不會觸發(fā)POLLOUT(可寫)事件,connection的寫由ae_handler實現(xiàn)。

write_handler:實際工作中被設(shè)置為sendReplyToClient。

write:將Redis的數(shù)據(jù)拷貝到RMDA的本地緩沖區(qū)中,通過ibv_post_send,這部分數(shù)據(jù)會通過遠程DMA能力寫入對端。

has_pending_data:檢查內(nèi)部的pending_list,在收到RDMA_CM_EVENT_DISCONNECTED等事件時,會將當(dāng)前connection加入到pending_list中,由后續(xù)beforeSleep時調(diào)用process_pending_data進行處理。

process_pending_data:檢查pending的connection,并調(diào)用read_handler讀取connection中的數(shù)據(jù)。

ae_handler:該方法有三個處理流程,第一是處理RDMA CQ事件,包括接收處理RedisRdmaCmd控制面消息,接收RDMA IMM類事件增加rx.offset;第二是調(diào)用read_handler和write_handler,這部分是與tcp、tls流程一致;第三是檢查rx.pos和rx.offset的值,若rx.pos == memory.len時,發(fā)送內(nèi)存地址交換這個RedisRdmaCmd控制面消息。

03

Redis RDMA測試

Redis測試通常采取自帶的redis-benchmark工具進行測試,該工具復(fù)用了redis中的ae處理邏輯,并調(diào)用hiredis進行redis數(shù)據(jù)的解析,在參考[6]中fork并改造了一份基于RDMA的redis-benchmark,可直接編譯使用,接下來使用該工具進行tcp及RDMA方式的性能測試對比。

在實際測試中使用的是同一個交換機下的兩臺服務(wù)器,傳輸方式是rocev2,經(jīng)過qperf的測試,tcp的latency為12us,rocev2的latency為4us。

3.1 單并發(fā)單線程

TCP方式

RedisServer:./src/redis-server --protected-mode no

RedisBenchmark:./src/redis-benchmark -h xx.xx.xx.xx -p 6379 -c 1 -n 500000 -t get

089447d4-3b5d-11ee-9e74-dac502259ad0.png

RDMA方式

RedisServer:./src/redis-server --loadmodule src/redis-rdma.so port=6379 bind=xx.xx.xx.xx --protected-mode no

RedisBenchmark:./src/redis-benchmark -h xx.xx.xx.xx -p 6379 -c 1 -n 500000 -t get --rdma

089ec2a4-3b5d-11ee-9e74-dac502259ad0.png

3.2多并發(fā)多線程

Redisbenchmark單線程4連接:

08aca3ba-3b5d-11ee-9e74-dac502259ad0.png

Redisbenchmark單線程8連接:

08bd7d0c-3b5d-11ee-9e74-dac502259ad0.png

Redisbenchmark單線程16/32連接:

08d3b7b6-3b5d-11ee-9e74-dac502259ad0.png

注:在我們的測試環(huán)境中16個連接時,redis-benchmark已經(jīng)100%,再進行增加連接數(shù)測試時,qps也不會再增加。

Redisbenchmark 4線程4連接:

08d8a67c-3b5d-11ee-9e74-dac502259ad0.png

Redisbenchmark 4線程16連接:

08e12950-3b5d-11ee-9e74-dac502259ad0.png

Redisbenchmark 4線程32/64連接:

08f1bf86-3b5d-11ee-9e74-dac502259ad0.png

注:在我們的測試環(huán)境中4線程32連接時,redis-server已經(jīng)100%,再進行增加連接數(shù)測試時,qps也不會再增加。

更多的連接和線程:

0905362e-3b5d-11ee-9e74-dac502259ad0.png

3.3測試總結(jié)

091cb132-3b5d-11ee-9e74-dac502259ad0.png

整體而言,在我們的測試環(huán)境下,redis服務(wù)能力rocev2(rdma)的傳輸方式相較tcp,有~50% 到 ~100%左右的能力提升。

可以發(fā)現(xiàn),由于rdma bypass了內(nèi)核協(xié)議棧,相同物理拓撲下redis一次讀取時延下降了16us左右(見3.1單并發(fā)測試數(shù)據(jù)),這里額外做了一個測試,選取了另外一組相隔較遠的機器進行測試,發(fā)現(xiàn)讀取時延仍然縮小的是這個數(shù)量級,見下圖。

092ee21c-3b5d-11ee-9e74-dac502259ad0.png

rdma方式建鏈的時間較長,實際測試中連接數(shù)越多,redis-benchmark真正開始測試的時間越長。

04

開源程序基于RDMA方案

4.1Tensorflow RDMA

Tensorflow是一個廣泛使用的深度學(xué)習(xí)框架,在Tensorflow中數(shù)據(jù)通常表示為Tensor張量,Tensor是一個多為數(shù)據(jù),可以在不同的設(shè)備之間進行傳輸,以便進行分布式計算。

在分布式系統(tǒng)中,Tensorflow可以通過網(wǎng)絡(luò)傳輸將Tensor從一個節(jié)點傳輸?shù)搅硪粋€節(jié)點,從1.1版本開始支持RDMA傳輸,以下為其基于RDMA傳輸?shù)闹饕桨?,參考[7][8]。

在RDMA傳輸通道建立之前,使用基于tcp的grpc通道傳輸傳遞RDMA的內(nèi)存地址、MR key、服務(wù)地址等信息

內(nèi)存拷貝方案:

a)對于可以DMA的Tensor(包括CPU上的內(nèi)存或者GPU Direct的內(nèi)存),采用直接從源Tensor寫到目標Tensor中的方案,實現(xiàn)內(nèi)存零拷貝

b)對于非DMA得Tensor,用protobuf序列化后,通過RDMA方式寫到接收端預(yù)先注冊的內(nèi)存中

c)對于不支持GPU Direct的Tensor,通過RDMA方式寫到接收端的CPU內(nèi)存,再在接收端通過拷貝的方式到GPU中,發(fā)送與接收CPU之間不存在內(nèi)存拷貝

內(nèi)部使用RdmaBuffer用于RDMA讀寫的內(nèi)存單元,RdmaBuffer有三個派生類,分別是RdmaAckBuffer、RdmaMessageBuffer和RdmaTensorBuffer,RdmaMessageBuffer負責(zé)發(fā)送 message ,比如請求一個tensor等等。一旦一個message被發(fā)送,message的接收方需要通過RdmaAckBuffer發(fā)送一個ack來釋放發(fā)送方的message buffer。一個RdmaAckBuffer和唯一的RdmaMessageBuffer綁定。RdmaTensorBuffer負責(zé)發(fā)送tensor,tensor的接收方需要返回一個message來釋放發(fā)送方的buffer

對于一個具體的recv和send流程如下:

0940ca9a-3b5d-11ee-9e74-dac502259ad0.png

a)接收側(cè)發(fā)送RDMA_MESSAGE_TENSOR_REQUEST消息,其中包含目的Tensor的地址,以用于發(fā)送側(cè)進行RDMA寫入。

b)為避免在每個步驟中發(fā)送額外的元數(shù)據(jù)消息,為每個Tensor維護一個本地元數(shù)據(jù)緩存,僅在更改時才會更新,每個RDMA_MESSAGE_TENSOR_REQUEST將包含接收方從本地緩存中獲取的元數(shù)據(jù)。發(fā)送方將比較消息中的元數(shù)據(jù)和Tensor的新元數(shù)據(jù),如果元數(shù)據(jù)更改,發(fā)送側(cè)發(fā)送包含新元數(shù)據(jù)的RDMA_MESSAGE_META_DATA_RESPONSE。

c)當(dāng)接收方收到 RDMA_MESSAGE_META_DATA_RESPONSE 時,將更新本地元數(shù)據(jù)緩存,重新分配結(jié)果/代理Tensor,重新發(fā)送Tensor請求。為了可追溯性,新的消息具有不同的名稱RDMA_MESSAGE_TENSOR_RE_REQUEST。

d)當(dāng)發(fā)送方收到 RDMA_MESSAGE_TENSOR_RE_REQUEST 時,它將使用消息中指定的請求索引定位相關(guān)的 RdmaTensorResponse,并調(diào)用其 Resume方法,該方法將 RDMA 寫入之前克隆的Tensor的內(nèi)容,到重新請求中指定的新遠程地址。

e)當(dāng)接收方接收到 RDMA 寫入時,它將使用立即值作為請求索引,找到相關(guān)的 RdmaTensorRequest,然后調(diào)用其 RecvTensorContent方法,包含可能存在的內(nèi)存復(fù)制、反序列化等工作。

4.2BrpcRDMA

百度的brpc當(dāng)前的RDMA傳輸實現(xiàn)中,數(shù)據(jù)傳輸是使用RMDA_SEND_WITH_IMM進行操作,這就要求接收端在接收數(shù)據(jù)前要先準備好內(nèi)存并預(yù)先POST RECV。為了實現(xiàn)高效的內(nèi)存管理,brpc內(nèi)部實現(xiàn)了靜態(tài)內(nèi)存池,且在RDMA數(shù)據(jù)傳輸實現(xiàn)中做了如下幾點優(yōu)化,參考[9][10]。

數(shù)據(jù)傳輸零拷貝,要發(fā)送的所有數(shù)據(jù)默認都存放在IOBuf的Block中,因此所發(fā)送的Block需要等到對端確認接收完成后才可以釋放,這些Block的引用被存放于RdmaEndpoint::_sbuf中。而要實現(xiàn)接收零拷貝,則需要確保接受端所預(yù)提交的接收緩沖區(qū)必須直接在IOBuf的Block里面,被存放于RdmaEndpoint::_rbuf。注意,接收端預(yù)提交的每一段Block,有一個固定的大小(recv_block_size)。發(fā)送端發(fā)送時,一個請求最多只能有這么大,否則接收端則無法成功接收。

數(shù)據(jù)傳輸有滑動窗口流控,這一流控機制是為了避免發(fā)送端持續(xù)在發(fā)送,其速度超過了接收端處理的速度。TCP傳輸中也有類似的邏輯,但是是由內(nèi)核協(xié)議棧來實現(xiàn)的,brpc內(nèi)實現(xiàn)了這一流控機制,通過接收端顯式回復(fù)ACK來確認接收端處理完畢。為了減少ACK本身的開銷,讓ACK以立即數(shù)形式返回,可以被附在數(shù)據(jù)消息里。

數(shù)據(jù)傳輸邏輯的第三個重要特性是事件聚合。每個消息的大小被限定在一個recv_block_size,默認為8KB。如果每個消息都觸發(fā)事件進行處理,會導(dǎo)致性能退化嚴重,甚至不如TCP傳輸(TCP擁有GSO、GRO等諸多優(yōu)化)。因此,brpc綜合考慮數(shù)據(jù)大小、窗口與ACK的情況,對每個發(fā)送消息選擇性設(shè)置solicited標志,來控制是否在發(fā)送端觸發(fā)事件通知。

4.3NCCLRDMA

NCCL的網(wǎng)絡(luò)傳輸實現(xiàn)是插件式的,各種不同的網(wǎng)絡(luò)傳輸只需要按照ncclNet中定義的方法去具體實現(xiàn)即可。

其中最主要的是isend、irecv及test方法,在調(diào)用 isend 或 irecv 之前,NCCL 將在所有緩沖區(qū)上調(diào)用 regMr 函數(shù),以便 RDMA NIC 準備緩沖區(qū),deregMr 將用于注銷緩沖區(qū)。

以下是NCCL RDMA的實現(xiàn)部分邏輯,基于當(dāng)前NCCL最新版本分析,主要參考[11]及參考[12]

(當(dāng)前實現(xiàn)與參考中略有不同)。

在NCCL基于RDMA的傳輸實現(xiàn)中,目前的數(shù)據(jù)傳輸主要是通過RDMA_WRITE操作

由于發(fā)送端進行RDMA_WRITE時,需要預(yù)先知道對端的DMA地址,NCCL中發(fā)送/接收端是通過一個緩沖區(qū)ncclIbSendFifo進行交互

ncclIbSendFifo是發(fā)送端申請的一塊內(nèi)存緩沖區(qū),在connect與accept階段通過傳統(tǒng)tcp socket的方式攜帶給接收端

在接收端異步進行接收時,recvProxyProgress調(diào)用irecv接口進行接收,在RDMA的實現(xiàn)中對應(yīng)的是將本端DMA的地址通過ncclIbSendFifo RDMA_WRITE至發(fā)送端

發(fā)送端進行發(fā)送時,sendProxyProgress調(diào)用isend接口進行發(fā)送,在RDMA中對應(yīng)的是從ncclIbSendFifo中獲取接收端的DMA地址,將上層的data直接RDMA_WRITE至接收端的DMA地址中

095bb832-3b5d-11ee-9e74-dac502259ad0.png

接收端維護本地的remFifoTail游標,每次接收時游標后移一位,接收端會將idx設(shè)置為一個自增的索引,同時將上層的DMA地址通過ncclIbSendFifo攜帶給發(fā)送端

發(fā)送端維護本地的fifoHead游標,每次發(fā)送后游標后移一位,發(fā)送端檢查fifo中元素的idx值是否為預(yù)期索引來判斷該fifo是否已經(jīng)被接收端設(shè)置過,即接收端的DMA地址已經(jīng)可以寫入

struct ncclIbSendFifo {
  uint64_t addr;
  int      size;
  uint32_t rkey;
  uint32_t nreqs;
  uint32_t tag;
  uint64_t idx;
};


// 發(fā)送端
ncclIbIsend:
uint64_t idx = comm->fifoHead+1;
if (slots[0].idx != idx) { *request = NULL; return ncclSuccess; }
comm->fifoHead++;


//接收端
ncclIbIrecv -> ncclIbPostFifo :
localElem[i].idx = comm->remFifo.fifoTail+1;
comm->remFifo.fifoTail++;

4.4Libvma及SMC-R方式

除了上述修改業(yè)務(wù)源碼的方案,業(yè)內(nèi)也有“零入侵”業(yè)務(wù)程序的方案,比如libvma及smc-r方式。

SMC-R:

smc-r(SMC over RDMA)是IBM在2017提交至linux kernel的一種兼容socket層,使用共享內(nèi)存技術(shù)、基于RDMA技術(shù)實現(xiàn)的高性能內(nèi)核網(wǎng)絡(luò)協(xié)議棧。smc-r的主要實現(xiàn)是在內(nèi)核態(tài)實現(xiàn)了一個新的af_smc協(xié)議族,基于RDMA verbs接口實現(xiàn)內(nèi)核proto_ops中的各方法。

0960c304-3b5d-11ee-9e74-dac502259ad0.png

smc-r支持fallback回退機制,在通信雙方最開始建立連接時是使用tcp握手(特定的tcp選項)進行協(xié)商是否雙方均支持SMC-R能力,當(dāng)協(xié)商不成功時fallback為原始的tcp通信。完成協(xié)議協(xié)商并建立連接后,協(xié)議棧為SMC-R socket分配一塊用于緩存待發(fā)送數(shù)據(jù)的環(huán)形緩沖區(qū)sndbuf和一塊用于緩存待接收數(shù)據(jù)的環(huán)形緩沖區(qū)RMB(Remote Memory Buffer)。

發(fā)送端應(yīng)用程序通過socket接口將待發(fā)送數(shù)據(jù)拷貝到本側(cè)sndbuf中,由SMC-R協(xié)議棧通過RDMA WRITE操作直接高效地寫入對側(cè)節(jié)點的RMB中。同時伴隨著使用RDMA SEND/RECV操作交互連接數(shù)據(jù)管理消息,用于更新、同步環(huán)形緩沖區(qū)中的數(shù)據(jù)游標。

接收端SMC-R協(xié)議棧感知到RMB中填入新數(shù)據(jù)后,通過epoll等方式告知接收端應(yīng)用程序?qū)MB中的數(shù)據(jù)拷貝到用戶態(tài),完成數(shù)據(jù)傳輸。所以在SMC-R中,RMB充當(dāng)傳輸過程中的共享內(nèi)存。

096d4912-3b5d-11ee-9e74-dac502259ad0.png

圖 smc-r發(fā)送接收(轉(zhuǎn)自阿里云)

下面是一個基于smc-r通信的實際測試場景的協(xié)商交互抓包:

09853874-3b5d-11ee-9e74-dac502259ad0.png

Libvma:

Libvma是Mellanox公司開源的一款高性能的用戶態(tài)網(wǎng)絡(luò)協(xié)議棧,它將socket的相關(guān)接口全部在用戶態(tài)空間實現(xiàn),實現(xiàn)對內(nèi)核的旁路,使用RDMA verbs接口直接調(diào)用網(wǎng)卡驅(qū)動,從而節(jié)省了大量的上下文數(shù)據(jù)拷貝,節(jié)省了 CPU 的資源降低了時延,業(yè)務(wù)在使用libvma時只需要使用LD_PRELOAD libvma.so替換原有的系統(tǒng)調(diào)用即可完成傳輸協(xié)議的替換。

Libvma內(nèi)部在tcp協(xié)議棧的實現(xiàn)上使用了lwip方案,重寫了epoll,使用了hugepage,內(nèi)部使用單獨的線程去輪詢RDMA CQ事件等方案,相較于內(nèi)核協(xié)議棧的實現(xiàn),在主機側(cè)的處理延遲有200%至500%的降低。

此外,在實際測試過程中發(fā)現(xiàn)libvma雖然使用的是RDMA verbs接口,但實際針對Mellanox mlx5系列驅(qū)動的網(wǎng)卡是直接用戶態(tài)驅(qū)動網(wǎng)卡,發(fā)送的仍然是原始基于tcp的以太報文,并不是rocev2的報文,具體討論可以見github上的issue參考[15]。

下面是基于libvma測試redis的場景,由于libvma bypass協(xié)議棧,并且重寫了epoll等其它特性,性能提升大概3倍:

09a4998a-3b5d-11ee-9e74-dac502259ad0.png

總結(jié):

相較于業(yè)務(wù)使用raw verbs進行源碼修改,libvma及smc-r方式可以提供“零入侵、零修改”源碼的優(yōu)勢,但由于應(yīng)用程序在將數(shù)據(jù)提供給socket接口時仍然存在一次拷貝,所以性能上對比verbs方案來說有一定的損耗,對于想快速驗證RDMA能力的業(yè)務(wù)是一個不錯的POC驗證方式。

目前阿里云的Alibaba Cloud Linux3默認支持smc-r能力,結(jié)合阿里云的eRDMA能力網(wǎng)卡,可以使業(yè)務(wù)進行透明無損的RDMA傳輸替換,減少cpu的使用率,降低一定的通信延時。但目前該能力在阿里云上屬于公測能力,生產(chǎn)穩(wěn)定性待驗證,參考[14]。

libvma方式?jīng)]有l(wèi)inux社區(qū)的支持,并且更多的是針對Mellanox系列網(wǎng)卡的支持,在工業(yè)界使用的場景也不太多,目前在金融的高頻交易領(lǐng)域有一些使用嘗試。

05

總結(jié)與展望

前面主要分析和調(diào)研了一些開源應(yīng)用在進行業(yè)務(wù)適配RDMA傳輸?shù)姆桨?,整體來看RDMA改造的方案是分為兩部分,分別為通信接口的改造以及RDMA內(nèi)存管理設(shè)計。 通信接口改造主要指將tcp socket的傳輸接口修改為ib verbs或者cm接口,這部分同時涉及到適配現(xiàn)有業(yè)務(wù)網(wǎng)絡(luò)事件的處理模型。 由于RDMA傳輸數(shù)據(jù)時,需要預(yù)先將內(nèi)存注冊到HCA卡上,所以RDMA內(nèi)存管理會比較復(fù)雜,同時也是性能高低與否的關(guān)鍵。

1)數(shù)據(jù)傳輸時申請內(nèi)存,并進行內(nèi)存注冊,再進行RDMA操作。顯然這種模式在代碼實現(xiàn)上最為簡單,但是性能及效率最低,現(xiàn)有方案中很少有在fast path中使用這種內(nèi)存管理方案。

2)提前注冊好一大塊內(nèi)存,在上層業(yè)務(wù)需要發(fā)送數(shù)據(jù)時,將數(shù)據(jù)拷貝至RDMA注冊好的內(nèi)存。這種模式性能相較第一種有提升,但存在一定的內(nèi)存拷貝。

3)使用內(nèi)存池,業(yè)務(wù)及RDMA的內(nèi)存使用同一塊。性能明顯是最優(yōu)的,但是實現(xiàn)邏輯較復(fù)雜,需要管理好內(nèi)存的申請及釋放、某些實現(xiàn)中通信雙方也需要做內(nèi)存使用量的協(xié)商。

結(jié)合前面應(yīng)用的RDMA方案,匯總?cè)缦卤恚?/p>

應(yīng)用名稱 網(wǎng)絡(luò)處理模型 內(nèi)存方案 其他特性
Redis (pr stage) 1.適配原有的單線程reactor非阻塞模式
2.rdma無pollout時間,在業(yè)務(wù)邏輯中額外處理
3.網(wǎng)絡(luò)支持插件式,不同的傳輸模式實現(xiàn)相同的網(wǎng)絡(luò)方法
1.預(yù)注冊內(nèi)存,RDMA Write模式
2.DMA地址通過控制消息交互
3.應(yīng)用與RDMA之間存在拷貝
1.有控制面交互,如xferbuffer
2.控制面信息復(fù)用RDMA通道
Tensorflow 異步發(fā)送、阻塞接收 1.RDMA Write模式
2.應(yīng)用及RDMA共享內(nèi)存池
3.通信雙方通過消息交互DMA地址
1.使用基于TCP的GRPC通道進行RDMA鏈接的協(xié)商
2.有控制面消息交互,如metadata更新
3.控制面信息復(fù)用RDMA通道
BRPC reactor模式 1.內(nèi)存池模式
2.使用RDMA Send模式
1.額外的流控機制
2.事件聚合
NCCL 1.reactor模式
2.網(wǎng)絡(luò)支持插件式,不同的傳輸模式實現(xiàn)相同的網(wǎng)絡(luò)方法
1.RDMA Write模式
2.通信雙方通過一個共享fifo來交互具體的DMA地址
3.DMA地址是預(yù)先注冊的內(nèi)存
1.RDMA建立階段使用TCP鏈接進行協(xié)商

隨著AI的火熱,國產(chǎn)DPU、GPU的高速發(fā)展,數(shù)據(jù)中心內(nèi)在高性能計算、機器學(xué)習(xí)、分布式存儲等場景下的業(yè)務(wù)也需要隨著硬件能力的提升去適配使用這些能力,RDMA因其諸多優(yōu)點目前已經(jīng)廣泛被應(yīng)用。調(diào)研學(xué)習(xí)現(xiàn)有的方案是為了更好地適配及修改自研的業(yè)務(wù),相信隨著越來越多業(yè)務(wù)場景下RDMA的使用,其相關(guān)生態(tài)及應(yīng)用方案也會越來越成熟。






審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 處理器
    +關(guān)注

    關(guān)注

    68

    文章

    19259

    瀏覽量

    229651
  • RPC
    RPC
    +關(guān)注

    關(guān)注

    0

    文章

    111

    瀏覽量

    11529
  • RDMA
    +關(guān)注

    關(guān)注

    0

    文章

    77

    瀏覽量

    8945
  • TCP通信
    +關(guān)注

    關(guān)注

    0

    文章

    146

    瀏覽量

    4221
  • TLS
    TLS
    +關(guān)注

    關(guān)注

    0

    文章

    44

    瀏覽量

    4248

原文標題:RDMA在典型場景下的技術(shù)應(yīng)用分析與探索

文章出處:【微信號:OSC開源社區(qū),微信公眾號:OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    深入了解RDMA技術(shù)

    Explorer,專注于高性能網(wǎng)絡(luò)、虛擬化網(wǎng)絡(luò)及網(wǎng)卡的測試方案研究。熟悉dpdk,rdma,sdn等技術(shù)的應(yīng)用與解決方案。
    的頭像 發(fā)表于 12-26 09:23 ?1772次閱讀
    深入了解<b class='flag-5'>RDMA</b>技術(shù)

    RDMA RNIC虛擬化方案

    遠程直接內(nèi)存訪問(Remote Direct Memory Access,RDMA)技術(shù)允許應(yīng)用程序繞過操作系統(tǒng)內(nèi)核,以零拷貝的方式和遠程計算機進行網(wǎng)絡(luò)通信,具有低延遲和高帶寬的優(yōu)勢。RDMA協(xié)議
    的頭像 發(fā)表于 01-23 17:23 ?1961次閱讀
    <b class='flag-5'>RDMA</b> RNIC虛擬化<b class='flag-5'>方案</b>

    數(shù)字電視改造方案

    請問有誰做過這種改造工程啊?    我也想做這個啊方案基本原理:用30多個機頂盒調(diào)制出30多個頻道的模擬信號,供給100-200個房間都能收看30多個頻道。酒店按
    發(fā)表于 11-05 09:40

    Redis Stream應(yīng)用案例

    的IoT設(shè)備會形成巨大的數(shù)據(jù)洪流,采集完成后在云端進行分析,產(chǎn)生巨大的用戶價值。這些數(shù)據(jù)雖然內(nèi)容各個不同,但是都有一個共同的特點,都是一種時序數(shù)據(jù)??吹竭@里,你可能會突然發(fā)現(xiàn),Redis Stream從
    發(fā)表于 06-26 17:15

    Redis緩存和MySQL數(shù)據(jù)不一致原因和解決方案

    高并發(fā)架構(gòu)系列:Redis緩存和MySQL數(shù)據(jù)一致性方案詳解
    發(fā)表于 03-27 15:55

    請問如何分析、排查、解決Redis變慢問題

    如何分析、排查、解決Redis變慢問題
    發(fā)表于 11-09 08:03

    簡要分析Redis的特性

    淺談Redis的數(shù)據(jù)結(jié)構(gòu)、虛擬內(nèi)存等特性2016-03-28 10:42 在這篇文章中,我們將談?wù)?Redis(REmote DIctionary Server)。Redis是一個開源的、內(nèi)存式
    發(fā)表于 10-11 15:21 ?0次下載
    簡要<b class='flag-5'>分析</b><b class='flag-5'>Redis</b>的特性

    redis和mongodb數(shù)據(jù)庫對比_redis、memcache、mongoDB 對比

    本文是對redis和mongodb數(shù)據(jù)庫對比分析。以及redis、memcache、mongoDB 區(qū)別對比。MongoDB和Redis都是NoSQL,采用結(jié)構(gòu)型數(shù)據(jù)存儲。二者在使用場
    發(fā)表于 02-07 08:45 ?4253次閱讀
    <b class='flag-5'>redis</b>和mongodb數(shù)據(jù)庫對比_<b class='flag-5'>redis</b>、memcache、mongoDB 對比

    技術(shù)盛宴 | 淺析RDMA網(wǎng)絡(luò)下MMU水線設(shè)置

    )水線是保證RDMA網(wǎng)絡(luò)無損和低延時的關(guān)鍵。本文將以RDMA網(wǎng)絡(luò)作為切入點,結(jié)合實際部署經(jīng)驗,分析MMU水線設(shè)置的一
    發(fā)表于 11-22 12:44 ?1145次閱讀

    redis工作原理

    一頭霧水。 很多時候,Redis出現(xiàn)訪問延遲變大,都與我們的使用不當(dāng)或運維不合理導(dǎo)致的。 這篇文章我們就來分析一下Redis在使用過程中,經(jīng)常會遇到的延遲問題以及如何定位和分析。 使用
    的頭像 發(fā)表于 09-24 15:57 ?3554次閱讀

    全面分析Redis的最佳實踐優(yōu)化

    這篇文章我想和你聊一聊 Redis 的最佳實踐。 你的項目或許已經(jīng)使用 Redis 很長時間了,但在使用過程中,你可能還會或多或少地遇到以下問題: 我的 Redis 內(nèi)存為什么增長這么快? 為什么我
    的頭像 發(fā)表于 04-26 10:51 ?1859次閱讀

    RDMA是什么?RDMA網(wǎng)卡有什么作用?

    近幾年RDMA這個詞在行業(yè)內(nèi)炒的如火如荼,但是很多人表示RDMA具體是什么?主要應(yīng)用在哪些領(lǐng)域,有什么作用?RDMA都有哪幾種協(xié)議?今天小編就這幾個問題給大家科普一下。
    發(fā)表于 12-27 17:15 ?1.2w次閱讀

    RDMA技術(shù)簡介 RDMA的控制通路和數(shù)據(jù)通路方案

    RDMA 技術(shù)基于傳統(tǒng)以太網(wǎng)的網(wǎng)絡(luò)概念,但與以太網(wǎng)網(wǎng)絡(luò)中的同類技術(shù)存在差異。關(guān)鍵區(qū) 別在于,RDMA 提供了一種消息服務(wù),應(yīng)用程序可以使用該服務(wù)直接訪問遠程計算機上的虛擬內(nèi)存。
    發(fā)表于 04-10 09:59 ?1827次閱讀

    Redis數(shù)據(jù)同步解決方案—NineData

    NineData(https://www.ninedata.cloud/)在Redis的同步上,提供了穩(wěn)定和高效的解決方案,并且性能上也領(lǐng)先其他同步工具,特別是在同步的動態(tài)限流、數(shù)據(jù)對比修復(fù)和限流
    的頭像 發(fā)表于 06-05 15:31 ?821次閱讀
    <b class='flag-5'>Redis</b>數(shù)據(jù)同步解決<b class='flag-5'>方案</b>—NineData

    redis容器內(nèi)怎么查看redis日志

    redis是一款流行的開源內(nèi)存數(shù)據(jù)庫,常用于緩存、消息隊列、任務(wù)管理等場景。在使用redis時,了解如何查看redis日志對于排查問題、監(jiān)控性能和分析應(yīng)用程序行為非常重要。在本文中,我
    的頭像 發(fā)表于 12-05 10:10 ?3637次閱讀
    RM新时代网站-首页