色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

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

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

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

Redis RDMA改造方案分析

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

01

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

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

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

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

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

02

Redis RDMA改造方案分析

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

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

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

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

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

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

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

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

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

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

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

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

iovcnt:進(jìn)行iov操作的最大值。

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

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

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

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

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

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

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

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

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

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

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

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

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

accept:該方法在redis源碼中有明確的定義,可直接調(diào)用上述accept_handler,tcp連接類型中,該方法被注冊(cè)為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:注冊(cè)一個(gè)寫處理函數(shù),tcp連接類型中,該方法會(huì)注冊(cè)connection可寫事件,回調(diào)函數(shù)為tcp的ae_handler。

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

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

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

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

get_peer_cert:TLS連接特殊方法。

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

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

圖:Redis Connection Call Graph

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

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

圖:RDMA C/S Workflow

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

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

交互邏輯及說明如下:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

03

Redis RDMA測(cè)試

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

在實(shí)際測(cè)試中使用的是同一個(gè)交換機(jī)下的兩臺(tái)服務(wù)器,傳輸方式是rocev2,經(jīng)過qperf的測(cè)試,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

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

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

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

更多的連接和線程:

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

3.3測(cè)試總結(jié)

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

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

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

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

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

04

開源程序基于RDMA方案

4.1Tensorflow RDMA

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

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

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

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

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

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

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

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

對(duì)于一個(gè)具體的recv和send流程如下:

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

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

b)為避免在每個(gè)步驟中發(fā)送額外的元數(shù)據(jù)消息,為每個(gè)Tensor維護(hù)一個(gè)本地元數(shù)據(jù)緩存,僅在更改時(shí)才會(huì)更新,每個(gè)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í),將更新本地元數(shù)據(jù)緩存,重新分配結(jié)果/代理Tensor,重新發(fā)送Tensor請(qǐng)求。為了可追溯性,新的消息具有不同的名稱RDMA_MESSAGE_TENSOR_RE_REQUEST。

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

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

4.2BrpcRDMA

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

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

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

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

4.3NCCLRDMA

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

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

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

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

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

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

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

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

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

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

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

發(fā)送端維護(hù)本地的fifoHead游標(biāo),每次發(fā)送后游標(biāo)后移一位,發(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ù)實(shí)現(xiàn)的高性能內(nèi)核網(wǎng)絡(luò)協(xié)議棧。smc-r的主要實(shí)現(xiàn)是在內(nèi)核態(tài)實(shí)現(xiàn)了一個(gè)新的af_smc協(xié)議族,基于RDMA verbs接口實(shí)現(xiàn)內(nèi)核proto_ops中的各方法。

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

smc-r支持fallback回退機(jī)制,在通信雙方最開始建立連接時(shí)是使用tcp握手(特定的tcp選項(xiàng))進(jìn)行協(xié)商是否雙方均支持SMC-R能力,當(dāng)協(xié)商不成功時(shí)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操作直接高效地寫入對(duì)側(cè)節(jié)點(diǎn)的RMB中。同時(shí)伴隨著使用RDMA SEND/RECV操作交互連接數(shù)據(jù)管理消息,用于更新、同步環(huán)形緩沖區(qū)中的數(shù)據(jù)游標(biāo)。

接收端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)自阿里云)

下面是一個(gè)基于smc-r通信的實(shí)際測(cè)試場(chǎng)景的協(xié)商交互抓包:

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

Libvma:

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

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

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

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

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

總結(jié):

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

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

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

05

總結(jié)與展望

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

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

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

