2020-05-12 14:40:18 -07:00
/*************************************************************************
* Copyright (c) 2016-2020, NVIDIA CORPORATION. All rights reserved.
*
* See LICENSE.txt for license information
************************************************************************/
#include "comm.h"
#include "info.h"
2020-09-04 14:35:05 -07:00
#include "graph.h"
2020-05-12 14:40:18 -07:00
#include "collectives.h"
2021-03-05 19:59:41 -08:00
enum { proxyRecv = 0 , proxySend = 1 , p2pProxyRecv = 2 , p2pProxySend = 3 };
2020-05-12 14:40:18 -07:00
static bool NeedProxy ( int type , int pattern , int root , struct ncclRing * ring , int nranks ) {
if ( pattern == ncclPatternRing || pattern == ncclPatternRingTwice ) return true ;
/* In chains, one rank does not need a proxy. Let's figure out which one it is */
// Which index in the reorganized rings should we compare root against */
const int myrank = 0 , nextrank = 1 , prevrank = nranks - 1 ;
int index = pattern == ncclPatternPipelineFrom ?
/* no recv / no send if root = */
2020-09-04 14:35:05 -07:00
/* bcast */ ( type == proxyRecv ? myrank : nextrank ) :
/* reduce */ ( type == proxyRecv ? prevrank : myrank );
2020-05-12 14:40:18 -07:00
int rank = ring -> userRanks [ index ];
return ( root != rank );
}
2020-09-04 14:35:05 -07:00
#define PROXYARGS_ALLOCATE_SIZE 128
2020-05-12 14:40:18 -07:00
struct ncclProxyPool {
struct ncclProxyPool * next ;
struct ncclProxyArgs elems [ PROXYARGS_ALLOCATE_SIZE ];
};
static ncclResult_t allocateArgs ( struct ncclComm * comm , struct ncclProxyArgs ** argsptr ) {
struct ncclProxyState * state = & comm -> proxyState ;
struct ncclProxyArgs * elem ;
2020-09-04 14:35:05 -07:00
pthread_mutex_lock ( & state -> poolMutex );
2020-05-12 14:40:18 -07:00
if ( state -> pool == NULL ) {
// Allocate a new pool of elements
struct ncclProxyPool * newPool ;
NCCLCHECK ( ncclCalloc ( & newPool , 1 ));
struct ncclProxyArgs * newElems = newPool -> elems ;
// Chain newly allocated elements
for ( int i = 0 ; i < PROXYARGS_ALLOCATE_SIZE ; i ++ ) {
if ( i + 1 < PROXYARGS_ALLOCATE_SIZE ) newElems [ i ]. next = newElems + i + 1 ;
}
// Add them all to the pool list
state -> pool = newElems ;
// Save the pool memory block for later resource release
newPool -> next = state -> pools ;
state -> pools = newPool ;
}
elem = state -> pool ;
state -> pool = state -> pool -> next ;
2020-09-04 14:35:05 -07:00
pthread_mutex_unlock ( & state -> poolMutex );
elem -> next = elem -> nextPeer = elem -> nextGroup = NULL ;
2020-05-12 14:40:18 -07:00
* argsptr = elem ;
return ncclSuccess ;
}
2020-09-04 14:35:05 -07:00
//#define DEBUG_PROXY 1
#ifdef DEBUG_PROXY
#define DEBUG_PROXY_PRINT printf
#else
#define DEBUG_PROXY_PRINT(...)
#endif
#define OP_INDEX(op) ((op) ? (op)-state->pools->elems : -1)
#define OP_SEEN 0x100000
ncclResult_t dumpProxyState ( struct ncclProxyState * state ) {
#ifdef DEBUG_PROXY
struct ncclProxyArgs * op = state -> ops ;
while ( op ) {
if ( op -> idle & OP_SEEN ) {
2021-02-09 15:34:08 -08:00
WARN ( "Active list loop at element %ld" , OP_INDEX ( op ));
2020-09-04 14:35:05 -07:00
}
op -> idle |= OP_SEEN ;
printf ( "[%ld]" , OP_INDEX ( op ));
if ( op -> nextPeer ) {
printf ( "(%ld)" , OP_INDEX ( op -> nextPeer ));
struct ncclProxyArgs * n = op -> nextPeer ;
n -> idle |= OP_SEEN ;
while ( n -> nextGroup || n -> nextPeer ) {
n = n -> nextGroup ? n -> nextGroup : n -> nextPeer ;
n -> idle |= OP_SEEN ;
}
}
if ( op -> nextGroup ) {
printf ( "--G->" );
op = op -> nextGroup ;
} else {
printf ( "--N->" );
op = op -> next ;
}
}
printf ( "[X] \n " );
struct ncclProxyArgs * free = state -> pool ;
while ( free ) {
if ( free -> idle & OP_SEEN ) {
2021-02-09 15:34:08 -08:00
WARN ( "Free list loop at element %ld" , OP_INDEX ( free ));
2020-09-04 14:35:05 -07:00
}
free -> idle |= OP_SEEN ;
free = free -> next ;
}
struct ncclProxyPool * p = state -> pools ;
int i = 0 ;
while ( p ) {
for ( int e = 0 ; e < PROXYARGS_ALLOCATE_SIZE ; e ++ ) {
if (( p -> elems [ e ]. idle & OP_SEEN ) == 0 ) {
2021-02-09 15:34:08 -08:00
WARN ( "Element %d of pool %d has been lost" , e , i );
2020-09-04 14:35:05 -07:00
struct ncclProxyArgs * free = state -> pool ;
printf ( "Free list " );
while ( free ) {
printf ( "--> %ld " , OP_INDEX ( free ));
free = free -> next ;
}
printf ( " \n " );
return ncclInternalError ;
}
p -> elems [ e ]. idle -= OP_SEEN ;
}
p = p -> next ;
i ++ ;
}
#endif
return ncclSuccess ;
}
static ncclResult_t ProxyAppend ( struct ncclProxyState * state , struct ncclProxyArgs * args , int shared ) {
struct ncclProxyArgs * proxyAppend = * args -> proxyAppendPtr ;
if ( proxyAppend ) {
if ( shared && proxyAppend -> opCount == args -> opCount ) {
args -> next = proxyAppend -> next ;
proxyAppend -> next = NULL ;
proxyAppend -> nextGroup = args ;
DEBUG_PROXY_PRINT ( "Insert %5ld (%d/%5ld/%5ld) as group, prevGroup %5ld, next %5ld : \n " , OP_INDEX ( args ), shared , proxyAppend -> opCount , args -> opCount , OP_INDEX ( proxyAppend ), OP_INDEX ( args -> next ));
} else {
proxyAppend -> nextPeer = args ;
DEBUG_PROXY_PRINT ( "Insert %5ld (%d/%5ld/%5ld) as nextPeer of %5ld : \n " , OP_INDEX ( args ), shared , proxyAppend -> opCount , args -> opCount , OP_INDEX ( proxyAppend ));
}
} else {
// Nothing running for that peer. Add to the list
2020-05-12 14:40:18 -07:00
if ( state -> ops == NULL ) {
// Create the list
2020-09-04 14:35:05 -07:00
DEBUG_PROXY_PRINT ( "Insert %5ld (%d/%5ld) as first element : \n " , OP_INDEX ( args ), shared , args -> opCount );
2020-05-12 14:40:18 -07:00
state -> ops = args ;
} else {
2020-09-04 14:35:05 -07:00
// Append element at the end of the list
struct ncclProxyArgs * last = state -> ops ;
while ( last -> nextGroup || last -> next ) last = last -> nextGroup ? last -> nextGroup : last -> next ;
last -> next = args ;
DEBUG_PROXY_PRINT ( "Insert %5ld (%d/%5ld) as last element : \n " , OP_INDEX ( args ), shared , args -> opCount );
2020-05-12 14:40:18 -07:00
}
}
2020-09-04 14:35:05 -07:00
* ( args -> proxyAppendPtr ) = args ;
return ncclSuccess ;
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
static ncclResult_t SaveProxy ( int type , int peer , struct ncclProxyArgs * args ) {
2020-05-12 14:40:18 -07:00
if ( peer < 0 ) return ncclSuccess ;
struct ncclPeer * peerComm = args -> channel -> peers + peer ;
2021-03-05 19:59:41 -08:00
struct ncclConnector * connector = type < p2pProxyRecv ? ( type == proxyRecv ? & peerComm -> recv : & peerComm -> send )
: ( type == p2pProxyRecv ? & peerComm -> p2pRecv : & peerComm -> p2pSend );
2020-05-12 14:40:18 -07:00
if ( connector -> transportComm == NULL ) {
2021-02-09 15:34:08 -08:00
WARN ( "[%d] Error no transport for %s peer %d on channel %d" , connector -> comm -> rank ,
2021-03-05 19:59:41 -08:00
type < p2pProxyRecv ? ( type == proxyRecv ? "recv" : "send" ) : ( type == p2pProxyRecv ? "p2pRecv" : "p2pSend" ),
peer , args -> channel -> id );
2020-05-12 14:40:18 -07:00
return ncclInternalError ;
}
if ( connector -> transportComm -> proxy == NULL ) return ncclSuccess ;
2020-09-04 14:35:05 -07:00
struct ncclProxyState * state = & connector -> comm -> proxyState ;
2020-05-12 14:40:18 -07:00
struct ncclProxyArgs * op ;
NCCLCHECK ( allocateArgs ( connector -> comm , & op ));
memcpy ( op , args , sizeof ( struct ncclProxyArgs ));
op -> connector = connector ;
op -> progress = connector -> transportComm -> proxy ;
op -> state = ncclProxyOpReady ;
2020-09-04 14:35:05 -07:00
op -> proxyAppendPtr =
connector -> conn . shared ?
state -> sharedBuffs -> proxyAppend + 2 * args -> channel -> id + type : // Shared buffers
& connector -> proxyAppend ; // Dedicated buffers
if ( state -> nextOps == NULL ) state -> nextOps = op ;
else state -> nextOpsEnd -> next = op ;
state -> nextOpsEnd = op ;
2020-05-12 14:40:18 -07:00
return ncclSuccess ;
}
ncclResult_t ncclProxySaveColl ( struct ncclProxyArgs * args , int pattern , int root , int nranks ) {
if ( pattern == ncclPatternRing || pattern == ncclPatternRingTwice || pattern == ncclPatternPipelineFrom || pattern == ncclPatternPipelineTo ) {
struct ncclRing * ring = & args -> channel -> ring ;
2020-09-04 14:35:05 -07:00
if ( NeedProxy ( proxyRecv , pattern , root , ring , nranks )) NCCLCHECK ( SaveProxy ( proxyRecv , ring -> prev , args ));
if ( NeedProxy ( proxySend , pattern , root , ring , nranks )) NCCLCHECK ( SaveProxy ( proxySend , ring -> next , args ));
2020-05-12 14:40:18 -07:00
}
if ( pattern == ncclPatternTreeUp || pattern == ncclPatternTreeUpDown ) {
// Tree up
2020-09-04 14:35:05 -07:00
struct ncclTree * tree = & args -> channel -> tree ;
for ( int i = 0 ; i < NCCL_MAX_TREE_ARITY ; i ++ ) NCCLCHECK ( SaveProxy ( proxyRecv , tree -> down [ i ], args ));
NCCLCHECK ( SaveProxy ( proxySend , tree -> up , args ));
2020-05-12 14:40:18 -07:00
}
if ( pattern == ncclPatternTreeDown || pattern == ncclPatternTreeUpDown ) {
// Tree down
2020-09-04 14:35:05 -07:00
struct ncclTree * tree = & args -> channel -> tree ;
for ( int i = 0 ; i < NCCL_MAX_TREE_ARITY ; i ++ ) NCCLCHECK ( SaveProxy ( proxySend , tree -> down [ i ], args ));
NCCLCHECK ( SaveProxy ( proxyRecv , tree -> up , args ));
2020-05-12 14:40:18 -07:00
}
if ( pattern == ncclPatternCollTreeUp ) {
// CollTree up
2020-09-04 14:35:05 -07:00
struct ncclTree * tree = & args -> channel -> collTree ;
NCCLCHECK ( SaveProxy ( proxyRecv , tree -> down [ 0 ], args ));
NCCLCHECK ( SaveProxy ( proxySend , tree -> up , args ));
2020-05-12 14:40:18 -07:00
}
if ( pattern == ncclPatternCollTreeDown ) {
// CollTree down
2020-09-04 14:35:05 -07:00
struct ncclTree * tree = & args -> channel -> collTree ;
NCCLCHECK ( SaveProxy ( proxySend , tree -> down [ 0 ], args ));
NCCLCHECK ( SaveProxy ( proxyRecv , tree -> up , args ));
2020-05-12 14:40:18 -07:00
}
return ncclSuccess ;
}
2020-09-04 14:35:05 -07:00
ncclResult_t ncclProxySaveP2p ( struct ncclInfo * info , struct ncclChannel * channel , int segment ) {
2020-05-12 14:40:18 -07:00
struct ncclProxyArgs args ;
memset ( & args , 0 , sizeof ( struct ncclProxyArgs ));
args . channel = channel ;
args . sliceSteps = 1 ;
args . chunkSteps = 1 ;
args . protocol = NCCL_PROTO_SIMPLE ;
2020-09-04 14:35:05 -07:00
args . segment = segment ;
args . opCount = channel -> workFifoTail - 1 ;
2020-05-12 14:40:18 -07:00
args . dtype = info -> datatype ;
2020-09-04 14:35:05 -07:00
if ( info -> delta > 0 && info -> recvbytes >= 0 ) {
int peerrecv = ( info -> comm -> nRanks + info -> comm -> rank - info -> delta ) % info -> comm -> nRanks ;
args . nsteps = DIVUP ( info -> recvbytes , info -> comm -> buffSizes [ NCCL_PROTO_SIMPLE ] / NCCL_STEPS / SENDRECV_SLICEFACTOR );
if ( args . nsteps == 0 ) args . nsteps = 1 ;
args . recvbytes = info -> recvbytes ;
args . sendbytes = 0 ;
2021-03-05 19:59:41 -08:00
NCCLCHECK ( SaveProxy ( LOAD ( info -> comm -> p2pNet ) ? p2pProxyRecv : proxyRecv , peerrecv , & args ));
2020-09-04 14:35:05 -07:00
}
2020-05-12 14:40:18 -07:00
if ( info -> delta > 0 && info -> sendbytes >= 0 ) {
int peersend = ( info -> comm -> rank + info -> delta ) % info -> comm -> nRanks ;
2020-06-29 08:47:46 -07:00
args . nsteps = DIVUP ( info -> sendbytes , info -> comm -> buffSizes [ NCCL_PROTO_SIMPLE ] / NCCL_STEPS / SENDRECV_SLICEFACTOR );
2020-05-12 14:40:18 -07:00
if ( args . nsteps == 0 ) args . nsteps = 1 ;
2020-09-04 14:35:05 -07:00
args . sendbytes = info -> sendbytes ;
args . recvbytes = 0 ;
2021-03-05 19:59:41 -08:00
NCCLCHECK ( SaveProxy ( LOAD ( info -> comm -> p2pNet ) ? p2pProxySend : proxySend , peersend , & args ));
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
return ncclSuccess ;
}
static ncclResult_t removeOp ( struct ncclProxyState * state , struct ncclProxyArgs ** opPtr , struct ncclProxyArgs ** prevOpPtr , struct ncclProxyArgs ** prevGroupPtr ) {
struct ncclProxyArgs * freeOp = * opPtr ;
DEBUG_PROXY_PRINT ( "Remove %ld/%ld -> %ld -> %ld/%ld \n " , OP_INDEX ( * prevOpPtr ), OP_INDEX ( * prevGroupPtr ), OP_INDEX ( freeOp ), OP_INDEX ( freeOp -> next ), OP_INDEX ( freeOp -> nextGroup ));
if ( * prevGroupPtr && * prevOpPtr ) return ncclInternalError ;
if ( freeOp -> nextGroup ) {
// Part of a group : remove the element
struct ncclProxyArgs * next = freeOp -> nextGroup ;
* opPtr = next ;
if ( * prevGroupPtr ) {
( * prevGroupPtr ) -> nextGroup = next ;
} else if ( * prevOpPtr ) {
( * prevOpPtr ) -> next = next ;
} else {
state -> ops = next ;
}
} else {
struct ncclProxyArgs * next = freeOp -> next ;
* opPtr = next ;
if (( * prevGroupPtr )) {
( * prevGroupPtr ) -> next = next ;
( * prevGroupPtr ) -> nextGroup = NULL ;
( * prevGroupPtr ) -> nextPeer = freeOp -> nextPeer ;
if ( * ( freeOp -> proxyAppendPtr ) == freeOp ) * ( freeOp -> proxyAppendPtr ) = * prevGroupPtr ;
( * prevOpPtr ) = * prevGroupPtr ;
( * prevGroupPtr ) = NULL ;
} else {
if ( freeOp -> nextPeer ) {
// replace op by nextPeer
struct ncclProxyArgs * nextPeer = freeOp -> nextPeer ;
if ( * prevOpPtr ) {
( * prevOpPtr ) -> next = nextPeer ;
} else {
state -> ops = nextPeer ;
}
struct ncclProxyArgs * lastGroup = nextPeer ;
while ( lastGroup -> nextGroup ) lastGroup = lastGroup -> nextGroup ;
lastGroup -> next = next ;
* ( prevOpPtr ) = lastGroup ;
} else {
* ( freeOp -> proxyAppendPtr ) = NULL ;
if ( * prevOpPtr ) {
( * prevOpPtr ) -> next = next ;
} else {
state -> ops = next ;
}
}
}
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
pthread_mutex_lock ( & state -> poolMutex );
freeOp -> next = state -> pool ;
state -> pool = freeOp ;
pthread_mutex_unlock ( & state -> poolMutex );
DEBUG_PROXY_PRINT ( "Removed %5ld (%5ld) : " , OP_INDEX ( freeOp ), OP_INDEX ( * freeOp -> proxyAppendPtr ));
NCCLCHECK ( dumpProxyState ( state ));
2020-05-12 14:40:18 -07:00
return ncclSuccess ;
}
2020-09-04 14:35:05 -07:00
static ncclResult_t progressOps ( struct ncclProxyState * state , struct ncclProxyArgs ** opsPtr , int * idle , struct ncclComm * comm ) {
struct ncclProxyArgs * prevOp = NULL ;
struct ncclProxyArgs * prevGroup = NULL ;
struct ncclProxyArgs * op = * opsPtr ;
while ( op ) {
if ( op -> state == ncclProxyOpNone ) return ncclInternalError ;
// opCount >= lastOpCount are part of an ongoing GroupStart/GroupEnd that hasn't started
// yet and might be cancelled before they even start. Hold on on those.
if ( op -> opCount < comm -> lastOpCount ) {
NCCLCHECK ( op -> progress ( op ));
* idle &= op -> idle ;
}
if ( op -> state == ncclProxyOpNone ) {
NCCLCHECK ( removeOp ( state , & op , & prevOp , & prevGroup ));
} else {
if ( op -> nextGroup ) {
prevGroup = op ;
prevOp = NULL ;
op = op -> nextGroup ;
} else {
prevOp = op ;
prevGroup = NULL ;
op = op -> next ;
}
2020-09-30 16:25:36 -07:00
}
2020-03-18 17:03:03 -07:00
}
return ncclSuccess ;
}
2020-05-12 14:40:18 -07:00
void * persistentThread ( void * comm_ ) {
struct ncclComm * comm = ( struct ncclComm * ) comm_ ;
struct ncclProxyState * state = & comm -> proxyState ;
2020-09-04 14:35:05 -07:00
char threadName [ 16 ];
sprintf ( threadName , "NCCLproxy %5d" , comm -> rank );
nvtxNameOsThreadA ( syscall ( SYS_gettid ), threadName );
pthread_mutex_lock ( & state -> opsMutex );
struct ncclProxyArgs ** opsPtr = & state -> ops ;
2020-05-12 14:40:18 -07:00
while ( 1 ) {
2020-12-01 11:33:47 -05:00
if ( LOAD ( comm -> abortFlag )) {
2020-09-04 14:35:05 -07:00
pthread_mutex_unlock ( & state -> opsMutex );
return NULL ;
}
2020-12-01 11:33:47 -05:00
while ( LOAD ( opsPtr ) == NULL ) {
2020-09-04 14:35:05 -07:00
if ( state -> stop ) {
// No more commands to process and proxy has been requested to stop
pthread_mutex_unlock ( & state -> opsMutex );
return NULL ;
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
pthread_cond_wait ( & state -> cond , & state -> opsMutex );
}
int idle = 1 ;
ncclResult_t ret = progressOps ( state , opsPtr , & idle , comm );
2020-05-12 14:40:18 -07:00
if ( ret != ncclSuccess ) {
comm -> fatalError = ret ;
INFO ( NCCL_ALL , "%s:%d -> %d [Proxy Thread]" , __FILE__ , __LINE__ , ret );
2020-09-04 14:35:05 -07:00
pthread_mutex_unlock ( & state -> opsMutex );
2020-05-12 14:40:18 -07:00
return NULL ;
}
2020-09-04 14:35:05 -07:00
if ( idle ) {
pthread_mutex_unlock ( & state -> opsMutex );
sched_yield (); // No request progressed. Let others run.
pthread_mutex_lock ( & state -> opsMutex );
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
}
}
ncclResult_t ncclProxyStart ( struct ncclComm * comm ) {
struct ncclProxyState * state = & comm -> proxyState ;
pthread_mutex_lock ( & state -> opsMutex );
// Sort operations as we append them : collectives and
// receives first, then sends.
ncclProxyArgs * next , * prev = NULL , * op = state -> nextOps ;
while ( op ) {
next = op -> next ;
if ( op -> sendbytes ) {
if ( prev ) prev -> next = next ;
else state -> nextOps = next ;
op -> next = NULL ;
NCCLCHECK ( ProxyAppend ( state , op , op -> connector -> conn . shared ));
} else prev = op ;
2020-05-12 14:40:18 -07:00
op = next ;
2020-09-04 14:35:05 -07:00
}
op = state -> nextOps ;
while ( op ) {
next = op -> next ;
op -> next = NULL ;
NCCLCHECK ( ProxyAppend ( state , op , op -> connector -> conn . shared ));
op = next ;
}
state -> nextOps = state -> nextOpsEnd = NULL ;
NCCLCHECK ( dumpProxyState ( state ));
if ( state -> ops != NULL )
pthread_cond_signal ( & state -> cond );
pthread_mutex_unlock ( & state -> opsMutex );
return ncclSuccess ;
}
NCCL_PARAM ( ProxySharedBuffersCount , "SHARED_BUFF_COUNT" , - 2 );
ncclResult_t ncclProxySharedBuffersInit ( struct ncclComm * comm , int cuda , int * size , char ** ptr ) {
struct ncclProxySharedBuffers * state = comm -> proxyState . sharedBuffs ;
if ( state == NULL ) {
NCCLCHECK ( ncclCalloc ( & state , 1 ));
comm -> proxyState . sharedBuffs = state ;
state -> nslots = ncclParamProxySharedBuffersCount ();
if ( state -> nslots == - 2 ) {
state -> nslots = NCCL_STEPS * NCCL_MAX_WORK_ELEMENTS ;
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
state -> slotSize = comm -> buffSizes [ NCCL_PROTO_SIMPLE ] / ( NCCL_STEPS * SENDRECV_SLICEFACTOR );
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
char * buff ;
int * used ;
2020-12-04 18:52:32 -05:00
* size = 2 * std :: max ( comm -> nChannels , comm -> p2pnChannels ) * state -> slotSize * state -> nslots ;
2020-09-04 14:35:05 -07:00
if ( cuda && state -> cudaBuff [ 0 ] == NULL ) {
2020-12-01 11:33:47 -05:00
NCCLCHECK ( ncclCudaCalloc ( & buff , * size , cuda ));
2020-12-04 18:52:32 -05:00
NCCLCHECK ( ncclCalloc ( & used , 2 * std :: max ( comm -> nChannels , comm -> p2pnChannels ) * state -> nslots ));
for ( int i = 0 ; i < 2 * std :: max ( comm -> nChannels , comm -> p2pnChannels ); i ++ ) {
2020-09-04 14:35:05 -07:00
state -> cudaBuff [ i ] = buff + state -> nslots * state -> slotSize * i ;
state -> cudaUsed [ i ] = used + state -> nslots * i ;
}
} else if ( state -> hostBuff [ 0 ] == NULL ) {
NCCLCHECK ( ncclCudaHostCalloc ( & buff , * size ));
2020-12-04 18:52:32 -05:00
NCCLCHECK ( ncclCalloc ( & used , 2 * std :: max ( comm -> nChannels , comm -> p2pnChannels ) * state -> nslots ));
for ( int i = 0 ; i < 2 * std :: max ( comm -> nChannels , comm -> p2pnChannels ); i ++ ) {
2020-09-04 14:35:05 -07:00
state -> hostBuff [ i ] = buff + state -> nslots * state -> slotSize * i ;
state -> hostUsed [ i ] = used + state -> nslots * i ;
}
}
buff = cuda ? state -> cudaBuff [ 0 ] : state -> hostBuff [ 0 ];
* ptr = buff ;
return ncclSuccess ;
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
ncclResult_t ncclProxySharedBuffersAlloc ( struct ncclComm * comm , int cuda , int type , int channel , int size , char ** ptr ) {
struct ncclProxySharedBuffers * state = comm -> proxyState . sharedBuffs ;
// Use different pools for different channels and also separate send/recv.
int p = 2 * channel + type ;
int * used = cuda ? state -> cudaUsed [ p ] : state -> hostUsed [ p ];
char * buff = cuda ? state -> cudaBuff [ p ] : state -> hostBuff [ p ];
if ( buff == NULL ) return ncclInternalError ;
int nslots = 1 ;
while ( nslots * state -> slotSize < size ) nslots *= 2 ;
for ( int s = 0 ; s < state -> nslots ; s += nslots ) {
int u = 0 ;
for ( int i = 0 ; i < nslots ; i ++ ) u += used [ s + i ];
if ( u == 0 ) {
for ( int i = 0 ; i < nslots ; i ++ ) used [ s + i ] = 1 ;
* ptr = buff + state -> slotSize * s ;
return ncclSuccess ;
2020-05-12 14:40:18 -07:00
}
}
2020-09-04 14:35:05 -07:00
* ptr = NULL ;
return ncclSuccess ;
2020-05-12 14:40:18 -07:00
}
2020-09-04 14:35:05 -07:00
ncclResult_t ncclProxySharedBuffersFree ( struct ncclComm * comm , int cuda , int type , int channel , int size , char * ptr ) {
struct ncclProxySharedBuffers * state = comm -> proxyState . sharedBuffs ;
int p = 2 * channel + type ;
int * used = cuda ? state -> cudaUsed [ p ] : state -> hostUsed [ p ];
char * buff = cuda ? state -> cudaBuff [ p ] : state -> hostBuff [ p ];
if ( buff == NULL ) return ncclInternalError ;
int nslots = 1 ;
while ( nslots * state -> slotSize < size ) nslots *= 2 ;
int s = ( ptr - buff ) / state -> slotSize ;
if ( s < 0 || s + nslots > state -> nslots ) {
2021-02-09 15:34:08 -08:00
WARN ( "Error freeing shared buffer : freeing ptr %p size %d (start %p slot size %d nslots %d)" , ptr , size , buff , state -> slotSize , state -> nslots );
2020-09-04 14:35:05 -07:00
return ncclInternalError ;
}
for ( int i = 0 ; i < nslots ; i ++ ) used [ s + i ] = 0 ;
return ncclSuccess ;
}
ncclResult_t ncclProxySharedBuffersDestroy ( struct ncclComm * comm ) {
struct ncclProxySharedBuffers * state = comm -> proxyState . sharedBuffs ;
if ( state ) {
2020-12-01 11:33:47 -05:00
CUDACHECK ( hipFree ( state -> cudaBuff [ 0 ]));
2020-09-04 14:35:05 -07:00
free ( state -> cudaUsed [ 0 ]);
NCCLCHECK ( ncclCudaHostFree ( state -> hostBuff [ 0 ]));
free ( state -> hostUsed [ 0 ]);
free ( state );
}
2020-05-12 14:40:18 -07:00
return ncclSuccess ;
}
ncclResult_t ncclProxyCreate ( struct ncclComm * comm ) {
if ( ! comm -> proxyThread ) {
comm -> proxyState . cond = PTHREAD_COND_INITIALIZER ;
2020-09-04 14:35:05 -07:00
comm -> proxyState . opsMutex = PTHREAD_MUTEX_INITIALIZER ;
comm -> proxyState . poolMutex = PTHREAD_MUTEX_INITIALIZER ;
2020-05-12 14:40:18 -07:00
comm -> proxyState . ops = NULL ;
pthread_create ( & comm -> proxyThread , NULL , persistentThread , comm );
}
return ncclSuccess ;
}
ncclResult_t ncclProxyDestroy ( struct ncclComm * comm ) {
struct ncclProxyState * state = & comm -> proxyState ;
// Request the proxy to stop and then wake it
2020-09-04 14:35:05 -07:00
pthread_mutex_lock ( & state -> opsMutex );
2020-05-12 14:40:18 -07:00
state -> stop = true ;
pthread_cond_signal ( & state -> cond );
2020-09-04 14:35:05 -07:00
pthread_mutex_unlock ( & state -> opsMutex );
2020-05-12 14:40:18 -07:00
if ( comm -> proxyThread ) pthread_join ( comm -> proxyThread , NULL );
// Free off any memory allocated for the proxy arg pools
2020-09-04 14:35:05 -07:00
pthread_mutex_lock ( & state -> poolMutex );
2020-05-12 14:40:18 -07:00
struct ncclProxyState * proxyState = & comm -> proxyState ;
while ( proxyState -> pools != NULL ) {
struct ncclProxyPool * next = proxyState -> pools -> next ;
free ( proxyState -> pools );
proxyState -> pools = next ;
}
2020-09-04 14:35:05 -07:00
pthread_mutex_unlock ( & state -> poolMutex );
NCCLCHECK ( ncclProxySharedBuffersDestroy ( comm ));
2020-05-12 14:40:18 -07:00
return ncclSuccess ;
}