2.8.3-1
Optimization for Tree allreduce on A100. Improve aggregation performance. Use shared buffers for inter-node send/recv. Add NVTX profiling hooks. Accelerate alltoall connections by merging communication for all channels. Add support for one hop communication through NVLink, for faster send/recv communication on cubemesh topologies like DGX-1. Improve alltoall scheduling to better balance intra/inter node communication. Increase send/recv parallelism by 8x, each warp sending or receiving to a different peer. Net: move to v4. Net: make flush operation asynchronous to accelerate alltoall. Net: define maximum number of requests. Fix hang when using LL128 protocol after 2^31 steps. Fix #379 : topology injection failing when using less GPUs than described in the XML. Fix #394 : protocol mismatch causing hangs or crashes when using one GPU per node.
This commit is contained in:
+154
-151
@@ -26,10 +26,8 @@ struct reqSlot {
|
||||
|
||||
struct collNetSendResources {
|
||||
void* collNetSendComm;
|
||||
struct ncclSendMem* hostSendMem;
|
||||
struct ncclRecvMem* hostRecvMem;
|
||||
struct ncclSendMem* devHostSendMem;
|
||||
struct ncclRecvMem* devHostRecvMem;
|
||||
struct ncclSendMem* sendMem;
|
||||
struct ncclRecvMem* recvMem;
|
||||
uint32_t* llData;
|
||||
int netDev;
|
||||
int useGdr;
|
||||
@@ -45,10 +43,8 @@ struct collNetSendResources {
|
||||
struct collNetRecvResources {
|
||||
void* netListenComm;
|
||||
void* collNetRecvComm;
|
||||
struct ncclSendMem* hostSendMem;
|
||||
struct ncclRecvMem* hostRecvMem;
|
||||
struct ncclSendMem* devHostSendMem;
|
||||
struct ncclRecvMem* devHostRecvMem;
|
||||
struct ncclSendMem* sendMem;
|
||||
struct ncclRecvMem* recvMem;
|
||||
uint32_t* llData;
|
||||
int netDev;
|
||||
int useGdr;
|
||||
@@ -67,16 +63,15 @@ ncclResult_t collNetCanConnect(int* ret, struct ncclTopoSystem* topo, struct ncc
|
||||
}
|
||||
|
||||
/* Setup send connector, and return connect information for others in the coll communicator to connect to me */
|
||||
ncclResult_t collNetSendSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* send, int channelId) {
|
||||
ncclResult_t collNetSendSetup(struct ncclComm* comm, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* send, int channelId) {
|
||||
struct collNetSendResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
send->transportResources = resources;
|
||||
|
||||
NCCLCHECK(ncclTopoGetNetDev(topo, myInfo->rank, graph, channelId, &resources->netDev));
|
||||
NCCLCHECK(ncclTopoCheckGdr(topo, myInfo->busId, resources->netDev, 1, &resources->useGdr));
|
||||
NCCLCHECK(ncclTopoGetNetDev(comm->topo, myInfo->rank, graph, channelId, &resources->netDev));
|
||||
NCCLCHECK(ncclTopoCheckGdr(comm->topo, myInfo->busId, resources->netDev, 1, &resources->useGdr));
|
||||
|
||||
NCCLCHECK(ncclCudaHostCalloc(&resources->hostSendMem, 1));
|
||||
resources->devHostSendMem = resources->hostSendMem;
|
||||
NCCLCHECK(ncclCudaHostCalloc(&resources->sendMem, 1));
|
||||
|
||||
int recvSize = offsetof(struct ncclRecvMem, buff);
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) recvSize += send->comm->buffSizes[p];
|
||||
@@ -84,8 +79,7 @@ ncclResult_t collNetSendSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph*
|
||||
if (resources->useGdr) {
|
||||
NCCLCHECK(ncclCudaCalloc((char**)(&resources->devRecvMem), recvSize));
|
||||
}
|
||||
NCCLCHECK(ncclCudaHostCalloc((char**)&resources->hostRecvMem, recvSize));
|
||||
resources->devHostRecvMem = resources->hostRecvMem;
|
||||
NCCLCHECK(ncclCudaHostCalloc((char**)&resources->recvMem, recvSize));
|
||||
NCCLCHECK(ncclIbMalloc((void**)&(resources->llData), send->comm->buffSizes[NCCL_PROTO_LL]/2));
|
||||
|
||||
INFO(NCCL_INIT|NCCL_NET,"Coll %02d : %d [send] via COLLNET/%s/%d%s", channelId, myInfo->rank, collNetName(), resources->netDev,
|
||||
@@ -94,16 +88,15 @@ ncclResult_t collNetSendSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph*
|
||||
}
|
||||
|
||||
/* Setup recv connector */
|
||||
ncclResult_t collNetRecvSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* recv, int channelId) {
|
||||
ncclResult_t collNetRecvSetup(struct ncclComm* comm, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* recv, int channelId) {
|
||||
struct collNetRecvResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
recv->transportResources = resources;
|
||||
|
||||
NCCLCHECK(ncclTopoGetNetDev(topo, myInfo->rank, graph, channelId, &resources->netDev));
|
||||
NCCLCHECK(ncclTopoCheckGdr(topo, myInfo->busId, resources->netDev, 0, &resources->useGdr));
|
||||
NCCLCHECK(ncclTopoGetNetDev(comm->topo, myInfo->rank, graph, channelId, &resources->netDev));
|
||||
NCCLCHECK(ncclTopoCheckGdr(comm->topo, myInfo->busId, resources->netDev, 0, &resources->useGdr));
|
||||
|
||||
NCCLCHECK(ncclCudaHostCalloc(&resources->hostSendMem, 1));
|
||||
resources->devHostSendMem = resources->hostSendMem;
|
||||
NCCLCHECK(ncclCudaHostCalloc(&resources->sendMem, 1));
|
||||
|
||||
int recvSize = offsetof(struct ncclRecvMem, buff);
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) recvSize += recv->comm->buffSizes[p];
|
||||
@@ -111,8 +104,7 @@ ncclResult_t collNetRecvSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph*
|
||||
if (resources->useGdr) {
|
||||
NCCLCHECK(ncclCudaCalloc((char**)(&resources->devRecvMem), recvSize));
|
||||
}
|
||||
NCCLCHECK(ncclCudaHostCalloc((char**)&resources->hostRecvMem, recvSize));
|
||||
resources->devHostRecvMem = resources->hostRecvMem;
|
||||
NCCLCHECK(ncclCudaHostCalloc((char**)&resources->recvMem, recvSize));
|
||||
|
||||
NCCLCHECK(ncclIbMalloc((void**)&(resources->llData), recv->comm->buffSizes[NCCL_PROTO_LL]/2));
|
||||
|
||||
@@ -123,25 +115,25 @@ ncclResult_t collNetRecvSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph*
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t collNetSendConnect(struct ncclConnect* connectInfos, int nranks, int rank, struct ncclConnector* send) {
|
||||
ncclResult_t collNetSendConnect(struct ncclComm* comm, struct ncclConnect* connectInfos, int nranks, int rank, struct ncclConnector* send) {
|
||||
// Setup device pointers
|
||||
struct collNetSendResources* resources = (struct collNetSendResources*)send->transportResources;
|
||||
struct collNetSendConnectInfo* info = (struct collNetSendConnectInfo*)(connectInfos+rank);
|
||||
|
||||
// Intermediate buffering on GPU for GPU Direct RDMA, but LL buffer is always on host
|
||||
struct ncclRecvMem* recvMem = resources->useGdr ? resources->devRecvMem : resources->devHostRecvMem;
|
||||
struct ncclRecvMem* recvMem = resources->useGdr ? resources->devRecvMem : resources->recvMem;
|
||||
int offset = 0;
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
send->conn.buffs[p] = (p == NCCL_PROTO_LL ? resources->devHostRecvMem->buff : recvMem->buff) + offset;
|
||||
send->conn.buffs[p] = (p == NCCL_PROTO_LL ? resources->recvMem->buff : recvMem->buff) + offset;
|
||||
offset += send->comm->buffSizes[p];
|
||||
}
|
||||
send->conn.direct |= resources->useGdr ? NCCL_DIRECT_NIC : 0;
|
||||
|
||||
// Head/Tail/Opcount/Fifos are always on host
|
||||
send->conn.tail = &resources->devHostRecvMem->tail;
|
||||
send->conn.fifo = resources->devHostRecvMem->sizesFifo;
|
||||
send->conn.head = &resources->devHostSendMem->head;
|
||||
for (int i=0; i<NCCL_STEPS; i++) send->conn.fifo[i] = -1;
|
||||
send->conn.tail = &resources->recvMem->tail;
|
||||
send->conn.sizesFifo = resources->recvMem->sizesFifo;
|
||||
send->conn.head = &resources->sendMem->head;
|
||||
for (int i=0; i<NCCL_STEPS; i++) send->conn.sizesFifo[i] = -1;
|
||||
|
||||
// Get info from recv side
|
||||
resources->collNetRank = rank;
|
||||
@@ -159,24 +151,24 @@ ncclResult_t collNetSendConnect(struct ncclConnect* connectInfos, int nranks, in
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t collNetRecvConnect(struct ncclConnect* connectInfos, int nranks, int rank, struct ncclConnector* recv) {
|
||||
ncclResult_t collNetRecvConnect(struct ncclComm* comm, struct ncclConnect* connectInfos, int nranks, int rank, struct ncclConnector* recv) {
|
||||
// Setup device pointers
|
||||
struct collNetRecvResources* resources = (struct collNetRecvResources*)recv->transportResources;
|
||||
struct collNetSendConnectInfo* info = (struct collNetSendConnectInfo*)(connectInfos+rank);
|
||||
resources->collNetRank = rank;
|
||||
|
||||
// Intermediate buffering on GPU for GPU Direct RDMA
|
||||
struct ncclRecvMem* recvMem = resources->useGdr ? resources->devRecvMem : resources->devHostRecvMem;
|
||||
struct ncclRecvMem* recvMem = resources->useGdr ? resources->devRecvMem : resources->recvMem;
|
||||
int offset = 0;
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
recv->conn.buffs[p] = (p == NCCL_PROTO_LL ? resources->devHostRecvMem->buff : recvMem->buff) + offset;
|
||||
recv->conn.buffs[p] = (p == NCCL_PROTO_LL ? resources->recvMem->buff : recvMem->buff) + offset;
|
||||
offset += recv->comm->buffSizes[p];
|
||||
}
|
||||
recv->conn.direct |= resources->useGdr ? NCCL_DIRECT_NIC : 0;
|
||||
|
||||
// Head/Tail/Opcount are always on host
|
||||
recv->conn.tail = &resources->devHostRecvMem->tail;
|
||||
recv->conn.head = &resources->devHostSendMem->head;
|
||||
recv->conn.tail = &resources->recvMem->tail;
|
||||
recv->conn.head = &resources->sendMem->head;
|
||||
|
||||
// Connect to coll comm
|
||||
collNetHandle_t** handlePtrs = NULL;
|
||||
@@ -213,8 +205,8 @@ cleanup:
|
||||
|
||||
ncclResult_t collNetSendFree(void* sendTransportResources) {
|
||||
struct collNetSendResources* resources = (struct collNetSendResources*)sendTransportResources;
|
||||
NCCLCHECK(ncclCudaHostFree(resources->hostSendMem));
|
||||
NCCLCHECK(ncclCudaHostFree(resources->hostRecvMem));
|
||||
NCCLCHECK(ncclCudaHostFree(resources->sendMem));
|
||||
NCCLCHECK(ncclCudaHostFree(resources->recvMem));
|
||||
if (resources->collNetSendComm) {
|
||||
NCCLCHECK(collNetDeregMr(resources->collNetSendComm, resources->sendMhandles[NCCL_PROTO_LL]));
|
||||
NCCLCHECK(collNetDeregMr(resources->collNetSendComm, resources->sendMhandles[NCCL_PROTO_SIMPLE]));
|
||||
@@ -228,12 +220,12 @@ ncclResult_t collNetSendFree(void* sendTransportResources) {
|
||||
|
||||
ncclResult_t collNetRecvFree(void* recvTransportResources) {
|
||||
struct collNetRecvResources* resources = (struct collNetRecvResources*)recvTransportResources;
|
||||
NCCLCHECK(ncclCudaHostFree(resources->hostSendMem));
|
||||
NCCLCHECK(ncclCudaHostFree(resources->sendMem));
|
||||
if (resources->collNetRecvComm) {
|
||||
NCCLCHECK(collNetDeregMr(resources->collNetRecvComm, resources->mhandles[NCCL_PROTO_LL]));
|
||||
NCCLCHECK(collNetDeregMr(resources->collNetRecvComm, resources->mhandles[NCCL_PROTO_SIMPLE]));
|
||||
}
|
||||
NCCLCHECK(ncclCudaHostFree(resources->hostRecvMem));
|
||||
NCCLCHECK(ncclCudaHostFree(resources->recvMem));
|
||||
if (resources->useGdr)
|
||||
CUDACHECK(cudaFree(resources->devRecvMem));
|
||||
free(resources->llData);
|
||||
@@ -256,96 +248,84 @@ ncclResult_t collNetSendProxy(struct ncclProxyArgs* args) {
|
||||
if (args->state == ncclProxyOpReady) {
|
||||
// Round to next multiple of sliceSteps
|
||||
resources->step = ROUNDUP(resources->step, args->chunkSteps);
|
||||
args->head = resources->step;
|
||||
args->tail = resources->step;
|
||||
args->end = args->head + args->nsteps;
|
||||
args->posted = args->transmitted = args->done = resources->step;
|
||||
args->end = resources->step + args->nsteps;
|
||||
args->state = ncclProxyOpProgress;
|
||||
}
|
||||
args->idle = 1;
|
||||
if (args->state == ncclProxyOpProgress) {
|
||||
int p = args->protocol;
|
||||
int stepSize = args->connector->comm->buffSizes[p] / NCCL_STEPS;
|
||||
char* localBuff = args->connector->conn.buffs[p];
|
||||
void* sendMhandle = resources->sendMhandles[p];
|
||||
void* recvMhandle = resources->recvMhandles[p];
|
||||
args->idle = 1;
|
||||
struct reqSlot* reqFifo = resources->reqFifo;
|
||||
if (args->head < args->end) {
|
||||
int buffSlot = args->tail%NCCL_STEPS;
|
||||
if (args->tail < args->end && args->tail < args->head + NCCL_STEPS
|
||||
&& reqFifo[buffSlot].recvBuff != NULL) {
|
||||
volatile int* sizesFifo = resources->hostRecvMem->sizesFifo;
|
||||
volatile uint64_t* recvTail = &resources->hostRecvMem->tail;
|
||||
int buffSlot = args->transmitted%NCCL_STEPS;
|
||||
if (args->transmitted < args->end && args->transmitted < args->done + NCCL_STEPS
|
||||
&& reqFifo[buffSlot].recvBuff != NULL) {
|
||||
volatile int* sizesFifo = resources->recvMem->sizesFifo;
|
||||
volatile uint64_t* recvTail = &resources->recvMem->tail;
|
||||
if (sizesFifo[buffSlot] != -1 && (*recvTail > args->transmitted || args->protocol == NCCL_PROTO_LL)) {
|
||||
// We have something to receive, let's check if it's completely ready.
|
||||
int size = sizesFifo[buffSlot];
|
||||
char* buff = localBuff+buffSlot*stepSize;
|
||||
int ready = 1;
|
||||
if (args->protocol == NCCL_PROTO_LL) {
|
||||
int size = sizesFifo[buffSlot];
|
||||
if (size != -1) {
|
||||
uint32_t flag = NCCL_LL_FLAG(args->tail + 1);
|
||||
int nFifoLines = DIVUP(size, sizeof(union ncclLLFifoLine));
|
||||
union ncclLLFifoLine* lines = (union ncclLLFifoLine*)(localBuff+buffSlot*stepSize);
|
||||
int ready = 1;
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
volatile uint32_t *f1 = &lines[i].flag1;
|
||||
volatile uint32_t *f2 = &lines[i].flag2;
|
||||
if (f1[0] != flag || f2[0] != flag) { ready = 0; break; }
|
||||
}
|
||||
if (ready) {
|
||||
int stepLines = stepSize / sizeof(union ncclLLFifoLine);
|
||||
//separate data from flag
|
||||
uint32_t* sendBuff = resources->llData+buffSlot*2*stepLines; // each line has two data elements
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
volatile uint32_t *d1 = &lines[i].data1;
|
||||
volatile uint32_t *d2 = &lines[i].data2;
|
||||
sendBuff[2*i] = d1[0];
|
||||
sendBuff[2*i+1] = d2[0];
|
||||
}
|
||||
int count = nFifoLines*2*sizeof(uint32_t) / ncclTypeSize(args->dtype);
|
||||
NCCLCHECK(collNetIallreduce(resources->collNetSendComm, (void*)sendBuff, (void*)(reqFifo[buffSlot].recvBuff), count, args->dtype, args->redOp, sendMhandle, recvMhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
TRACE(NCCL_NET, "sendProxy [%d/%d] Iallreduce (LL) posted, req %p", args->head, buffSlot, args->requests[buffSlot]);
|
||||
sizesFifo[buffSlot] = -1;
|
||||
// Make sure size is reset to zero before we update the head.
|
||||
__sync_synchronize();
|
||||
args->tail += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (args->tail < *recvTail) {
|
||||
// Send through network
|
||||
if (sizesFifo[buffSlot] != -1) {
|
||||
int count = sizesFifo[buffSlot]/ncclTypeSize(args->dtype);
|
||||
NCCLCHECK(collNetIallreduce(resources->collNetSendComm, localBuff+buffSlot*stepSize, (void*)(reqFifo[buffSlot].recvBuff), count, args->dtype, args->redOp, sendMhandle, recvMhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
TRACE(NCCL_NET, "sendProxy [%d/%d] Iallreduce posted, req %p count %d", args->head, buffSlot, args->requests[buffSlot], count);
|
||||
sizesFifo[buffSlot] = -1;
|
||||
// Make sure size is reset to zero before we update the head.
|
||||
__sync_synchronize();
|
||||
args->tail += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
}
|
||||
uint32_t flag = NCCL_LL_FLAG(args->transmitted + 1);
|
||||
int nFifoLines = DIVUP(size, sizeof(union ncclLLFifoLine));
|
||||
union ncclLLFifoLine* lines = (union ncclLLFifoLine*)buff;
|
||||
// Pack data into another buffer
|
||||
int stepLines = stepSize / sizeof(union ncclLLFifoLine);
|
||||
uint32_t* sendBuff = resources->llData+buffSlot*2*stepLines; // each line has two data elements
|
||||
buff = (char*)sendBuff;
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
volatile uint32_t *f1 = &lines[i].flag1;
|
||||
volatile uint32_t *d1 = &lines[i].data1;
|
||||
volatile uint32_t *f2 = &lines[i].flag2;
|
||||
volatile uint32_t *d2 = &lines[i].data2;
|
||||
if (f1[0] != flag || f2[0] != flag) { ready = 0; break; }
|
||||
sendBuff[2*i] = d1[0];
|
||||
sendBuff[2*i+1] = d2[0];
|
||||
}
|
||||
size = nFifoLines*2*sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
if (args->head < args->tail) {
|
||||
int done, size;
|
||||
int buffSlot = args->head%NCCL_STEPS;
|
||||
NCCLCHECK(collNetTest((void*)(args->requests[buffSlot]), &done, &size));
|
||||
if (done) {
|
||||
TRACE(NCCL_NET, "sendProxy [%d/%d] request %p done, size %d", args->head, buffSlot, args->requests[buffSlot], size);
|
||||
reqFifo[buffSlot].size = size;
|
||||
// Make sure size is updated before we set recvBuff to NULL (from the view of recv proxy, concerning the flush)
|
||||
// (reordered store after store is possible on POWER, though not on x86)
|
||||
__sync_synchronize();
|
||||
reqFifo[buffSlot].recvBuff = NULL; // Notify recvProxy
|
||||
args->head += args->sliceSteps;
|
||||
resources->hostSendMem->head = args->head;
|
||||
args->idle = 0;
|
||||
if (ready) {
|
||||
// Data is ready, try to send.
|
||||
int count = size/ncclTypeSize(args->dtype);
|
||||
NCCLCHECK(collNetIallreduce(resources->collNetSendComm, (void*) buff, (void*)(reqFifo[buffSlot].recvBuff), count, args->dtype, args->redOp, sendMhandle, recvMhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
TRACE(NCCL_NET, "sendProxy [%d/%d] Iallreduce posted, req %p", args->transmitted, buffSlot, args->requests[buffSlot]);
|
||||
sizesFifo[buffSlot] = -1;
|
||||
// Make sure size is reset to zero before we update the head.
|
||||
__sync_synchronize();
|
||||
args->transmitted += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args->head == args->end) {
|
||||
resources->step = args->end;
|
||||
args->idle = 0;
|
||||
args->state = ncclProxyOpNone;
|
||||
// Check whether the network has completed some send operations.
|
||||
if (args->done < args->transmitted) {
|
||||
int done, size;
|
||||
int buffSlot = args->done%NCCL_STEPS;
|
||||
NCCLCHECK(collNetTest((void*)(args->requests[buffSlot]), &done, &size));
|
||||
if (done) {
|
||||
TRACE(NCCL_NET, "sendProxy [%d/%d] request %p done, size %d", args->done, buffSlot, args->requests[buffSlot], size);
|
||||
reqFifo[buffSlot].size = size;
|
||||
// Make sure size is updated before we set recvBuff to NULL (from the view of recv proxy, concerning the flush)
|
||||
// (reordered store after store is possible on POWER, though not on x86)
|
||||
__sync_synchronize();
|
||||
reqFifo[buffSlot].recvBuff = NULL; // Notify recvProxy
|
||||
args->done += args->sliceSteps;
|
||||
resources->sendMem->head = args->done;
|
||||
args->idle = 0;
|
||||
if (args->done == args->end) {
|
||||
resources->step = args->end;
|
||||
args->state = ncclProxyOpNone;
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ncclSuccess;
|
||||
@@ -360,56 +340,79 @@ ncclResult_t collNetRecvProxy(struct ncclProxyArgs* args) {
|
||||
if (args->state == ncclProxyOpReady) {
|
||||
// Round to next multiple of sliceSteps
|
||||
resources->step = ROUNDUP(resources->step, args->chunkSteps);
|
||||
args->head = resources->step;
|
||||
args->tail = resources->step;
|
||||
args->end = args->head + args->nsteps;
|
||||
args->posted = args->received = args->transmitted = args->done = resources->step;
|
||||
args->end = resources->step + args->nsteps;
|
||||
args->state = ncclProxyOpProgress;
|
||||
}
|
||||
args->idle = 1;
|
||||
if (args->state == ncclProxyOpProgress) {
|
||||
args->idle = 1;
|
||||
int p = args->protocol;
|
||||
int stepSize = args->connector->comm->buffSizes[p] / NCCL_STEPS;
|
||||
char* localBuff = args->connector->conn.buffs[p];
|
||||
void* mhandle = resources->mhandles[p];
|
||||
struct reqSlot* reqFifo = resources->reqFifo;
|
||||
if (args->head < args->end) {
|
||||
if ((args->tail < args->head + NCCL_STEPS) && (args->tail < (resources->hostSendMem->head) + NCCL_STEPS) && (args->tail < args->end)) {
|
||||
int buffSlot = args->tail%NCCL_STEPS;
|
||||
char* recvBuff = p == NCCL_PROTO_LL ? (char*)resources->llData : localBuff;
|
||||
int recvStepSize = p == NCCL_PROTO_LL ? stepSize/2 : stepSize;
|
||||
reqFifo[buffSlot].recvBuff = recvBuff+buffSlot*recvStepSize;
|
||||
TRACE(NCCL_NET, "recvProxy [%d/%d] posted buffer %p", args->tail, buffSlot, reqFifo[buffSlot].recvBuff);
|
||||
args->tail += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
}
|
||||
if (args->tail > args->head) {
|
||||
int buffSlot = args->head%NCCL_STEPS;
|
||||
if (reqFifo[buffSlot].recvBuff == NULL) { // Buffer is cleared : coll is complete
|
||||
TRACE(NCCL_NET, "recvProxy [%d/%d] done, size %d", args->head, buffSlot, reqFifo[buffSlot].size);
|
||||
args->head += args->sliceSteps;
|
||||
if (args->protocol == NCCL_PROTO_LL) { // ll
|
||||
// re-attach flag
|
||||
uint32_t flag = args->head;
|
||||
int stepLines = stepSize / sizeof(union ncclLLFifoLine);
|
||||
union ncclLLFifoLine* lines = (union ncclLLFifoLine*)(localBuff+buffSlot*stepSize);
|
||||
uint32_t* recvData = resources->llData+buffSlot*2*stepLines;
|
||||
int nFifoLines = DIVUP(reqFifo[buffSlot].size, 2*sizeof(uint32_t));
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
lines[i].v[0] = ((uint64_t)flag << 32) + recvData[2*i];
|
||||
lines[i].v[1] = ((uint64_t)flag << 32) + recvData[2*i+1];
|
||||
}
|
||||
} else if (args->protocol == NCCL_PROTO_SIMPLE) {
|
||||
if (resources->useGdr) NCCLCHECK(collNetFlush(resources->collNetRecvComm, localBuff+buffSlot*stepSize, reqFifo[buffSlot].size, mhandle));
|
||||
resources->hostRecvMem->tail = args->head;
|
||||
if ((args->posted < args->done + NCCL_STEPS) && (args->posted < args->end)) {
|
||||
int buffSlot = args->posted%NCCL_STEPS;
|
||||
char* recvBuff = p == NCCL_PROTO_LL ? (char*)resources->llData : localBuff;
|
||||
int recvStepSize = p == NCCL_PROTO_LL ? stepSize/2 : stepSize;
|
||||
reqFifo[buffSlot].recvBuff = recvBuff+buffSlot*recvStepSize;
|
||||
TRACE(NCCL_NET, "recvProxy [%d/%d] posted buffer %p", args->posted, buffSlot, reqFifo[buffSlot].recvBuff);
|
||||
args->posted += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
if (args->posted > args->received) {
|
||||
int buffSlot = args->received%NCCL_STEPS;
|
||||
if (reqFifo[buffSlot].recvBuff == NULL) { // Buffer is cleared : coll is complete
|
||||
TRACE(NCCL_NET, "recvProxy [%d/%d] done, size %d", args->received, buffSlot, reqFifo[buffSlot].size);
|
||||
if (args->protocol == NCCL_PROTO_LL) { // ll
|
||||
// re-attach flag
|
||||
uint32_t flag = NCCL_LL_FLAG(args->received + 1);
|
||||
int stepLines = stepSize / sizeof(union ncclLLFifoLine);
|
||||
union ncclLLFifoLine* lines = (union ncclLLFifoLine*)(localBuff+buffSlot*stepSize);
|
||||
uint32_t* recvData = resources->llData+buffSlot*2*stepLines;
|
||||
int nFifoLines = DIVUP(reqFifo[buffSlot].size, 2*sizeof(uint32_t));
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
lines[i].v[0] = ((uint64_t)flag << 32) + recvData[2*i];
|
||||
lines[i].v[1] = ((uint64_t)flag << 32) + recvData[2*i+1];
|
||||
}
|
||||
args->idle = 0;
|
||||
}
|
||||
args->received += args->sliceSteps;
|
||||
if (reqFifo[buffSlot].size > 0 && args->protocol == NCCL_PROTO_SIMPLE && resources->useGdr) {
|
||||
NCCLCHECK(collNetIflush(resources->collNetRecvComm, localBuff+buffSlot*stepSize, reqFifo[buffSlot].size, mhandle, args->requests+buffSlot));
|
||||
} else {
|
||||
args->requests[buffSlot] = NULL;
|
||||
}
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
if (args->head == args->end) {
|
||||
resources->step = args->end;
|
||||
args->idle = 0;
|
||||
args->state = ncclProxyOpNone;
|
||||
if (args->received > args->transmitted) {
|
||||
// Progress flush operations
|
||||
int buffSlot = args->transmitted%NCCL_STEPS;
|
||||
int done = 1;
|
||||
if (args->requests[buffSlot]) NCCLCHECK(collNetTest(args->requests[buffSlot], &done, NULL));
|
||||
if (done) {
|
||||
args->transmitted += args->sliceSteps;
|
||||
__sync_synchronize();
|
||||
resources->recvMem->tail = args->transmitted;
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
if (args->transmitted > args->done) {
|
||||
volatile uint64_t* sendHead = &resources->sendMem->head;
|
||||
uint64_t done = *sendHead;
|
||||
while (done > args->done &&
|
||||
// LL and LL128 can acknowledge 0-bytes send before they even happen. Don't go past what we transmitted.
|
||||
args->transmitted > args->done) {
|
||||
args->done += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
if (args->done == args->end) {
|
||||
resources->step = args->end;
|
||||
args->state = ncclProxyOpNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ncclSuccess;
|
||||
|
||||
+256
-179
@@ -7,6 +7,7 @@
|
||||
#include "comm.h"
|
||||
#include "net.h"
|
||||
#include "graph.h"
|
||||
#include "collectives.h"
|
||||
|
||||
struct netConnectInfo {
|
||||
ncclNetHandle_t netHandle;
|
||||
@@ -22,6 +23,7 @@ struct netSendResources {
|
||||
struct ncclRecvMem* recvMem;
|
||||
int netDev;
|
||||
int useGdr;
|
||||
int shared;
|
||||
char* buffers[LOC_COUNT];
|
||||
int buffSizes[LOC_COUNT];
|
||||
void* mhandles[LOC_COUNT];
|
||||
@@ -37,6 +39,7 @@ struct netRecvResources {
|
||||
struct ncclRecvMem* recvMem;
|
||||
int netDev;
|
||||
int useGdr;
|
||||
int shared;
|
||||
char* buffers[LOC_COUNT];
|
||||
int buffSizes[LOC_COUNT];
|
||||
void* mhandles[LOC_COUNT];
|
||||
@@ -51,108 +54,118 @@ ncclResult_t netCanConnect(int* ret, struct ncclTopoSystem* topo, struct ncclTop
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
NCCL_PARAM(NetSharedBuffers, "NET_SHARED_BUFFERS", -2);
|
||||
|
||||
/* Determine if we will use this transport for this peer and return connect
|
||||
* information for this peer */
|
||||
ncclResult_t netSendSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* send, int channelId) {
|
||||
ncclResult_t netSendSetup(struct ncclComm* comm, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* send, int channelId) {
|
||||
struct netSendResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
send->transportResources = resources;
|
||||
send->conn.shared = resources->shared = ncclParamNetSharedBuffers() != -2 ? ncclParamNetSharedBuffers() : graph ? 0 : 1;
|
||||
|
||||
NCCLCHECK(ncclTopoGetNetDev(topo, myInfo->rank, graph, channelId, &resources->netDev));
|
||||
NCCLCHECK(ncclTopoCheckGdr(topo, myInfo->busId, resources->netDev, 1, &resources->useGdr));
|
||||
NCCLCHECK(ncclTopoGetNetDev(comm->topo, myInfo->rank, graph, channelId, &resources->netDev));
|
||||
NCCLCHECK(ncclTopoCheckGdr(comm->topo, myInfo->busId, resources->netDev, 1, &resources->useGdr));
|
||||
|
||||
NCCLCHECK(ncclCudaHostCalloc(&resources->sendMem, 1));
|
||||
NCCLCHECK(ncclCudaHostCalloc(&resources->recvMem, 1));
|
||||
|
||||
send->conn.direct |= resources->useGdr ? NCCL_DIRECT_NIC : 0;
|
||||
send->conn.tail = &resources->recvMem->tail;
|
||||
send->conn.fifo = resources->recvMem->sizesFifo;
|
||||
send->conn.sizesFifo = resources->recvMem->sizesFifo;
|
||||
// Only fuse P2P buffers, continue to allocate dedicated buffers for ring/tree
|
||||
send->conn.ptrsFifo = resources->shared ? resources->recvMem->ptrsFifo : NULL;
|
||||
send->conn.head = &resources->sendMem->head;
|
||||
for (int i=0; i<NCCL_STEPS; i++) send->conn.fifo[i] = -1;
|
||||
resources->sendMem->head = resources->shared ? -NCCL_STEPS : 0; // Don't give any credit yet when sharing buffers
|
||||
for (int i=0; i<NCCL_STEPS; i++) send->conn.sizesFifo[i] = -1;
|
||||
|
||||
int protoLoc[NCCL_NUM_PROTOCOLS];
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
protoLoc[p] = p != NCCL_PROTO_LL && resources->useGdr ? LOC_DEVMEM : LOC_HOSTMEM;
|
||||
if (resources->shared == 0) {
|
||||
int protoLoc[NCCL_NUM_PROTOCOLS];
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
protoLoc[p] = p != NCCL_PROTO_LL && resources->useGdr ? LOC_DEVMEM : LOC_HOSTMEM;
|
||||
}
|
||||
int buffSizes[NCCL_NUM_PROTOCOLS];
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
buffSizes[p] = send->comm->buffSizes[p];
|
||||
resources->buffSizes[protoLoc[p]] += buffSizes[p];
|
||||
}
|
||||
|
||||
if (resources->buffSizes[LOC_DEVMEM]) {
|
||||
NCCLCHECK(ncclCudaCalloc(resources->buffers+LOC_DEVMEM, resources->buffSizes[LOC_DEVMEM]));
|
||||
}
|
||||
if (resources->buffSizes[LOC_HOSTMEM]) {
|
||||
NCCLCHECK(ncclCudaHostCalloc(resources->buffers+LOC_HOSTMEM, resources->buffSizes[LOC_HOSTMEM]));
|
||||
}
|
||||
|
||||
int offsets[LOC_COUNT];
|
||||
offsets[LOC_HOSTMEM] = offsets[LOC_DEVMEM] = 0;
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
resources->mhandlesProto[p] = resources->mhandles+protoLoc[p];
|
||||
send->conn.buffs[p] = resources->buffers[protoLoc[p]] + offsets[protoLoc[p]];
|
||||
offsets[protoLoc[p]] += buffSizes[p];
|
||||
}
|
||||
}
|
||||
|
||||
int buffSizes[NCCL_NUM_PROTOCOLS];
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
// Only allocate buffers for simple for p2p connections
|
||||
buffSizes[p] = graph == NULL && p != NCCL_PROTO_SIMPLE ? 0 : send->comm->buffSizes[p];
|
||||
resources->buffSizes[protoLoc[p]] += buffSizes[p];
|
||||
}
|
||||
|
||||
if (resources->buffSizes[LOC_DEVMEM]) {
|
||||
NCCLCHECK(ncclCudaCalloc(resources->buffers+LOC_DEVMEM, resources->buffSizes[LOC_DEVMEM]));
|
||||
}
|
||||
if (resources->buffSizes[LOC_HOSTMEM]) {
|
||||
NCCLCHECK(ncclCudaHostCalloc(resources->buffers+LOC_HOSTMEM, resources->buffSizes[LOC_HOSTMEM]));
|
||||
}
|
||||
|
||||
int offsets[LOC_COUNT];
|
||||
offsets[LOC_HOSTMEM] = offsets[LOC_DEVMEM] = 0;
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
resources->mhandlesProto[p] = resources->mhandles+protoLoc[p];
|
||||
send->conn.buffs[p] = resources->buffers[protoLoc[p]] + offsets[protoLoc[p]];
|
||||
offsets[protoLoc[p]] += buffSizes[p];
|
||||
}
|
||||
|
||||
INFO(NCCL_INIT|NCCL_NET,"Channel %02d : %d[%lx] -> %d[%lx] [send] via NET/%s/%d%s", channelId, myInfo->rank, myInfo->busId, peerInfo->rank, peerInfo->busId, ncclNetName(), resources->netDev,
|
||||
resources->useGdr ? "/GDRDMA" : "");
|
||||
INFO(NCCL_INIT|NCCL_NET,"Channel %02d : %d[%lx] -> %d[%lx] [send] via NET/%s/%d%s%s", channelId, myInfo->rank, myInfo->busId, peerInfo->rank, peerInfo->busId, ncclNetName(), resources->netDev,
|
||||
resources->useGdr ? "/GDRDMA" : "", resources->shared ? "/Shared" : "");
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t netRecvSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* recv, int channelId) {
|
||||
ncclResult_t netRecvSetup(struct ncclComm* comm, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* recv, int channelId) {
|
||||
struct netRecvResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
recv->transportResources = resources;
|
||||
recv->conn.shared = resources->shared = ncclParamNetSharedBuffers() != -2 ? ncclParamNetSharedBuffers() : graph ? 0 : 1;
|
||||
|
||||
NCCLCHECK(ncclTopoGetNetDev(topo, myInfo->rank, graph, channelId, &resources->netDev));
|
||||
NCCLCHECK(ncclTopoCheckGdr(topo, myInfo->busId, resources->netDev, 0, &resources->useGdr));
|
||||
NCCLCHECK(ncclTopoGetNetDev(comm->topo, myInfo->rank, graph, channelId, &resources->netDev));
|
||||
NCCLCHECK(ncclTopoCheckGdr(comm->topo, myInfo->busId, resources->netDev, 0, &resources->useGdr));
|
||||
|
||||
NCCLCHECK(ncclCudaHostCalloc(&resources->sendMem, 1));
|
||||
NCCLCHECK(ncclCudaHostCalloc(&resources->recvMem, 1));
|
||||
|
||||
recv->conn.direct |= resources->useGdr ? NCCL_DIRECT_NIC : 0;
|
||||
recv->conn.tail = &resources->recvMem->tail;
|
||||
// Only fuse P2P buffers, continue to allocate dedicated buffers for ring/tree
|
||||
recv->conn.ptrsFifo = resources->shared ? resources->recvMem->ptrsFifo : NULL;
|
||||
recv->conn.head = &resources->sendMem->head;
|
||||
|
||||
int protoLoc[NCCL_NUM_PROTOCOLS];
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
protoLoc[p] = resources->useGdr ? LOC_DEVMEM : LOC_HOSTMEM;
|
||||
if (resources->shared == 0) { // Only allocate dedicated buffers for ring/tree not for p2p
|
||||
int protoLoc[NCCL_NUM_PROTOCOLS];
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
protoLoc[p] = resources->useGdr ? LOC_DEVMEM : LOC_HOSTMEM;
|
||||
}
|
||||
|
||||
int buffSizes[NCCL_NUM_PROTOCOLS];
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
buffSizes[p] = recv->comm->buffSizes[p];
|
||||
resources->buffSizes[protoLoc[p]] += buffSizes[p];
|
||||
}
|
||||
|
||||
if (resources->buffSizes[LOC_DEVMEM]) {
|
||||
NCCLCHECK(ncclCudaCalloc(resources->buffers+LOC_DEVMEM, resources->buffSizes[LOC_DEVMEM]));
|
||||
}
|
||||
if (resources->buffSizes[LOC_HOSTMEM]) {
|
||||
NCCLCHECK(ncclCudaHostCalloc(resources->buffers+LOC_HOSTMEM, resources->buffSizes[LOC_HOSTMEM]));
|
||||
}
|
||||
|
||||
int offsets[LOC_COUNT];
|
||||
offsets[LOC_HOSTMEM] = offsets[LOC_DEVMEM] = 0;
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
resources->mhandlesProto[p] = resources->mhandles+protoLoc[p];
|
||||
recv->conn.buffs[p] = resources->buffers[protoLoc[p]] + offsets[protoLoc[p]];
|
||||
offsets[protoLoc[p]] += buffSizes[p];
|
||||
}
|
||||
}
|
||||
|
||||
int buffSizes[NCCL_NUM_PROTOCOLS];
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
// Only allocate buffers for simple for p2p connections
|
||||
buffSizes[p] = graph == NULL && p != NCCL_PROTO_SIMPLE ? 0 : recv->comm->buffSizes[p];
|
||||
resources->buffSizes[protoLoc[p]] += buffSizes[p];
|
||||
}
|
||||
|
||||
if (resources->buffSizes[LOC_DEVMEM]) {
|
||||
NCCLCHECK(ncclCudaCalloc(resources->buffers+LOC_DEVMEM, resources->buffSizes[LOC_DEVMEM]));
|
||||
}
|
||||
if (resources->buffSizes[LOC_HOSTMEM]) {
|
||||
NCCLCHECK(ncclCudaHostCalloc(resources->buffers+LOC_HOSTMEM, resources->buffSizes[LOC_HOSTMEM]));
|
||||
}
|
||||
|
||||
int offsets[LOC_COUNT];
|
||||
offsets[LOC_HOSTMEM] = offsets[LOC_DEVMEM] = 0;
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
resources->mhandlesProto[p] = resources->mhandles+protoLoc[p];
|
||||
recv->conn.buffs[p] = resources->buffers[protoLoc[p]] + offsets[protoLoc[p]];
|
||||
offsets[protoLoc[p]] += buffSizes[p];
|
||||
}
|
||||
|
||||
INFO(NCCL_INIT|NCCL_NET,"Channel %02d : %d[%lx] -> %d[%lx] [receive] via NET/%s/%d%s", channelId, peerInfo->rank, peerInfo->busId, myInfo->rank, myInfo->busId, ncclNetName(), resources->netDev,
|
||||
resources->useGdr ? "/GDRDMA" : "");
|
||||
INFO(NCCL_INIT|NCCL_NET,"Channel %02d : %d[%lx] -> %d[%lx] [receive] via NET/%s/%d%s%s", channelId, peerInfo->rank, peerInfo->busId, myInfo->rank, myInfo->busId, ncclNetName(), resources->netDev,
|
||||
resources->useGdr ? "/GDRDMA" : "", resources->shared ? "/Shared" : "");
|
||||
struct netConnectInfo* info = (struct netConnectInfo*) connectInfo;
|
||||
NCCLCHECK(ncclNetListen(resources->netDev, &info->netHandle, &resources->netListenComm));
|
||||
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t netSendConnect(struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* send) {
|
||||
ncclResult_t netSendConnect(struct ncclComm* comm, struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* send) {
|
||||
// Setup device pointers
|
||||
struct netSendResources* resources = (struct netSendResources*)send->transportResources;
|
||||
struct netConnectInfo* info = (struct netConnectInfo*)connectInfo;
|
||||
@@ -160,6 +173,13 @@ ncclResult_t netSendConnect(struct ncclConnect* connectInfo, int nranks, int ran
|
||||
// Connect to remote peer
|
||||
NCCLCHECK(ncclNetConnect(resources->netDev, info->netHandle, &resources->netSendComm));
|
||||
|
||||
if (resources->shared) {
|
||||
// Get shared buffers
|
||||
int loc = resources->useGdr ? LOC_DEVMEM : LOC_HOSTMEM;
|
||||
NCCLCHECK(ncclProxySharedBuffersInit(send->comm, resources->useGdr, resources->buffSizes+loc, resources->buffers+loc));
|
||||
resources->mhandlesProto[NCCL_PROTO_SIMPLE] = resources->mhandles+loc;
|
||||
}
|
||||
|
||||
if (resources->buffSizes[LOC_DEVMEM]) {
|
||||
NCCLCHECK(ncclNetRegMr(resources->netSendComm, resources->buffers[LOC_DEVMEM], resources->buffSizes[LOC_DEVMEM], NCCL_PTR_CUDA, &resources->mhandles[LOC_DEVMEM]));
|
||||
}
|
||||
@@ -170,7 +190,7 @@ ncclResult_t netSendConnect(struct ncclConnect* connectInfo, int nranks, int ran
|
||||
}
|
||||
|
||||
/* Connect to this peer */
|
||||
ncclResult_t netRecvConnect(struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* recv) {
|
||||
ncclResult_t netRecvConnect(struct ncclComm* comm, struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* recv) {
|
||||
// Setup device pointers
|
||||
struct netRecvResources* resources = (struct netRecvResources*)recv->transportResources;
|
||||
|
||||
@@ -178,6 +198,13 @@ ncclResult_t netRecvConnect(struct ncclConnect* connectInfo, int nranks, int ran
|
||||
NCCLCHECK(ncclNetAccept(resources->netListenComm, &resources->netRecvComm));
|
||||
NCCLCHECK(ncclNetCloseListen(resources->netListenComm));
|
||||
|
||||
if (resources->shared) {
|
||||
// Get shared buffers
|
||||
int loc = resources->useGdr ? LOC_DEVMEM : LOC_HOSTMEM;
|
||||
NCCLCHECK(ncclProxySharedBuffersInit(recv->comm, resources->useGdr, resources->buffSizes+loc, resources->buffers+loc));
|
||||
resources->mhandlesProto[NCCL_PROTO_SIMPLE] = resources->mhandles+loc;
|
||||
}
|
||||
|
||||
if (resources->buffSizes[LOC_DEVMEM]) {
|
||||
NCCLCHECK(ncclNetRegMr(resources->netRecvComm, resources->buffers[LOC_DEVMEM], resources->buffSizes[LOC_DEVMEM], NCCL_PTR_CUDA, &resources->mhandles[LOC_DEVMEM]));
|
||||
}
|
||||
@@ -195,8 +222,10 @@ ncclResult_t netSendFree(void* transportResources) {
|
||||
if (resources->buffers[l])
|
||||
NCCLCHECK(ncclNetDeregMr(resources->netSendComm, resources->mhandles[l]));
|
||||
}
|
||||
NCCLCHECK(ncclCudaHostFree(resources->buffers[LOC_HOSTMEM]));
|
||||
CUDACHECK(cudaFree(resources->buffers[LOC_DEVMEM]));
|
||||
if (resources->shared == 0) {
|
||||
NCCLCHECK(ncclCudaHostFree(resources->buffers[LOC_HOSTMEM]));
|
||||
CUDACHECK(cudaFree(resources->buffers[LOC_DEVMEM]));
|
||||
}
|
||||
NCCLCHECK(ncclNetCloseSend(resources->netSendComm));
|
||||
free(resources);
|
||||
return ncclSuccess;
|
||||
@@ -210,115 +239,121 @@ ncclResult_t netRecvFree(void* transportResources) {
|
||||
if (resources->buffers[l])
|
||||
NCCLCHECK(ncclNetDeregMr(resources->netRecvComm, resources->mhandles[l]));
|
||||
}
|
||||
NCCLCHECK(ncclCudaHostFree(resources->buffers[LOC_HOSTMEM]));
|
||||
CUDACHECK(cudaFree(resources->buffers[LOC_DEVMEM]));
|
||||
if (resources->shared == 0) {
|
||||
NCCLCHECK(ncclCudaHostFree(resources->buffers[LOC_HOSTMEM]));
|
||||
CUDACHECK(cudaFree(resources->buffers[LOC_DEVMEM]));
|
||||
}
|
||||
NCCLCHECK(ncclNetCloseRecv(resources->netRecvComm));
|
||||
free(resources);
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
static_assert(NCCL_STEPS <= NCCL_NET_MAX_REQUESTS, "Not enough net requests to cover for steps");
|
||||
|
||||
ncclResult_t netSendProxy(struct ncclProxyArgs* args) {
|
||||
struct netSendResources* resources = (struct netSendResources*) (args->connector->transportResources);
|
||||
if (args->state == ncclProxyOpReady) {
|
||||
// Round to next multiple of sliceSteps
|
||||
resources->step = ROUNDUP(resources->step, args->chunkSteps);
|
||||
args->head = resources->step;
|
||||
args->tail = resources->step;
|
||||
args->end = args->head + args->nsteps;
|
||||
args->posted = args->transmitted = args->done = resources->step;
|
||||
args->end = resources->step + args->nsteps;
|
||||
args->state = ncclProxyOpProgress;
|
||||
}
|
||||
args->idle = 1;
|
||||
if (args->state == ncclProxyOpProgress) {
|
||||
int p = args->protocol;
|
||||
int stepSize = args->connector->comm->buffSizes[p] / NCCL_STEPS;
|
||||
char* localBuff = args->connector->conn.buffs[p];
|
||||
void* mhandle = *(resources->mhandlesProto[p]);
|
||||
args->idle = 1;
|
||||
if (args->head < args->end) {
|
||||
int buffSlot = args->tail%NCCL_STEPS;
|
||||
if (args->tail < args->end && args->tail < args->head + NCCL_STEPS) {
|
||||
volatile int* sizesFifo = resources->recvMem->sizesFifo;
|
||||
volatile uint64_t* recvTail = &resources->recvMem->tail;
|
||||
int buffSize = stepSize*args->sliceSteps;
|
||||
if (resources->shared) buffSize /= SENDRECV_SLICEFACTOR;
|
||||
if (args->sendbytes < buffSize) buffSize = args->sendbytes;
|
||||
// Post buffers to the GPU
|
||||
if (args->posted < args->end && args->posted < args->done + NCCL_STEPS) {
|
||||
if (resources->shared) {
|
||||
char* ptr;
|
||||
NCCLCHECK(ncclProxySharedBuffersAlloc(args->connector->comm, resources->useGdr, 0, args->channel->id, buffSize, &ptr));
|
||||
if (ptr == NULL) return ncclInternalError;
|
||||
resources->recvMem->ptrsFifo[args->posted%NCCL_STEPS] = ptr;
|
||||
__sync_synchronize();
|
||||
volatile uint64_t* sendHead = &resources->sendMem->head;
|
||||
args->posted += args->sliceSteps;
|
||||
*sendHead = args->posted - NCCL_STEPS;
|
||||
} else args->posted += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
// Check whether we received data from the GPU and send it to the network
|
||||
int buffSlot = args->transmitted%NCCL_STEPS;
|
||||
if (args->transmitted < args->posted && args->transmitted < args->done + NCCL_STEPS) {
|
||||
volatile int* sizesFifo = resources->recvMem->sizesFifo;
|
||||
volatile uint64_t* recvTail = &resources->recvMem->tail;
|
||||
if (sizesFifo[buffSlot] != -1 && (*recvTail > args->transmitted || args->protocol == NCCL_PROTO_LL)) {
|
||||
// We have something to receive, let's check if it's completely ready.
|
||||
int size = sizesFifo[buffSlot];
|
||||
char* buff = resources->shared ? (char*)resources->recvMem->ptrsFifo[buffSlot] : localBuff+buffSlot*stepSize;
|
||||
int ready = 1;
|
||||
if (args->protocol == NCCL_PROTO_LL128) {
|
||||
if (args->tail < *recvTail) {
|
||||
if (sizesFifo[buffSlot] != -1) {
|
||||
int ready = resources->useGdr;
|
||||
if (!ready) {
|
||||
// When data is in sysmem, we need to wait until all flags are correct since the GPU only
|
||||
// called threadfence()
|
||||
uint64_t flag = args->tail + 1;
|
||||
int nFifoLines = DIVUP(sizesFifo[buffSlot], sizeof(uint64_t)*NCCL_LL128_LINEELEMS);
|
||||
volatile uint64_t* lines = (volatile uint64_t*)(localBuff+buffSlot*stepSize);
|
||||
ready = 1;
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
if (lines[i*NCCL_LL128_LINEELEMS+NCCL_LL128_DATAELEMS] != flag) { ready = 0; break; }
|
||||
}
|
||||
}
|
||||
if (ready) {
|
||||
// Send through network
|
||||
NCCLCHECK(ncclNetIsend(resources->netSendComm, localBuff+buffSlot*stepSize, sizesFifo[buffSlot], mhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
sizesFifo[buffSlot] = -1;
|
||||
// Make sure size is reset to zero before we update the head.
|
||||
__sync_synchronize();
|
||||
args->tail += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
}
|
||||
}
|
||||
int ready = resources->useGdr;
|
||||
if (!ready) {
|
||||
// When data is in sysmem, we need to wait until all flags are correct since the GPU only
|
||||
// called threadfence()
|
||||
uint64_t flag = args->transmitted + 1;
|
||||
int nFifoLines = DIVUP(sizesFifo[buffSlot], sizeof(uint64_t)*NCCL_LL128_LINEELEMS);
|
||||
volatile uint64_t* lines = (volatile uint64_t*)buff;
|
||||
ready = 1;
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
if (lines[i*NCCL_LL128_LINEELEMS+NCCL_LL128_DATAELEMS] != flag) { ready = 0; break; }
|
||||
}
|
||||
}
|
||||
} else if (args->protocol == NCCL_PROTO_LL) {
|
||||
int size = sizesFifo[buffSlot];
|
||||
if (size != -1) {
|
||||
uint32_t flag = NCCL_LL_FLAG(args->tail + 1);
|
||||
int nFifoLines = DIVUP(size, sizeof(union ncclLLFifoLine));
|
||||
size = nFifoLines * sizeof(union ncclLLFifoLine);
|
||||
union ncclLLFifoLine* lines = (union ncclLLFifoLine*)(localBuff+buffSlot*stepSize);
|
||||
int ready = 1;
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
volatile uint32_t *f1 = &lines[i].flag1;
|
||||
volatile uint32_t *f2 = &lines[i].flag2;
|
||||
if (f1[0] != flag || f2[0] != flag) { ready = 0; break; }
|
||||
}
|
||||
if (ready) {
|
||||
NCCLCHECK(ncclNetIsend(resources->netSendComm, lines, size, mhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
sizesFifo[buffSlot] = -1;
|
||||
// Make sure size is reset to zero before we update the head.
|
||||
__sync_synchronize();
|
||||
args->tail += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (args->tail < *recvTail) {
|
||||
// Send through network
|
||||
if (sizesFifo[buffSlot] != -1) {
|
||||
NCCLCHECK(ncclNetIsend(resources->netSendComm, localBuff+buffSlot*stepSize, sizesFifo[buffSlot], mhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
sizesFifo[buffSlot] = -1;
|
||||
// Make sure size is reset to zero before we update the head.
|
||||
__sync_synchronize();
|
||||
args->tail += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
}
|
||||
uint32_t flag = NCCL_LL_FLAG(args->transmitted + 1);
|
||||
int nFifoLines = DIVUP(size, sizeof(union ncclLLFifoLine));
|
||||
union ncclLLFifoLine* lines = (union ncclLLFifoLine*)buff;
|
||||
for (int i=0; i<nFifoLines; i++) {
|
||||
volatile uint32_t *f1 = &lines[i].flag1;
|
||||
volatile uint32_t *f2 = &lines[i].flag2;
|
||||
if (f1[0] != flag || f2[0] != flag) { ready = 0; break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args->head < args->tail) {
|
||||
int done;
|
||||
int buffSlot = args->head%NCCL_STEPS;
|
||||
NCCLCHECK(ncclNetTest(args->requests[buffSlot], &done, NULL));
|
||||
if (done) {
|
||||
args->head += args->sliceSteps;
|
||||
resources->sendMem->head = args->head;
|
||||
args->idle = 0;
|
||||
if (ready) {
|
||||
// Data is ready, try to send.
|
||||
NCCLCHECK(ncclNetIsend(resources->netSendComm, buff, size, mhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
TRACE(NCCL_NET, "sendProxy [%d/%d] Isend (LL) posted, req %p", args->transmitted, buffSlot, args->requests[buffSlot]);
|
||||
sizesFifo[buffSlot] = -1;
|
||||
// Make sure size is reset to zero before we update the head.
|
||||
__sync_synchronize();
|
||||
args->transmitted += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (args->head == args->end) {
|
||||
resources->step = args->end;
|
||||
args->idle = 0;
|
||||
args->state = ncclProxyOpNone;
|
||||
// Check whether the network has completed some send operations.
|
||||
if (args->done < args->transmitted) {
|
||||
int done;
|
||||
int buffSlot = args->done%NCCL_STEPS;
|
||||
NCCLCHECK(ncclNetTest(args->requests[buffSlot], &done, NULL));
|
||||
if (done) {
|
||||
TRACE(NCCL_NET, "sendProxy [%d/%d] request %p done, size %d", args->done, buffSlot, args->requests[buffSlot]);
|
||||
if (resources->shared) {
|
||||
char* ptr = (char*)resources->recvMem->ptrsFifo[args->done%NCCL_STEPS];
|
||||
NCCLCHECK(ncclProxySharedBuffersFree(args->connector->comm, resources->useGdr, 0, args->channel->id, buffSize, ptr));
|
||||
}
|
||||
args->done += args->sliceSteps;
|
||||
|
||||
if (resources->shared == 0) {
|
||||
resources->sendMem->head = args->done;
|
||||
}
|
||||
args->idle = 0;
|
||||
if (args->done == args->end) {
|
||||
resources->step = args->end;
|
||||
args->state = ncclProxyOpNone;
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ncclSuccess;
|
||||
@@ -329,46 +364,88 @@ ncclResult_t netRecvProxy(struct ncclProxyArgs* args) {
|
||||
if (args->state == ncclProxyOpReady) {
|
||||
// Round to next multiple of sliceSteps
|
||||
resources->step = ROUNDUP(resources->step, args->chunkSteps);
|
||||
args->head = resources->step;
|
||||
args->tail = resources->step;
|
||||
args->end = args->head + args->nsteps;
|
||||
args->posted = args->received = args->transmitted = args->done = resources->step;
|
||||
args->end = resources->step + args->nsteps;
|
||||
args->state = ncclProxyOpProgress;
|
||||
}
|
||||
args->idle = 1;
|
||||
if (args->state == ncclProxyOpProgress) {
|
||||
args->idle = 1;
|
||||
int p = args->protocol;
|
||||
int stepSize = args->connector->comm->buffSizes[p] / NCCL_STEPS;
|
||||
char* localBuff = args->connector->conn.buffs[p];
|
||||
void* mhandle = *(resources->mhandlesProto[p]);
|
||||
if (args->head < args->end) {
|
||||
volatile uint64_t* sendHead = &resources->sendMem->head;
|
||||
if ((args->tail < args->head + NCCL_STEPS) && (args->tail < *sendHead + NCCL_STEPS) && (args->tail < args->end)) {
|
||||
int buffSlot = args->tail%NCCL_STEPS;
|
||||
int sliceSize = stepSize * args->sliceSteps;
|
||||
NCCLCHECK(ncclNetIrecv(resources->netRecvComm, localBuff+buffSlot*stepSize, sliceSize, mhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
args->tail += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
}
|
||||
int buffSize = stepSize*args->sliceSteps;
|
||||
if (resources->shared) buffSize /= SENDRECV_SLICEFACTOR;
|
||||
if (args->recvbytes < buffSize) buffSize = args->recvbytes;
|
||||
if ((args->posted < args->done + NCCL_STEPS) && (args->posted < args->end)) {
|
||||
int buffSlot = args->posted%NCCL_STEPS;
|
||||
char* ptr;
|
||||
if (resources->shared) {
|
||||
NCCLCHECK(ncclProxySharedBuffersAlloc(args->connector->comm, resources->useGdr, 1, args->channel->id, buffSize, &ptr));
|
||||
if (ptr == NULL) return ncclInternalError;
|
||||
volatile void** ptrsFifo = (volatile void**)resources->recvMem->ptrsFifo;
|
||||
ptrsFifo[buffSlot] = ptr;
|
||||
} else {
|
||||
ptr = localBuff+buffSlot*stepSize;
|
||||
}
|
||||
if (args->tail > args->head) {
|
||||
int buffSlot = args->head%NCCL_STEPS;
|
||||
int done, size;
|
||||
NCCLCHECK(ncclNetTest(args->requests[buffSlot], &done, &size));
|
||||
if (done) {
|
||||
args->head += args->sliceSteps;
|
||||
if (args->protocol == NCCL_PROTO_SIMPLE) {
|
||||
if (resources->useGdr) NCCLCHECK(ncclNetFlush(resources->netRecvComm, localBuff+buffSlot*stepSize, size, mhandle));
|
||||
resources->recvMem->tail = args->head;
|
||||
}
|
||||
args->idle = 0;
|
||||
}
|
||||
NCCLCHECK(ncclNetIrecv(resources->netRecvComm, ptr, buffSize, mhandle, args->requests+buffSlot));
|
||||
if (args->requests[buffSlot] != NULL) {
|
||||
TRACE(NCCL_NET, "recvProxy [%d/%d] posted recv request %p", args->posted, buffSlot, args->requests[buffSlot]);
|
||||
args->posted += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
} else if (resources->shared) {
|
||||
NCCLCHECK(ncclProxySharedBuffersFree(args->connector->comm, resources->useGdr, 1, args->channel->id, buffSize, ptr));
|
||||
}
|
||||
}
|
||||
if (args->head == args->end) {
|
||||
resources->step = args->end;
|
||||
args->idle = 0;
|
||||
args->state = ncclProxyOpNone;
|
||||
if (args->posted > args->received) {
|
||||
int buffSlot = args->received%NCCL_STEPS;
|
||||
int done, size;
|
||||
NCCLCHECK(ncclNetTest(args->requests[buffSlot], &done, &size));
|
||||
if (done) {
|
||||
args->received += args->sliceSteps;
|
||||
if (size > 0 && args->protocol == NCCL_PROTO_SIMPLE && resources->useGdr) {
|
||||
// Don't pass data to the GPU yet, flush first.
|
||||
volatile void** ptrsFifo = (volatile void**)resources->recvMem->ptrsFifo;
|
||||
char* ptr = resources->shared ? (char*)(ptrsFifo[buffSlot]) : localBuff+buffSlot*stepSize;
|
||||
NCCLCHECK(ncclNetIflush(resources->netRecvComm, ptr, size, mhandle, args->requests+buffSlot));
|
||||
} else {
|
||||
args->requests[buffSlot] = NULL;
|
||||
}
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
if (args->received > args->transmitted) {
|
||||
// Progress flush operations
|
||||
int buffSlot = args->transmitted%NCCL_STEPS;
|
||||
int done = 1;
|
||||
if (args->requests[buffSlot]) NCCLCHECK(ncclNetTest(args->requests[buffSlot], &done, NULL));
|
||||
if (done) {
|
||||
args->transmitted += args->sliceSteps;
|
||||
__sync_synchronize();
|
||||
resources->recvMem->tail = args->transmitted;
|
||||
args->idle = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
if (args->transmitted > args->done) {
|
||||
volatile uint64_t* sendHead = &resources->sendMem->head;
|
||||
uint64_t done = *sendHead;
|
||||
while (done > args->done &&
|
||||
// LL and LL128 can acknowledge 0-bytes send before they even happen. Don't go past what we transmitted.
|
||||
args->transmitted > args->done) {
|
||||
if (resources->shared) {
|
||||
char* ptr = (char*)resources->recvMem->ptrsFifo[args->done%NCCL_STEPS];
|
||||
NCCLCHECK(ncclProxySharedBuffersFree(args->connector->comm, resources->useGdr, 1, args->channel->id, buffSize, ptr));
|
||||
}
|
||||
args->done += args->sliceSteps;
|
||||
args->idle = 0;
|
||||
if (args->done == args->end) {
|
||||
resources->step = args->end;
|
||||
args->state = ncclProxyOpNone;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return ncclSuccess;
|
||||
|
||||
+87
-70
@@ -24,9 +24,8 @@
|
||||
#include "ibvwrap.h"
|
||||
|
||||
#define USE_RDMA_WRITE 1
|
||||
#define USE_RDMA_SEND_INLINE 0
|
||||
#define MAXNAMESIZE 64
|
||||
static char ncclIbIfName[MAX_IF_NAME_SIZE];
|
||||
static char ncclIbIfName[MAX_IF_NAME_SIZE+1];
|
||||
static union socketAddress ncclIbIfAddr;
|
||||
|
||||
static int ncclNIbDevs = -1;
|
||||
@@ -57,6 +56,8 @@ pthread_mutex_t ncclIbLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
NCCL_PARAM(IbGidIndex, "IB_GID_INDEX", 0);
|
||||
NCCL_PARAM(IbTimeout, "IB_TIMEOUT", 14);
|
||||
NCCL_PARAM(IbRetryCnt, "IB_RETRY_CNT", 7);
|
||||
NCCL_PARAM(IbPkey, "IB_PKEY", 0);
|
||||
NCCL_PARAM(IbUseInline, "IB_USE_INLINE", 0);
|
||||
NCCL_PARAM(IbSl, "IB_SL", 0);
|
||||
NCCL_PARAM(IbTc, "IB_TC", 0);
|
||||
NCCL_PARAM(IbArThreshold, "IB_AR_THRESHOLD", 8192);
|
||||
@@ -199,7 +200,7 @@ ncclResult_t ncclIbInit(ncclDebugLogger_t logFunction) {
|
||||
ncclIbDevs[d].port, ncclIbDevs[d].link == IBV_LINK_LAYER_INFINIBAND ? "IB" : "RoCE");
|
||||
}
|
||||
line[1023] = '\0';
|
||||
char addrline[1024];
|
||||
char addrline[SOCKET_NAME_MAXLEN+1];
|
||||
INFO(NCCL_INIT|NCCL_NET, "NET/IB : Using%s ; OOB %s:%s", line, ncclIbIfName, socketToString(&ncclIbIfAddr.sa, addrline));
|
||||
}
|
||||
pthread_mutex_unlock(&ncclIbLock);
|
||||
@@ -246,7 +247,7 @@ ncclResult_t ncclIbGetProperties(int dev, ncclNetProperties_t* props) {
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
#define MAX_REQUESTS 128
|
||||
#define MAX_REQUESTS NCCL_NET_MAX_REQUESTS
|
||||
|
||||
struct ncclIbQpInfo {
|
||||
uint32_t lid;
|
||||
@@ -267,18 +268,19 @@ struct ncclIbHandle {
|
||||
union socketAddress connectAddr;
|
||||
};
|
||||
|
||||
struct ncclIbVerbs {
|
||||
struct ibv_pd* pd;
|
||||
struct ibv_cq* cq;
|
||||
};
|
||||
|
||||
struct ncclIbRequest {
|
||||
int used;
|
||||
int type;
|
||||
struct ncclIbVerbs* verbs;
|
||||
int done;
|
||||
int events;
|
||||
int size;
|
||||
int free;
|
||||
};
|
||||
|
||||
struct ncclIbVerbs {
|
||||
struct ibv_pd* pd;
|
||||
struct ibv_cq* cq;
|
||||
uint64_t pad[2];
|
||||
struct ncclIbRequest reqs[MAX_REQUESTS];
|
||||
};
|
||||
|
||||
struct ncclIbListenComm {
|
||||
@@ -292,18 +294,23 @@ struct ncclIbSendFifo {
|
||||
uint32_t seq;
|
||||
uint32_t rkey;
|
||||
uint32_t ready;
|
||||
uint64_t pad[1]; // Pad FIFO element size to be 32-bytes
|
||||
};
|
||||
|
||||
struct ncclIbSendComm {
|
||||
struct ncclIbVerbs verbs;
|
||||
struct ncclIbSendFifo fifo[MAX_REQUESTS];
|
||||
struct ncclIbRequest reqs[MAX_REQUESTS];
|
||||
uint32_t fifoHead;
|
||||
int fd;
|
||||
int ready;
|
||||
struct ibv_qp* qp;
|
||||
struct ibv_mr* fifoMr;
|
||||
};
|
||||
// The SendFifo needs to be 32-byte aligned and each element needs
|
||||
// to be a 32-byte multiple, so that an entry does not get split and
|
||||
// written out of order when IB Relaxed Ordering is enabled
|
||||
static_assert((offsetof(struct ncclIbSendComm, fifo) % 32) == 0, "ncclIbSendComm fifo must be 32-byte aligned");
|
||||
static_assert((sizeof(struct ncclIbSendFifo) % 32) == 0, "ncclIbSendFifo element size must be 32-byte multiples");
|
||||
|
||||
struct ncclIbGpuFlush {
|
||||
int enabled;
|
||||
@@ -326,16 +333,17 @@ struct ncclIbRemFifo {
|
||||
struct ncclIbRecvComm {
|
||||
struct ncclIbVerbs verbs;
|
||||
struct ncclIbRemFifo remFifo;
|
||||
struct ncclIbRequest reqs[MAX_REQUESTS];
|
||||
int fd;
|
||||
int ready;
|
||||
struct ibv_qp* qp;
|
||||
struct ncclIbGpuFlush gpuFlush;
|
||||
};
|
||||
static_assert((offsetof(struct ncclIbRecvComm, remFifo) % 32) == 0, "ncclIbSendComm fifo must be 32-byte aligned");
|
||||
|
||||
ncclResult_t ncclIbInitVerbs(ibv_context* ctx, struct ncclIbVerbs* verbs) {
|
||||
NCCLCHECK(wrap_ibv_alloc_pd(&verbs->pd, ctx));
|
||||
NCCLCHECK(wrap_ibv_create_cq(&verbs->cq, ctx, MAX_REQUESTS, NULL, NULL, 0));
|
||||
// Recv requests can generate 2 completions (one for the post FIFO, one for the Recv).
|
||||
NCCLCHECK(wrap_ibv_create_cq(&verbs->cq, ctx, 2*MAX_REQUESTS, NULL, NULL, 0));
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
@@ -351,17 +359,17 @@ ncclResult_t ncclIbCreateQp(uint8_t ib_port, struct ncclIbVerbs* verbs, int acce
|
||||
qpInitAttr.send_cq = verbs->cq;
|
||||
qpInitAttr.recv_cq = verbs->cq;
|
||||
qpInitAttr.qp_type = IBV_QPT_RC;
|
||||
// We might send 2 requests per send (RDMA_WRITE+RDMA_WRITE_WITH_IMM)
|
||||
// We might send 2 messages per send (RDMA and RDMA_WITH_IMM)
|
||||
qpInitAttr.cap.max_send_wr = 2*MAX_REQUESTS;
|
||||
qpInitAttr.cap.max_recv_wr = MAX_REQUESTS;
|
||||
qpInitAttr.cap.max_send_sge = 1;
|
||||
qpInitAttr.cap.max_recv_sge = 1;
|
||||
qpInitAttr.cap.max_inline_data = 0;
|
||||
qpInitAttr.cap.max_inline_data = ncclParamIbUseInline() ? sizeof(struct ncclIbSendFifo) : 0;
|
||||
NCCLCHECK(wrap_ibv_create_qp(qp, verbs->pd, &qpInitAttr));
|
||||
struct ibv_qp_attr qpAttr;
|
||||
memset(&qpAttr, 0, sizeof(struct ibv_qp_attr));
|
||||
qpAttr.qp_state = IBV_QPS_INIT;
|
||||
qpAttr.pkey_index = 0;
|
||||
qpAttr.pkey_index = ncclParamIbPkey();
|
||||
qpAttr.port_num = ib_port;
|
||||
qpAttr.qp_access_flags = access_flags;
|
||||
NCCLCHECK(wrap_ibv_modify_qp(*qp, &qpAttr, IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT | IBV_QP_ACCESS_FLAGS));
|
||||
@@ -476,7 +484,7 @@ ncclResult_t ncclIbAccept(void* listenComm, void** recvComm) {
|
||||
socklen_t socklen = sizeof(struct sockaddr_in);
|
||||
SYSCHECKVAL(accept(lComm->fd, (struct sockaddr*)&sockaddr, &socklen), "accept", rComm->fd);
|
||||
struct ncclIbQpInfo remQpInfo;
|
||||
NCCLCHECK(socketReceive(rComm->fd, &remQpInfo, sizeof(remQpInfo)));
|
||||
NCCLCHECK(socketRecv(rComm->fd, &remQpInfo, sizeof(remQpInfo)));
|
||||
|
||||
// IB setup
|
||||
ibv_context* ctx = ncclIbDevs[lComm->dev].context;
|
||||
@@ -504,14 +512,7 @@ ncclResult_t ncclIbAccept(void* listenComm, void** recvComm) {
|
||||
NCCLCHECK(wrap_ibv_reg_mr(&rComm->remFifo.mr, rComm->verbs.pd, &rComm->remFifo.elems, sizeof(struct ncclIbSendFifo)*MAX_REQUESTS, IBV_ACCESS_REMOTE_WRITE|IBV_ACCESS_LOCAL_WRITE|IBV_ACCESS_REMOTE_READ));
|
||||
rComm->remFifo.sge.length = sizeof(struct ncclIbSendFifo);
|
||||
rComm->remFifo.sge.lkey = rComm->remFifo.mr->lkey;
|
||||
|
||||
#if USE_RDMA_SEND_INLINE
|
||||
// Determine whether the remFifo element data can be sent INLINE
|
||||
struct ibv_qp_attr attr;
|
||||
struct ibv_qp_init_attr init_attr;
|
||||
NCCLCHECK(wrap_ibv_query_qp(qp, &attr, IBV_QP_CAP, &init_attr));
|
||||
if (init_attr.cap.max_inline_data >= rComm->remFifo.sge.length) rComm->remFifo.flags = IBV_SEND_INLINE;
|
||||
#endif
|
||||
if (ncclParamIbUseInline()) rComm->remFifo.flags = IBV_SEND_INLINE;
|
||||
|
||||
// Allocate Flush dummy buffer for GPU Direct RDMA
|
||||
rComm->gpuFlush.enabled = (ncclIbGdrSupport(lComm->dev) == 0) && (ncclParamIbGdrFlushDisable() == 0) ? 1 : 0;
|
||||
@@ -548,16 +549,15 @@ ncclResult_t ncclIbAccept(void* listenComm, void** recvComm) {
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclIbGetRequest(struct ncclIbRequest* reqs, struct ncclIbRequest** req) {
|
||||
ncclResult_t ncclIbGetRequest(struct ncclIbVerbs* verbs, struct ncclIbRequest** req) {
|
||||
for (int i=0; i<MAX_REQUESTS; i++) {
|
||||
struct ncclIbRequest* r = reqs+i;
|
||||
struct ncclIbRequest* r = verbs->reqs+i;
|
||||
if (r->used == 0) {
|
||||
r->used = 1;
|
||||
r->type = 0;
|
||||
r->verbs = NULL;
|
||||
r->done = 0;
|
||||
r->verbs = verbs;
|
||||
r->events = 1;
|
||||
r->size = -1;
|
||||
r->free = 0;
|
||||
*req = r;
|
||||
return ncclSuccess;
|
||||
}
|
||||
@@ -566,6 +566,10 @@ ncclResult_t ncclIbGetRequest(struct ncclIbRequest* reqs, struct ncclIbRequest**
|
||||
*req = NULL;
|
||||
return ncclInternalError;
|
||||
}
|
||||
ncclResult_t ncclIbFreeRequest(struct ncclIbRequest* r) {
|
||||
r->used = 0;
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclSendCheck(struct ncclIbSendComm* comm) {
|
||||
struct ncclIbQpInfo remQpInfo;
|
||||
@@ -580,7 +584,6 @@ ncclResult_t ncclSendCheck(struct ncclIbSendComm* comm) {
|
||||
NCCLCHECK(ncclIbRtrQp(qp, &remQpInfo));
|
||||
NCCLCHECK(ncclIbRtsQp(qp));
|
||||
comm->ready = 1;
|
||||
|
||||
// Block until this is done. It *should* not block indefinitely.
|
||||
NCCLCHECK(socketSend(comm->fd, &comm->ready, sizeof(int)));
|
||||
|
||||
@@ -601,6 +604,7 @@ ncclResult_t ncclIbTest(void* request, int* done, int* size);
|
||||
#define REG_ALIGN (4096)
|
||||
|
||||
ncclResult_t ncclIbRegMr(void* comm, void* data, int size, int type, void** mhandle) {
|
||||
static_assert(offsetof(struct ncclIbSendComm, verbs) == offsetof(struct ncclIbRecvComm, verbs), "Send and recv comms must have verbs at the same offset");
|
||||
struct ncclIbVerbs* verbs = (struct ncclIbVerbs*)comm;
|
||||
uint64_t addr = (uint64_t)data;
|
||||
assert(size > 0);
|
||||
@@ -634,8 +638,7 @@ ncclResult_t ncclIbIsend(void* sendComm, void* data, int size, void* mhandle, vo
|
||||
if (*readyPtr == 0) { *request = NULL; return ncclSuccess; }
|
||||
|
||||
struct ncclIbRequest* req;
|
||||
NCCLCHECK(ncclIbGetRequest(comm->reqs, &req));
|
||||
req->verbs = &comm->verbs;
|
||||
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
|
||||
req->size = size;
|
||||
|
||||
struct ibv_send_wr wr;
|
||||
@@ -651,23 +654,24 @@ ncclResult_t ncclIbIsend(void* sendComm, void* data, int size, void* mhandle, vo
|
||||
wr.sg_list = &sge;
|
||||
wr.num_sge = 1;
|
||||
}
|
||||
#if USE_RDMA_WRITE == 0
|
||||
wr.opcode = IBV_WR_SEND;
|
||||
wr.send_flags = IBV_SEND_SIGNALED;
|
||||
|
||||
int useAr = 0;
|
||||
if (size > ncclParamIbArThreshold()) {
|
||||
useAr = 1;
|
||||
}
|
||||
#if USE_RDMA_WRITE
|
||||
#else
|
||||
__sync_synchronize(); // order the readyPtr load against rkey load below
|
||||
// Sanity checks to catch user collective call count/size mismatches
|
||||
// plus any potential programming errors
|
||||
if (size > slot->size || slot->size <= 0 || slot->addr == 0 || slot->rkey == 0 || slot->seq != comm->fifoHead) {
|
||||
if (size > slot->size || slot->size < 0 || slot->addr == 0 || slot->rkey == 0 || slot->seq != comm->fifoHead) {
|
||||
WARN("NET/IB : collective mismatch error local size %d remote %d addr %lx rkey %x seq %x/%x",
|
||||
size, slot->size, slot->addr, slot->rkey, slot->seq, comm->fifoHead);
|
||||
return ncclInternalError;
|
||||
}
|
||||
int useAr = 0;
|
||||
if (size > ncclParamIbArThreshold()) {
|
||||
useAr = 1;
|
||||
}
|
||||
wr.opcode = useAr ? IBV_WR_RDMA_WRITE : IBV_WR_RDMA_WRITE_WITH_IMM;
|
||||
wr.send_flags = useAr ? 0 : IBV_SEND_SIGNALED;
|
||||
wr.wr.rdma.remote_addr = slot->addr;
|
||||
wr.wr.rdma.rkey = slot->rkey;
|
||||
wr.imm_data = size; // Send the message size via imm_data
|
||||
@@ -691,7 +695,7 @@ ncclResult_t ncclIbIsend(void* sendComm, void* data, int size, void* mhandle, vo
|
||||
wr.opcode = IBV_WR_RDMA_WRITE_WITH_IMM;
|
||||
wr.sg_list = NULL;
|
||||
wr.num_sge = 0;
|
||||
wr.send_flags &= ~IBV_SEND_SIGNALED;
|
||||
wr.send_flags |= IBV_SEND_SIGNALED;
|
||||
NCCLCHECK(wrap_ibv_post_send(comm->qp, &wr, &bad_wr));
|
||||
}
|
||||
#endif
|
||||
@@ -699,28 +703,51 @@ ncclResult_t ncclIbIsend(void* sendComm, void* data, int size, void* mhandle, vo
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclIbPostFifo(struct ncclIbRecvComm* comm, uint32_t rkey, uint64_t addr, int size) {
|
||||
ncclResult_t ncclIbPostFifo(struct ncclIbRecvComm* comm, uint32_t rkey, uint64_t addr, int size, struct ncclIbRequest* req) {
|
||||
struct ibv_send_wr wr;
|
||||
memset(&wr, 0, sizeof(wr));
|
||||
struct ncclIbRequest* req;
|
||||
NCCLCHECK(ncclIbGetRequest(comm->reqs, &req));
|
||||
req->verbs = &comm->verbs;
|
||||
req->free = 1; // Not a user req ; free as soon as it is complete.
|
||||
wr.wr_id = (uint64_t)req;
|
||||
|
||||
struct ncclIbSendFifo* localElem = comm->remFifo.elems + (comm->remFifo.tail % MAX_REQUESTS);
|
||||
int slot = comm->remFifo.tail%MAX_REQUESTS;
|
||||
struct ncclIbSendFifo* localElem = comm->remFifo.elems + slot;
|
||||
localElem->addr = addr;
|
||||
localElem->rkey = rkey;
|
||||
localElem->ready = 1;
|
||||
localElem->size = size; // Sanity/Debugging
|
||||
localElem->seq = comm->remFifo.tail; // Sanity/Debugging
|
||||
wr.wr.rdma.remote_addr = comm->remFifo.addr + (comm->remFifo.tail % MAX_REQUESTS) * sizeof(struct ncclIbSendFifo);
|
||||
wr.wr.rdma.remote_addr = comm->remFifo.addr + slot*sizeof(struct ncclIbSendFifo);
|
||||
wr.wr.rdma.rkey = comm->remFifo.rkey;
|
||||
comm->remFifo.sge.addr = (uint64_t)localElem;
|
||||
wr.sg_list = &comm->remFifo.sge;
|
||||
wr.num_sge = 1;
|
||||
wr.opcode = IBV_WR_RDMA_WRITE;
|
||||
wr.send_flags = IBV_SEND_SIGNALED | comm->remFifo.flags; // IBV_SEND_INLINE
|
||||
wr.send_flags = comm->remFifo.flags; // IBV_SEND_INLINE
|
||||
|
||||
// We need to occasionally post a request with the IBV_SEND_SIGNALED flag, otherwise
|
||||
// the send queue will never empty.
|
||||
//
|
||||
// From https://www.rdmamojo.com/2014/06/30/working-unsignaled-completions/
|
||||
// "How to use Unsignaled Completion?" / "Gotchas and Pitfalls"
|
||||
// All posted Send Requested, Signaled and Unsignaled, are considered outstanding until
|
||||
// a Work Completion that they, or Send Requests that were posted after them, was polled
|
||||
// from the Completion Queue associated with the Send Queue. This means if one works with
|
||||
// a Queue Pair that was configured to work with Unsignaled Completions, he must make
|
||||
// sure that occasionally (before the Send Queue is full with outstanding Send Requests)
|
||||
// a Send Request that generate Work Completion will be posted.
|
||||
//
|
||||
// Not following this rule may lead to a case that the Send Queue is full with Send
|
||||
// Requests that won't generate Work Completion:
|
||||
//
|
||||
// - The Send Queue is full, so no new Send Requests can be posted to it
|
||||
// - The Send Queue can't be emptied, since no Work Completion can be generated anymore
|
||||
// (the reason is that no Work Completion, that can generate Work Completion that
|
||||
// polling it will empty the Send Queue, can be posted)
|
||||
// - The status of all posted Send Request is considered unknown
|
||||
//
|
||||
if (slot == 0) {
|
||||
wr.send_flags |= IBV_SEND_SIGNALED;
|
||||
wr.wr_id = (uint64_t)req;
|
||||
req->events++;
|
||||
}
|
||||
|
||||
struct ibv_send_wr* bad_wr;
|
||||
NCCLCHECK(wrap_ibv_post_send(comm->qp, &wr, &bad_wr));
|
||||
@@ -737,8 +764,7 @@ ncclResult_t ncclIbIrecv(void* recvComm, void* data, int size, void* mhandle, vo
|
||||
struct ibv_mr* mr = (struct ibv_mr*)mhandle;
|
||||
|
||||
struct ncclIbRequest* req;
|
||||
NCCLCHECK(ncclIbGetRequest(comm->reqs, &req));
|
||||
req->verbs = &comm->verbs;
|
||||
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
|
||||
req->size = size;
|
||||
|
||||
struct ibv_recv_wr wr;
|
||||
@@ -760,17 +786,16 @@ ncclResult_t ncclIbIrecv(void* recvComm, void* data, int size, void* mhandle, vo
|
||||
*request = req;
|
||||
|
||||
// Post to FIFO to notify sender
|
||||
NCCLCHECK(ncclIbPostFifo(comm, mr->rkey, (uint64_t)data, size));
|
||||
NCCLCHECK(ncclIbPostFifo(comm, mr->rkey, (uint64_t)data, size, req));
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclIbFlush(void* recvComm, void* data, int size, void* mhandle) {
|
||||
ncclResult_t ncclIbIflush(void* recvComm, void* data, int size, void* mhandle, void** request) {
|
||||
struct ncclIbRecvComm* comm = (struct ncclIbRecvComm*)recvComm;
|
||||
if (comm->gpuFlush.enabled == 0 || size == 0) return ncclSuccess;
|
||||
|
||||
struct ncclIbRequest* req;
|
||||
NCCLCHECK(ncclIbGetRequest(comm->reqs, &req));
|
||||
req->verbs = &comm->verbs;
|
||||
NCCLCHECK(ncclIbGetRequest(&comm->verbs, &req));
|
||||
struct ibv_mr* mr = (struct ibv_mr*)mhandle;
|
||||
|
||||
struct ibv_send_wr wr;
|
||||
@@ -787,11 +812,7 @@ ncclResult_t ncclIbFlush(void* recvComm, void* data, int size, void* mhandle) {
|
||||
struct ibv_send_wr* bad_wr;
|
||||
NCCLCHECK(wrap_ibv_post_send(comm->gpuFlush.qp, &wr, &bad_wr));
|
||||
|
||||
int done = 0;
|
||||
while (done == 0) {
|
||||
NCCLCHECK((ncclResult_t)ncclIbTest(req, &done, NULL));
|
||||
}
|
||||
|
||||
*request = req;
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
@@ -800,10 +821,10 @@ ncclResult_t ncclIbTest(void* request, int* done, int* size) {
|
||||
*done = 0;
|
||||
|
||||
while (1) {
|
||||
if (r->done == 1) {
|
||||
if (r->events == 0) {
|
||||
*done = 1;
|
||||
if (size) *size = r->size;
|
||||
r->used = 0;
|
||||
NCCLCHECK(ncclIbFreeRequest(r));
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
@@ -828,11 +849,7 @@ ncclResult_t ncclIbTest(void* request, int* done, int* size) {
|
||||
doneReq->size = wc->imm_data;
|
||||
#endif
|
||||
}
|
||||
doneReq->done = 1;
|
||||
if (doneReq->free == 1) {
|
||||
// This is an internal (FIFO post) req. Free it immediately.
|
||||
doneReq->used = 0;
|
||||
}
|
||||
doneReq->events--;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -887,7 +904,7 @@ ncclNet_t ncclNetIb = {
|
||||
ncclIbDeregMr,
|
||||
ncclIbIsend,
|
||||
ncclIbIrecv,
|
||||
ncclIbFlush,
|
||||
ncclIbIflush,
|
||||
ncclIbTest,
|
||||
ncclIbCloseSend,
|
||||
ncclIbCloseRecv,
|
||||
|
||||
+18
-11
@@ -48,17 +48,19 @@ ncclResult_t ncclSocketInit(ncclDebugLogger_t logFunction) {
|
||||
WARN("NET/Socket : no interface found");
|
||||
return ncclInternalError;
|
||||
} else {
|
||||
char line[1024];
|
||||
char addrline[1024];
|
||||
#define MAX_LINE_LEN (2047)
|
||||
char line[MAX_LINE_LEN+1];
|
||||
char addrline[SOCKET_NAME_MAXLEN+1];
|
||||
line[0] = '\0';
|
||||
addrline[SOCKET_NAME_MAXLEN] = '\0';
|
||||
for (int i=0; i<ncclNetIfs; i++) {
|
||||
strcpy(ncclSocketDevs[i].devName, names+i*MAX_IF_NAME_SIZE);
|
||||
memcpy(&ncclSocketDevs[i].addr, addrs+i, sizeof(union socketAddress));
|
||||
NCCLCHECK(ncclSocketGetPciPath(ncclSocketDevs[i].devName, &ncclSocketDevs[i].pciPath));
|
||||
snprintf(line+strlen(line), 1023-strlen(line), " [%d]%s:%s", i, names+i*MAX_IF_NAME_SIZE,
|
||||
snprintf(line+strlen(line), MAX_LINE_LEN-strlen(line), " [%d]%s:%s", i, names+i*MAX_IF_NAME_SIZE,
|
||||
socketToString(&addrs[i].sa, addrline));
|
||||
}
|
||||
line[1023] = '\0';
|
||||
line[MAX_LINE_LEN] = '\0';
|
||||
INFO(NCCL_INIT|NCCL_NET,"NET/Socket : Using%s", line);
|
||||
}
|
||||
}
|
||||
@@ -112,8 +114,7 @@ ncclResult_t GetSocketAddr(int dev, union socketAddress* addr) {
|
||||
|
||||
#define MAX_SOCKETS 64
|
||||
#define MAX_THREADS 16
|
||||
#define MAX_REQUESTS 128
|
||||
#define MAX_QUEUE_LEN MAX_REQUESTS
|
||||
#define MAX_REQUESTS NCCL_NET_MAX_REQUESTS
|
||||
#define MIN_CHUNKSIZE (64*1024)
|
||||
|
||||
NCCL_PARAM(SocketNsocksPerThread, "NSOCKS_PERTHREAD", -2);
|
||||
@@ -149,6 +150,7 @@ struct ncclSocketRequest {
|
||||
|
||||
struct ncclSocketTaskQueue {
|
||||
int next;
|
||||
int len;
|
||||
struct ncclSocketTask* tasks;
|
||||
};
|
||||
|
||||
@@ -188,7 +190,7 @@ void* persistentSocketThread(void *args_) {
|
||||
while (1) {
|
||||
int idle = 1;
|
||||
int mark = myQueue->next; // mark newest task seen
|
||||
for (int i=0; i<MAX_QUEUE_LEN; i+=nSocksPerThread) {
|
||||
for (int i=0; i<myQueue->len; i+=nSocksPerThread) {
|
||||
int repeat;
|
||||
do {
|
||||
repeat = 0;
|
||||
@@ -363,7 +365,11 @@ ncclResult_t ncclSocketGetTask(struct ncclSocketComm* comm, int op, void* data,
|
||||
struct ncclSocketTaskQueue* queue = &res->threadTaskQueue;
|
||||
// create helper threads and prepare per-thread task queue
|
||||
if (queue->tasks == NULL) {
|
||||
NCCLCHECK(ncclCalloc(&queue->tasks, MAX_QUEUE_LEN));
|
||||
// each request can be divided up to nSocks tasks, and
|
||||
// these tasks are distributed to nThreads threads,
|
||||
// we need to make sure each thread queue has enough slots for MAX_REQUESTS
|
||||
queue->len = MAX_REQUESTS * DIVUP(comm->nSocks, comm->nThreads);
|
||||
NCCLCHECK(ncclCalloc(&queue->tasks, queue->len));
|
||||
queue->next = 0;
|
||||
res->comm = comm;
|
||||
pthread_mutex_init(&res->threadLock, NULL);
|
||||
@@ -382,7 +388,7 @@ ncclResult_t ncclSocketGetTask(struct ncclSocketComm* comm, int op, void* data,
|
||||
r->used = 1;
|
||||
*req = r;
|
||||
pthread_mutex_lock(&res->threadLock);
|
||||
queue->next = (queue->next+1)%MAX_QUEUE_LEN;
|
||||
queue->next = (queue->next+1)%queue->len;
|
||||
res->state = start;
|
||||
pthread_cond_signal(&res->threadCond);
|
||||
pthread_mutex_unlock(&res->threadLock);
|
||||
@@ -420,6 +426,7 @@ ncclResult_t ncclSocketTest(void* request, int* done, int* size) {
|
||||
// divide into subtasks
|
||||
int chunkOffset = 0, i = 0;
|
||||
if (r->comm->nSocks > 0) {
|
||||
// each request can be divided up to nSocks tasks
|
||||
int taskSize = std::max(MIN_CHUNKSIZE, DIVUP(r->size, r->comm->nSocks));
|
||||
while (chunkOffset < r->size) {
|
||||
int chunkSize = std::min(taskSize, r->size-chunkOffset);
|
||||
@@ -477,7 +484,7 @@ ncclResult_t ncclSocketIrecv(void* recvComm, void* data, int size, void* mhandle
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclSocketFlush(void* recvComm, void* data, int size, void* mhandle) {
|
||||
ncclResult_t ncclSocketIflush(void* recvComm, void* data, int size, void* mhandle, void** request) {
|
||||
// We don't support CUDA pointers, so we don't need a flush operation
|
||||
return ncclInternalError;
|
||||
}
|
||||
@@ -526,7 +533,7 @@ ncclNet_t ncclNetSocket = {
|
||||
ncclSocketDeregMr,
|
||||
ncclSocketIsend,
|
||||
ncclSocketIrecv,
|
||||
ncclSocketFlush,
|
||||
ncclSocketIflush,
|
||||
ncclSocketTest,
|
||||
ncclSocketClose,
|
||||
ncclSocketClose,
|
||||
|
||||
+104
-114
@@ -7,24 +7,29 @@
|
||||
#include "comm.h"
|
||||
#include "graph.h"
|
||||
#include "utils.h"
|
||||
#include "bootstrap.h"
|
||||
|
||||
struct p2pConnectInfo {
|
||||
int direct;
|
||||
int rank;
|
||||
int read;
|
||||
union {
|
||||
void* directPtr;
|
||||
cudaIpcMemHandle_t devIpc;
|
||||
};
|
||||
void* directPtr;
|
||||
cudaIpcMemHandle_t devIpc;
|
||||
};
|
||||
|
||||
struct p2pSendResources {
|
||||
struct ncclSendMem* devMem;
|
||||
void* ipcPtr;
|
||||
int remoteId;
|
||||
int memRank;
|
||||
void* bootstrap;
|
||||
};
|
||||
|
||||
struct p2pRecvResources {
|
||||
struct ncclRecvMem* devMem;
|
||||
void* ipcPtr;
|
||||
int remoteId;
|
||||
int memRank;
|
||||
void* bootstrap;
|
||||
};
|
||||
|
||||
#include <sys/types.h>
|
||||
@@ -55,9 +60,10 @@ ncclResult_t p2pCanConnect(int* ret, struct ncclTopoSystem* topo, struct ncclTop
|
||||
}
|
||||
|
||||
// Check topology / p2p level.
|
||||
int read;
|
||||
NCCLCHECK(ncclTopoCheckP2p(topo, info1->busId, info2->busId, ret, &read));
|
||||
int intermediateRank;
|
||||
NCCLCHECK(ncclTopoCheckP2p(topo, info1->busId, info2->busId, ret, NULL, &intermediateRank));
|
||||
if (*ret == 0) return ncclSuccess;
|
||||
if (intermediateRank != -1) return ncclSuccess;
|
||||
|
||||
// Convert the peer's busId into a local cudaDev index (cf. CUDA_VISIBLE_DEVICES)
|
||||
int cudaDev1 = busIdToCudaDev(info1->busId);
|
||||
@@ -100,145 +106,134 @@ ncclResult_t p2pCanConnect(int* ret, struct ncclTopoSystem* topo, struct ncclTop
|
||||
// Setting this to non zero causes P2P to use Reads rather than Writes
|
||||
NCCL_PARAM(P2pReadEnable, "P2P_READ_ENABLE", -2);
|
||||
|
||||
static int p2pUseRead(struct ncclTopoSystem* topo, struct ncclPeerInfo* info1, struct ncclPeerInfo* info2) {
|
||||
int readEnable = ncclParamP2pReadEnable();
|
||||
if (readEnable != -2) return readEnable;
|
||||
|
||||
int p2p, read;
|
||||
static ncclResult_t p2pGetInfo(struct ncclTopoSystem* topo, struct ncclPeerInfo* info1, struct ncclPeerInfo* info2, int* read, int* intermediateRank) {
|
||||
int p2p;
|
||||
// Queries the topology to see if the GPUs are Ampere and
|
||||
// connected via NVLink, if so we enable P2P Read by default
|
||||
NCCLCHECK(ncclTopoCheckP2p(topo, info1->busId, info2->busId, &p2p, &read));
|
||||
NCCLCHECK(ncclTopoCheckP2p(topo, info1->busId, info2->busId, &p2p, read, intermediateRank));
|
||||
|
||||
return read;
|
||||
int readEnable = ncclParamP2pReadEnable();
|
||||
if (readEnable != -2) *read = readEnable;
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
/* Send: Create and return connect structures for this peer to connect to me */
|
||||
ncclResult_t p2pSendSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo,
|
||||
struct ncclConnect* connectInfo, struct ncclConnector* send, int channelId) {
|
||||
|
||||
struct p2pSendResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
send->transportResources = resources;
|
||||
int useRead = p2pUseRead(topo, myInfo, peerInfo);
|
||||
int sendSize = sizeof(struct ncclSendMem);
|
||||
// For P2P Read the SIMPLE buffer is tagged on the end of the ncclSendMem structure
|
||||
if (useRead) sendSize += send->comm->buffSizes[NCCL_PROTO_SIMPLE];
|
||||
ALIGN_SIZE(sendSize, CUDA_IPC_MIN);
|
||||
NCCLCHECK(ncclCudaCalloc((char**)&resources->devMem, sendSize));
|
||||
|
||||
struct p2pConnectInfo info;
|
||||
info.read = useRead;
|
||||
const char* useReadStr = info.read ? "/read" : "";
|
||||
static ncclResult_t p2pMap(struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct p2pConnectInfo* p2pInfo, void** devMem, void** ipcPtr) {
|
||||
if (myInfo->pidHash == peerInfo->pidHash) {
|
||||
info.direct = 1;
|
||||
info.directPtr = resources->devMem;
|
||||
if (myInfo->cudaDev == peerInfo->cudaDev) {
|
||||
INFO(NCCL_INIT|NCCL_P2P,"Channel %02d : %d[%d] -> %d[%d] via P2P/common device%s",
|
||||
channelId, myInfo->rank, myInfo->cudaDev, peerInfo->rank, peerInfo->cudaDev, useReadStr);
|
||||
return ncclInternalError;
|
||||
} else {
|
||||
if (peerInfo->cudaDev != myInfo->cudaDev) {
|
||||
// Enable P2P access
|
||||
cudaError_t err = cudaDeviceEnablePeerAccess(peerInfo->cudaDev, 0);
|
||||
if (err == cudaErrorPeerAccessAlreadyEnabled) {
|
||||
cudaGetLastError();
|
||||
} else if (err != cudaSuccess) {
|
||||
WARN("failed to peer with device %d(=%lx): %d %s",
|
||||
peerInfo->cudaDev, peerInfo->busId, err, cudaGetErrorString(err));
|
||||
peerInfo->cudaDev, peerInfo->busId, err, cudaGetErrorString(err));
|
||||
return ncclInternalError;
|
||||
}
|
||||
INFO(NCCL_INIT|NCCL_P2P,"Channel %02d : %d[%lx] -> %d[%lx] via P2P/direct pointer%s",
|
||||
}
|
||||
*devMem = p2pInfo->directPtr;
|
||||
*ipcPtr = NULL;
|
||||
} else {
|
||||
CUDACHECK(cudaIpcOpenMemHandle(devMem, p2pInfo->devIpc, cudaIpcMemLazyEnablePeerAccess));
|
||||
*ipcPtr = *devMem;
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
/* Send: Create and return connect structures for this peer to connect to me */
|
||||
ncclResult_t p2pSendSetup(struct ncclComm* comm, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo,
|
||||
struct ncclConnect* connectInfo, struct ncclConnector* send, int channelId) {
|
||||
|
||||
struct p2pSendResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
send->transportResources = resources;
|
||||
int useRead, intermediateRank;
|
||||
NCCLCHECK(p2pGetInfo(comm->topo, myInfo, peerInfo, &useRead, &intermediateRank));
|
||||
int sendSize = sizeof(struct ncclSendMem);
|
||||
// For P2P Read the SIMPLE buffer is tagged on the end of the ncclSendMem structure
|
||||
if (useRead) sendSize += send->comm->buffSizes[NCCL_PROTO_SIMPLE];
|
||||
ALIGN_SIZE(sendSize, CUDA_IPC_MIN);
|
||||
|
||||
struct p2pConnectInfo info;
|
||||
info.read = useRead;
|
||||
const char* useReadStr = info.read ? "/read" : "";
|
||||
|
||||
resources->remoteId = -1;
|
||||
resources->bootstrap = comm->bootstrap;
|
||||
if (intermediateRank == -1) {
|
||||
NCCLCHECK(ncclCudaCalloc((char**)&info.directPtr, sendSize));
|
||||
info.rank = myInfo->rank;
|
||||
if (myInfo->pidHash == peerInfo->pidHash) {
|
||||
if (useRead == 0) send->conn.direct |= NCCL_DIRECT_GPU;
|
||||
INFO(NCCL_INIT|NCCL_P2P, "Channel %02d : %d[%lx] -> %d[%lx] via P2P/direct pointer%s",
|
||||
channelId, myInfo->rank, myInfo->busId, peerInfo->rank, peerInfo->busId, useReadStr);
|
||||
} else {
|
||||
CUDACHECK(cudaIpcGetMemHandle(&info.devIpc, info.directPtr));
|
||||
INFO(NCCL_INIT|NCCL_P2P,"Channel %02d : %d[%lx] -> %d[%lx] via P2P/IPC%s",
|
||||
channelId, myInfo->rank, myInfo->busId, peerInfo->rank, peerInfo->busId, useReadStr);
|
||||
}
|
||||
} else {
|
||||
// Convert the peer's busId into a local cudaDev index (cf. CUDA_VISIBLE_DEVICES)
|
||||
int peerCudaDev = busIdToCudaDev(peerInfo->busId);
|
||||
info.direct = 0;
|
||||
// Map IPC and enable P2P access
|
||||
cudaError_t err = cudaIpcGetMemHandle(&info.devIpc, (void*)resources->devMem);
|
||||
if (err != cudaSuccess) {
|
||||
WARN("rank %d failed to get CUDA IPC handle to device %d(=%lx) : %d %s",
|
||||
myInfo->rank, peerCudaDev, peerInfo->busId, err, cudaGetErrorString(err));
|
||||
return ncclInternalError;
|
||||
}
|
||||
INFO(NCCL_INIT|NCCL_P2P,"Channel %02d : %d[%lx] -> %d[%lx] via P2P/IPC%s",
|
||||
channelId, myInfo->rank, myInfo->busId, peerInfo->rank, peerInfo->busId, useReadStr);
|
||||
//TRACE_DUMP_IPC(&info.devIpc);
|
||||
NCCLCHECK(bootstrapRemAlloc(sendSize, intermediateRank, resources->bootstrap, &resources->remoteId, &info.devIpc, &info.directPtr));
|
||||
info.rank = intermediateRank;
|
||||
INFO(NCCL_INIT|NCCL_P2P, "Channel %02d : %d[%lx] -> %d[%lx] via P2P/indirect/%d[%lx]%s",
|
||||
channelId, myInfo->rank, myInfo->busId, peerInfo->rank, peerInfo->busId, intermediateRank,
|
||||
comm->peerInfo[intermediateRank].busId, useReadStr);
|
||||
}
|
||||
resources->memRank = info.rank;
|
||||
|
||||
NCCLCHECK(p2pMap(myInfo, comm->peerInfo+info.rank, &info, (void**)&resources->devMem, &resources->ipcPtr));
|
||||
|
||||
static_assert(sizeof(struct p2pConnectInfo) <= sizeof(struct ncclConnect), "p2p Connect Info is too big");
|
||||
memcpy(connectInfo, &info, sizeof(struct p2pConnectInfo));
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
/* Create and return connect structures for this peer to connect to me */
|
||||
ncclResult_t p2pRecvSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo,
|
||||
ncclResult_t p2pRecvSetup(struct ncclComm* comm, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo,
|
||||
struct ncclConnect* connectInfo, struct ncclConnector * recv, int channelId) {
|
||||
|
||||
struct p2pRecvResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
recv->transportResources = resources;
|
||||
int useRead = p2pUseRead(topo, myInfo, peerInfo);
|
||||
int useRead, intermediateRank;
|
||||
NCCLCHECK(p2pGetInfo(comm->topo, myInfo, peerInfo, &useRead, &intermediateRank));
|
||||
int recvSize = offsetof(struct ncclRecvMem, buff);
|
||||
// For P2P Read the SIMPLE buffer is tagged on the end of the ncclSendMem structure
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) if (!(useRead && p == NCCL_PROTO_SIMPLE)) recvSize += recv->comm->buffSizes[p];
|
||||
ALIGN_SIZE(recvSize, CUDA_IPC_MIN);
|
||||
NCCLCHECK(ncclCudaCalloc((char**)&resources->devMem, recvSize));
|
||||
|
||||
struct p2pConnectInfo info;
|
||||
info.read = useRead;
|
||||
if (myInfo->pidHash == peerInfo->pidHash) {
|
||||
info.direct = 1;
|
||||
info.directPtr = resources->devMem;
|
||||
if (myInfo->cudaDev == peerInfo->cudaDev) {
|
||||
TRACE(NCCL_INIT|NCCL_P2P,"%d <- %d via P2P/common device", myInfo->rank, peerInfo->rank);
|
||||
|
||||
resources->remoteId = -1;
|
||||
resources->bootstrap = comm->bootstrap;
|
||||
if (intermediateRank == -1) {
|
||||
NCCLCHECK(ncclCudaCalloc((char**)&info.directPtr, recvSize));
|
||||
info.rank = myInfo->rank;
|
||||
if (myInfo->pidHash == peerInfo->pidHash) {
|
||||
if (useRead == 0) recv->conn.direct |= NCCL_DIRECT_GPU;
|
||||
} else {
|
||||
// Enable P2P access
|
||||
cudaError_t err = cudaDeviceEnablePeerAccess(peerInfo->cudaDev, 0);
|
||||
if (err == cudaErrorPeerAccessAlreadyEnabled) {
|
||||
cudaGetLastError();
|
||||
} else if (err != cudaSuccess) {
|
||||
WARN("failed to peer with device %d(=%lx): %d %s",
|
||||
peerInfo->cudaDev, peerInfo->busId, err, cudaGetErrorString(err));
|
||||
return ncclInternalError;
|
||||
}
|
||||
TRACE(NCCL_INIT|NCCL_P2P,"Channel %02d : %d[%lx] <- %d[%lx] via P2P/direct pointer", channelId, myInfo->rank, myInfo->busId, peerInfo->rank, peerInfo->busId);
|
||||
CUDACHECK(cudaIpcGetMemHandle(&info.devIpc, info.directPtr));
|
||||
}
|
||||
} else {
|
||||
// Convert the peer's busId into a local cudaDev index (cf. CUDA_VISIBLE_DEVICES)
|
||||
int peerCudaDev = busIdToCudaDev(peerInfo->busId);
|
||||
info.direct = 0;
|
||||
// Map IPC and enable P2P access
|
||||
cudaError_t err = cudaIpcGetMemHandle(&info.devIpc, (void*)resources->devMem);
|
||||
if (err != cudaSuccess) {
|
||||
WARN("rank %d failed to get CUDA IPC handle to device %d(=%lx) : %d %s",
|
||||
myInfo->rank, peerCudaDev, peerInfo->busId, err, cudaGetErrorString(err));
|
||||
return ncclInternalError;
|
||||
}
|
||||
TRACE(NCCL_INIT|NCCL_P2P,"Channel %02d : %d[%lx] <- %d[%lx] via P2P/IPC", channelId, myInfo->rank, myInfo->busId, peerInfo->rank, peerInfo->busId);
|
||||
//TRACE_DUMP_IPC(&info.devIpc);
|
||||
NCCLCHECK(bootstrapRemAlloc(recvSize, intermediateRank, resources->bootstrap, &resources->remoteId, &info.devIpc, &info.directPtr));
|
||||
info.rank = intermediateRank;
|
||||
}
|
||||
resources->memRank = info.rank;
|
||||
|
||||
NCCLCHECK(p2pMap(myInfo, comm->peerInfo+info.rank, &info, (void**)&resources->devMem, &resources->ipcPtr));
|
||||
|
||||
static_assert(sizeof(struct p2pConnectInfo) <= sizeof(struct ncclConnect), "p2p Connect Info is too big");
|
||||
memcpy(connectInfo, &info, sizeof(struct p2pConnectInfo));
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
/* Connect/Send to this peer */
|
||||
static ncclResult_t p2pSendConnect(struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* send) {
|
||||
static ncclResult_t p2pSendConnect(struct ncclComm* comm, struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* send) {
|
||||
struct p2pSendResources* resources = (struct p2pSendResources*)send->transportResources;
|
||||
struct ncclRecvMem* remDevMem;
|
||||
struct p2pConnectInfo* info = (struct p2pConnectInfo*)connectInfo;
|
||||
if (info->direct) {
|
||||
remDevMem = (struct ncclRecvMem*)(info->directPtr);
|
||||
if (info->read == 0) send->conn.direct |= NCCL_DIRECT_GPU;
|
||||
} else {
|
||||
//TRACE_DUMP_IPC(&info->devIpc);
|
||||
cudaError_t err = cudaIpcOpenMemHandle(&resources->ipcPtr, info->devIpc, cudaIpcMemLazyEnablePeerAccess);
|
||||
remDevMem = (struct ncclRecvMem*)resources->ipcPtr;
|
||||
if (err != cudaSuccess) {
|
||||
WARN("failed to open CUDA IPC handle : %d %s",
|
||||
err, cudaGetErrorString(err));
|
||||
return ncclUnhandledCudaError;
|
||||
}
|
||||
}
|
||||
|
||||
NCCLCHECK(p2pMap(comm->peerInfo+rank, comm->peerInfo+info->rank, info, (void**)&remDevMem, &resources->ipcPtr));
|
||||
|
||||
int offset = 0;
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
@@ -257,26 +252,12 @@ static ncclResult_t p2pSendConnect(struct ncclConnect* connectInfo, int nranks,
|
||||
}
|
||||
|
||||
/* Connect/Recv from this peer */
|
||||
ncclResult_t p2pRecvConnect(struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* recv) {
|
||||
ncclResult_t p2pRecvConnect(struct ncclComm* comm, struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* recv) {
|
||||
struct p2pRecvResources* resources = (struct p2pRecvResources*)recv->transportResources;
|
||||
struct ncclSendMem* remDevMem;
|
||||
struct p2pConnectInfo* info = (struct p2pConnectInfo*)connectInfo;
|
||||
if (info->direct) {
|
||||
remDevMem = (struct ncclSendMem*)(info->directPtr);
|
||||
if (info->read == 0) {
|
||||
recv->conn.direct |= NCCL_DIRECT_GPU;
|
||||
recv->conn.ptrExchange = &remDevMem->ptrExchange;
|
||||
}
|
||||
} else {
|
||||
//TRACE_DUMP_IPC(&info->devIpc);
|
||||
cudaError_t err = cudaIpcOpenMemHandle(&resources->ipcPtr, info->devIpc, cudaIpcMemLazyEnablePeerAccess);
|
||||
remDevMem = (struct ncclSendMem*)resources->ipcPtr;
|
||||
if (err != cudaSuccess) {
|
||||
WARN("failed to open CUDA IPC handle : %d %s",
|
||||
err, cudaGetErrorString(err));
|
||||
return ncclUnhandledCudaError;
|
||||
}
|
||||
}
|
||||
|
||||
NCCLCHECK(p2pMap(comm->peerInfo+rank, comm->peerInfo+info->rank, info, (void**)&remDevMem, &resources->ipcPtr));
|
||||
|
||||
int offset = 0;
|
||||
for (int p=0; p<NCCL_NUM_PROTOCOLS; p++) {
|
||||
@@ -290,6 +271,7 @@ ncclResult_t p2pRecvConnect(struct ncclConnect* connectInfo, int nranks, int ran
|
||||
}
|
||||
recv->conn.tail = &resources->devMem->tail;
|
||||
recv->conn.head = &remDevMem->head;
|
||||
recv->conn.ptrExchange = &remDevMem->ptrExchange;
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
@@ -297,6 +279,10 @@ ncclResult_t p2pSendFree(void* resources) {
|
||||
struct p2pSendResources* sendRes = (struct p2pSendResources*)resources;
|
||||
if (sendRes->ipcPtr)
|
||||
CUDACHECK(cudaIpcCloseMemHandle(sendRes->ipcPtr));
|
||||
if (sendRes->remoteId != -1) {
|
||||
NCCLCHECK(bootstrapRemFree(sendRes->remoteId, sendRes->memRank, sendRes->bootstrap));
|
||||
sendRes->devMem = NULL;
|
||||
}
|
||||
CUDACHECK(cudaFree(sendRes->devMem));
|
||||
free(sendRes);
|
||||
return ncclSuccess;
|
||||
@@ -306,6 +292,10 @@ ncclResult_t p2pRecvFree(void* resources) {
|
||||
struct p2pRecvResources* recvRes = (struct p2pRecvResources*)resources;
|
||||
if (recvRes->ipcPtr)
|
||||
CUDACHECK(cudaIpcCloseMemHandle(recvRes->ipcPtr));
|
||||
if (recvRes->remoteId != -1) {
|
||||
NCCLCHECK(bootstrapRemFree(recvRes->remoteId, recvRes->memRank, recvRes->bootstrap));
|
||||
recvRes->devMem = NULL;
|
||||
}
|
||||
CUDACHECK(cudaFree(recvRes->devMem));
|
||||
free(recvRes);
|
||||
return ncclSuccess;
|
||||
|
||||
@@ -57,7 +57,7 @@ ncclResult_t shmCanConnect(int* ret, struct ncclTopoSystem* topo, struct ncclTop
|
||||
#define MAX_SHM_NAME_LEN 1024
|
||||
|
||||
/* Create and return connect structures for this peer to connect to me */
|
||||
ncclResult_t shmSendSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* send, int channelId) {
|
||||
ncclResult_t shmSendSetup(struct ncclComm* comm, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* send, int channelId) {
|
||||
|
||||
struct shmSendResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
@@ -81,7 +81,7 @@ ncclResult_t shmSendSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* gra
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t shmRecvSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* recv, int channelId) {
|
||||
ncclResult_t shmRecvSetup(struct ncclComm* comm, struct ncclTopoGraph* graph, struct ncclPeerInfo* myInfo, struct ncclPeerInfo* peerInfo, struct ncclConnect* connectInfo, struct ncclConnector* recv, int channelId) {
|
||||
struct shmRecvResources* resources;
|
||||
NCCLCHECK(ncclCalloc(&resources, 1));
|
||||
recv->transportResources = resources;
|
||||
@@ -106,7 +106,7 @@ ncclResult_t shmRecvSetup(struct ncclTopoSystem* topo, struct ncclTopoGraph* gra
|
||||
}
|
||||
|
||||
/* Connect to this peer */
|
||||
ncclResult_t shmSendConnect(struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* send) {
|
||||
ncclResult_t shmSendConnect(struct ncclComm* comm, struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* send) {
|
||||
// Setup device pointers
|
||||
struct shmConnectInfo* info = (struct shmConnectInfo*)connectInfo;
|
||||
struct shmSendResources* resources = (struct shmSendResources*)send->transportResources;
|
||||
@@ -131,7 +131,7 @@ ncclResult_t shmSendConnect(struct ncclConnect* connectInfo, int nranks, int ran
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t shmRecvConnect(struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* recv) {
|
||||
ncclResult_t shmRecvConnect(struct ncclComm* comm, struct ncclConnect* connectInfo, int nranks, int rank, struct ncclConnector* recv) {
|
||||
// Setup device pointers
|
||||
struct shmRecvResources* resources = (struct shmRecvResources*)recv->transportResources;
|
||||
struct shmConnectInfo* info = (struct shmConnectInfo*)connectInfo;
|
||||
|
||||
Reference in New Issue
Block a user