3)使用內(nèi)存池,業(yè)務(wù)及RDMA的內(nèi)存使用同一塊。性能明顯是最優(yōu)的,但是實(shí)現(xiàn)邏輯較復(fù)雜,需要管理好內(nèi)存的申請(qǐng)及釋放、某些實(shí)現(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時(shí)間,在業(yè)務(wù)邏輯中額外處理
3.網(wǎng)絡(luò)支持插件式,不同的傳輸模式實(shí)現(xiàn)相同的網(wǎng)絡(luò)方法
1.預(yù)注冊(cè)內(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通道進(jìn)行RDMA鏈接的協(xié)商
2.有控制面消息交互,如metadata更新
3.控制面信息復(fù)用RDMA通道
BRPC reactor模式 1.內(nèi)存池模式
2.使用RDMA Send模式
1.額外的流控機(jī)制
2.事件聚合
NCCL 1.reactor模式
2.網(wǎng)絡(luò)支持插件式,不同的傳輸模式實(shí)現(xiàn)相同的網(wǎng)絡(luò)方法
1.RDMA Write模式
2.通信雙方通過一個(gè)共享fifo來交互具體的DMA地址
3.DMA地址是預(yù)先注冊(cè)的內(nèi)存
1.RDMA建立階段使用TCP鏈接進(jìn)行協(xié)商

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






審核編輯:劉清

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

    關(guān)注

    68

    文章

    19259

    瀏覽量

    229653
  • 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

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

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

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    深入了解RDMA技術(shù)

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

    RDMA RNIC虛擬化方案

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

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

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

    Redis Stream應(yīng)用案例

    的IoT設(shè)備會(huì)形成巨大的數(shù)據(jù)洪流,采集完成后在云端進(jìn)行分析,產(chǎn)生巨大的用戶價(jià)值。這些數(shù)據(jù)雖然內(nèi)容各個(gè)不同,但是都有一個(gè)共同的特點(diǎn),都是一種時(shí)序數(shù)據(jù)??吹竭@里,你可能會(huì)突然發(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

    請(qǐng)問如何分析、排查、解決Redis變慢問題

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

    簡(jiǎn)要分析Redis的特性

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

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

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

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

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

    redis工作原理

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

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

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

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

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

    RDMA技術(shù)簡(jiǎn)介 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ù)直接訪問遠(yuǎn)程計(jì)算機(jī)上的虛擬內(nèi)存。
    發(fā)表于 04-10 09:59 ?1829次閱讀

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

    NineData(https://www.ninedata.cloud/)在Redis的同步上,提供了穩(wěn)定和高效的解決方案,并且性能上也領(lǐng)先其他同步工具,特別是在同步的動(dòng)態(tài)限流、數(shù)據(jù)對(duì)比修復(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ù)庫,常用于緩存、消息隊(duì)列、任務(wù)管理等場(chǎng)景。在使用redis時(shí),了解如何查看redis日志對(duì)于排查問題、監(jiān)控性能和分析應(yīng)用程序行為非常重要。在本文中,我
    的頭像 發(fā)表于 12-05 10:10 ?3637次閱讀
    主站蜘蛛池模板: 无人区在线日本高清免费| 特级黑人三人共一女| 亚洲伦理精品久久| yy4408午夜场理论片| 精品伊人久久| 校花娇喘呻吟校长陈若雪视频| 99精品视频免费在线观看| 国内偷拍夫妻av| 十九禁啊啪射视频在线观看| 97干97吻| 久久成人国产精品一区二区| 无码成A毛片免费| 超碰视频在线| 男女夜晚在爽视频免费观看| 亚洲色无码播放| 国产看午夜精品理论片| 日本熟妇乱人伦A片精品软件| 中国国产不卡视频在线观看| 国产亚洲精品久久久久5区| 日本黄色官网| 98色精品视频在线| 久久人妻熟女中文字幕AV蜜芽| 亚洲AV无码影院在线播放| 成人综合在线视频免费观看完整版| 美女脱内衣裸身尿口露出来 | 亚洲伊人久久网| 国产亚洲精品久久久久久线投注 | 成人免费视频无遮挡在线看| 美女扒开尿孔| 又硬又粗又大一区二区三区视频| 国内精品蜜汁乔依琳视频| 四虎永久免费| 广播电台在线收听| 人妻少妇69式99偷拍| 99免费在线观看| 男男h开荤粗肉h文1v1| 中文字幕中文字幕永久免费| 久久91精品国产91久| 亚洲一区综合图区| 精精国产www视频在线观看免费| 亚洲XXX午休国产熟女屁|