本文简介
在NCCL的传输层分析(一)中,我们详细介绍了NCCL传输层所涉及的几个重要的数据结构。在本文中,我们将详细介绍NCCL传输层流程中所涉及的具体函数,从而分析整个NCCL传输层中的具体流程。本文将以NCCL 2.19.4介绍NCCL传输层的具体流程。
接收端流程
由于在NCCL传输层中,数据的发送流程是由接收端进行驱动的,所以分析NCCL传输层流程时,我们先从接收端的流程开始分析。
NCCL接收端的数据流程主要由ncclIbIrecv和ncclIbPostFifo两个函数组成。
ncclIbIrecv流程分析:
1 首先在recv端创建一个ncclIbRequest,并且指定了req的type
2 往多个QP中发送ibv_recv_wr,但这里的ibv_recv_wr中不带有任何sge。这里发送ibv_recv_wr是为了感知到对Send端通过RDMA_WRITE_WITH_IMM发送到Recv端的数据(RDMA中如果对端使用RDMA_WRITE_WITH_IMM写到本端,本端需要通过ibv_poll_cq去轮询cq收到完成的WC)。
3 往多个队列发送完ibv_recv_wr后,通过ncclIbPostFifo往Send发送地址信息。
ncclIbPostFifo流程分析:
1 往comm->remFifo[MAX_REQUESTS][NCCL_NET_IB_MAX_RECVS]中的某一行填写地址信息,即ncclIbSendFifo中的addr,rkey等地址信息
2 将某一行的地址信息作为一整块内存(内存大小为NCCL_NET_IB_MAX_RECVS*sizeof(struct ncclIbSendFifo)),并把这块内存作为WR的sge,
sge的length就是这块内存的大小(NCCL_NET_IB_MAX_RECVS*sizeof(struct ncclIbSendFifo)),并填充好WR中其他相关的信息。
3 将WR通过RDMA_WRITE写到对端的一块内存,对端将会根据本端发送的地址信息进行数据的发送。
通过上述流程我们可以看到,Recv端的流程主要在于往Send端的Fifo数组中填入地址信息,写入了地址信息后,Send端就可以知道应该往Recv端的哪块内存写数据了,然后就可以开始数据发送的流程了。
发送端流程
NCCL发送端的函数实现主要有两个函数,一个是ncclIbIsend,另一个是ncclIbMultiSend。我们先来看一下ncclIbIsend的具体流程
ncclIbISend流程分析:
1 Send端在Send Comm->fifo[slot][r].idx中进行while循环轮询,这个地址就是上面提到的会被Recv端通过ncclIbPostFifo函数给RDMA_WRITE给写过来,
Send端通过轮询该值是否被RDMA_WRITE写好,以此来判断Recv端是否准备好。当这个值被写入了,就表明Recv端已经准备就绪,Send端可以开始进行
数据发送的流程了。
2 创建ncclIbRequest,并填充req中的信息,包括size,data,lkey等地址信息。
3 通过ncclIbMultiSend将数据发送出去
ncclIbMultiSend流程分析:
1 创建wr,wr中的remote_addr由Recv端通过RDMA_WRITE反馈过来
2 采用创建的多个QP,均匀的将所要发送的数据大小进行切分,通过多个QP将数据发送出去。
总结:
通过分析NCCL传输层的数据数据流程可以看到,Recv会将地址信息通过RDMA_WRITE写到发送端,发送端会根据Recv端写过来的地址信息去将自己的数据发送出去。总而言之,NCCL的数据发送的流程是由Recv驱动的,这是NCCL传输层最关键的一点。