2018-09-24 16:06:59 -07:00
/*************************************************************************
2020-01-16 16:02:42 -08:00
* Copyright (c) 2015-2020, NVIDIA CORPORATION. All rights reserved.
2020-01-15 17:54:27 -07:00
* Modifications Copyright (c) 2019-2020 Advanced Micro Devices, Inc. All rights reserved.
2018-09-24 16:06:59 -07:00
*
* See LICENSE.txt for license information
************************************************************************/
#include "nccl.h"
2018-12-13 15:56:12 -08:00
#include "channel.h"
2018-09-24 16:06:59 -07:00
#include "nvmlwrap.h"
#include "bootstrap.h"
#include "transport.h"
#include "group.h"
#include "net.h"
2020-01-16 16:02:42 -08:00
#include "coll_net.h"
2018-12-13 15:56:12 -08:00
#include "enqueue.h"
2019-11-19 14:57:39 -08:00
#include "graph.h"
#include "argcheck.h"
2018-09-24 16:06:59 -07:00
#include <fcntl.h>
#include <unistd.h>
2019-07-05 15:43:00 -07:00
#include <hip/hip_runtime.h>
2018-09-24 16:06:59 -07:00
#include <string.h>
#include <errno.h>
#include <assert.h>
2018-11-13 10:37:20 -08:00
#include <dlfcn.h>
2019-11-19 14:57:39 -08:00
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
2020-04-01 13:21:38 -07:00
#include "graph/topo.h"
2018-09-24 16:06:59 -07:00
2018-11-13 10:37:20 -08:00
#define STR2(v) #v
#define STR(v) STR2(v)
2018-09-24 16:06:59 -07:00
#ifdef ENABLE_TRACE
std :: chrono :: high_resolution_clock :: time_point ncclEpoch ;
#endif
2019-08-15 12:15:30 -04:00
#if CUDART_VERSION >= 9020 || defined(__HIP_PLATFORM_HCC__) || defined(__HCC__) || defined(__HIPCC__)
2018-09-24 16:06:59 -07:00
#define NCCL_GROUP_CUDA_STREAM 0 // CGMD: CUDA 9.2,10.X Don't need to use an internal CUDA stream
#else
#define NCCL_GROUP_CUDA_STREAM 1 // CGMD: CUDA 9.0,9.1 Need to use an internal CUDA stream
#endif
NCCL_PARAM ( GroupCudaStream , "GROUP_CUDA_STREAM" , NCCL_GROUP_CUDA_STREAM );
NCCL_PARAM ( CheckPointers , "CHECK_POINTERS" , 0 );
ncclNet_t * ncclNet = NULL ;
2020-01-16 16:02:42 -08:00
ncclCollNet_t * ncclCollNet = NULL ;
2018-09-24 16:06:59 -07:00
2018-12-04 14:47:41 -08:00
// Returns ncclInternalError if anything fails, causing that network to be ignored.
2018-11-13 10:37:20 -08:00
ncclResult_t initNet ( ncclNet_t * net ) {
int ndev ;
2018-12-04 14:47:41 -08:00
if ( net -> init ( ncclDebugLog ) != ncclSuccess ) return ncclInternalError ;
if ( net -> devices ( & ndev ) != ncclSuccess ) return ncclInternalError ;
2018-12-13 15:56:12 -08:00
if ( ndev <= 0 ) return ncclSystemError ;
2018-11-13 10:37:20 -08:00
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
ncclResult_t initCollNet ( ncclCollNet_t * collnet ) {
int ndev ;
if ( collnet -> init ( ncclDebugLog ) != ncclSuccess ) return ncclInternalError ;
if ( collnet -> devices ( & ndev ) != ncclSuccess ) return ncclInternalError ;
if ( ndev <= 0 ) return ncclSystemError ;
return ncclSuccess ;
}
ncclResult_t initNetPlugin ( ncclNet_t ** net , ncclCollNet_t ** collnet ) {
2018-11-13 10:37:20 -08:00
void * netPluginLib = dlopen ( "libnccl-net.so" , RTLD_NOW | RTLD_LOCAL );
if ( netPluginLib == NULL ) {
2018-12-04 11:57:35 -08:00
// dlopen does not guarantee to set errno, but dlerror only gives us a
// string, so checking errno doesn't hurt to try to provide a better
// error message
if ( errno == ENOENT ) {
2019-11-19 14:57:39 -08:00
INFO ( NCCL_INIT | NCCL_NET , "NET/Plugin : No plugin found (libnccl-net.so), using internal implementation" );
2018-12-04 11:57:35 -08:00
} else {
2018-12-13 15:56:12 -08:00
INFO ( NCCL_INIT | NCCL_NET , "NET/Plugin : Plugin load returned %d : %s." , errno , dlerror ());
2018-12-04 11:57:35 -08:00
}
2018-11-13 10:37:20 -08:00
return ncclSuccess ;
}
ncclNet_t * extNet = ( ncclNet_t * ) dlsym ( netPluginLib , STR ( NCCL_PLUGIN_SYMBOL ));
if ( extNet == NULL ) {
2018-12-13 15:56:12 -08:00
INFO ( NCCL_INIT | NCCL_NET , "NET/Plugin: Failed to find " STR ( NCCL_PLUGIN_SYMBOL ) " symbol." );
2020-01-16 16:02:42 -08:00
} else if ( initNet ( extNet ) == ncclSuccess ) {
2018-11-13 10:37:20 -08:00
* net = extNet ;
2020-01-16 16:02:42 -08:00
// Check for CollNet
ncclCollNet_t * extCollNet = ( ncclCollNet_t * ) dlsym ( netPluginLib , STR ( NCCL_COLLNET_PLUGIN_SYMBOL ));
if ( extCollNet == NULL ) {
INFO ( NCCL_INIT | NCCL_NET , "NET/Plugin: Failed to find " STR ( NCCL_COLLNET_PLUGIN_SYMBOL ) " symbol." );
} else if ( initCollNet ( extCollNet ) == ncclSuccess ) {
* collnet = extCollNet ;
}
2018-11-13 10:37:20 -08:00
return ncclSuccess ;
}
if ( netPluginLib != NULL ) dlclose ( netPluginLib );
return ncclSuccess ;
}
ncclResult_t initNet () {
2019-06-25 13:22:47 -07:00
// Always initialize bootstrap network
NCCLCHECK ( bootstrapNetInit ());
2018-11-13 10:37:20 -08:00
2020-01-16 16:02:42 -08:00
NCCLCHECK ( initNetPlugin ( & ncclNet , & ncclCollNet ));
2018-12-13 15:56:12 -08:00
if ( ncclNet != NULL ) return ncclSuccess ;
2018-11-13 10:37:20 -08:00
if ( initNet ( & ncclNetIb ) == ncclSuccess ) {
ncclNet = & ncclNetIb ;
2018-09-24 16:06:59 -07:00
} else {
2019-06-25 13:22:47 -07:00
NCCLCHECK ( initNet ( & ncclNetSocket ));
2018-11-13 10:37:20 -08:00
ncclNet = & ncclNetSocket ;
2018-09-24 16:06:59 -07:00
}
2018-11-13 10:37:20 -08:00
return ncclSuccess ;
2018-09-24 16:06:59 -07:00
}
2020-01-16 16:02:42 -08:00
NCCL_PARAM ( CollNetEnable , "COLLNET_ENABLE" , 0 );
2018-09-24 16:06:59 -07:00
pthread_mutex_t initLock = PTHREAD_MUTEX_INITIALIZER ;
static bool initialized = false ;
static ncclResult_t ncclInit () {
if ( initialized ) return ncclSuccess ;
pthread_mutex_lock ( & initLock );
if ( ! initialized ) {
initEnv ();
initNet ();
2020-01-16 16:02:42 -08:00
INFO ( NCCL_INIT , "Using network %s" , ncclNetName ());
2018-09-24 16:06:59 -07:00
initialized = true ;
}
pthread_mutex_unlock ( & initLock );
return ncclSuccess ;
}
NCCL_API ( ncclResult_t , ncclGetVersion , int * version );
ncclResult_t ncclGetVersion ( int * version ) {
if ( version == NULL ) return ncclInvalidArgument ;
* version = NCCL_VERSION_CODE ;
return ncclSuccess ;
}
NCCL_API ( ncclResult_t , ncclGetUniqueId , ncclUniqueId * out );
ncclResult_t ncclGetUniqueId ( ncclUniqueId * out ) {
NCCLCHECK ( ncclInit ());
NCCLCHECK ( PtrCheck ( out , "GetUniqueId" , "out" ));
return bootstrapGetUniqueId ( out );
}
2019-03-14 19:39:20 -07:00
// Prevent compiler from optimizing out these operations
2019-12-06 18:14:55 +01:00
#ifdef __clang__
2019-12-09 18:31:13 +01:00
#define NCCL_NO_OPTIMIZE __attribute__((optnone))
2019-12-06 18:14:55 +01:00
#else
#define NCCL_NO_OPTIMIZE __attribute__((optimize("O0")))
#endif
void NCCL_NO_OPTIMIZE commPoison ( ncclComm_t comm ) {
2019-11-19 14:57:39 -08:00
comm -> rank = comm -> cudaDev = comm -> busId = comm -> nRanks = - 1 ;
2019-03-14 19:39:20 -07:00
}
2019-11-26 16:33:13 -08:00
#ifdef ENABLE_COLLTRACE
void * ncclCommThreadMain ( void * arg ) {
ncclComm_t comm = ( ncclComm_t ) arg ;
do {
int tail = LOAD ( comm -> hostDevComm . collTraceTail ) % COLLTRACE_NUM_ITEMS ;
int head = comm -> hostDevComm . collTraceHead ;
int count ;
if ( head <= tail )
count = tail - head ;
else
count = COLLTRACE_NUM_ITEMS + head - tail ;
usleep ( 1000 ); //sleep 1ms
for ( int i = 0 ; i < count ; i ++ ) {
char line [ 1024 ];
int offset = 0 ;
#define VEGA_GPU_RTC_FREQUENCY 2.5E7
sprintf ( line , "## [%12.6f] [%02d:%02d] %06lx" ,
( double )( comm -> hostDevComm . collTrace [ head ]. timeStamp ) / VEGA_GPU_RTC_FREQUENCY , comm -> rank , comm -> hostDevComm . collTrace [ head ]. bid , comm -> hostDevComm . collTrace [ head ]. opCount );
offset = strlen ( line );
switch ( comm -> hostDevComm . collTrace [ head ]. type ) {
case ncclCollTraceKernelLaunchType :
sprintf ( line + offset , " KL hwid %8x funcIndex %d" ,
comm -> hostDevComm . collTrace [ head ]. data_0 , comm -> hostDevComm . collTrace [ head ]. funcIndex );
break ;
case ncclCollTraceCollEndType :
if ( comm -> hostDevComm . collTrace [ head ]. funcIndex != - 1 )
sprintf ( line + offset , " CE next funcIndex %d" ,
comm -> hostDevComm . collTrace [ head ]. funcIndex );
else
sprintf ( line + offset , " KE" );
break ;
case ncclCollTraceAbortType :
sprintf ( line + offset , " Abort" );
break ;
default :
sprintf ( line + offset , " unknown collective trace data type" );
break ;
}
INFO ( NCCL_COLL , "%s" , line );
head ++ ;
head %= COLLTRACE_NUM_ITEMS ;
}
comm -> hostDevComm . collTraceHead = tail ;
} while ( ! LOAD ( & comm -> hostDevComm . collTraceExit ));
pthread_exit ( NULL );
}
#endif
2019-12-06 18:14:55 +01:00
#undef NCCL_NO_OPTIMIZE
2018-09-24 16:06:59 -07:00
static ncclResult_t commFree ( ncclComm_t comm ) {
if ( comm == NULL )
return ncclSuccess ;
2019-07-05 15:43:00 -07:00
#ifdef ENABLE_PROFILING
struct ncclProf * prof = ( struct ncclProf * ) malloc ( sizeof ( struct ncclProf ));
CUDACHECK ( hipMemcpy ( prof , comm -> hostDevComm . devProf , sizeof ( struct ncclProf ), hipMemcpyDeviceToHost ));
uint64_t wait_send_cycle = 0 , wait_recv_cycle = 0 ;
for ( int chan = 0 ; chan < comm -> nChannels ; chan ++ ) {
wait_send_cycle += prof -> wait_send_cycle [ chan ];
wait_recv_cycle += prof -> wait_recv_cycle [ chan ];
}
2019-11-05 11:36:45 -08:00
#define VEGA_GPU_RTC_FREQUENCY 2.5E7
2019-07-05 15:43:00 -07:00
if ( comm -> rank == 0 ) {
INFO ( NCCL_INIT , "# %4s %6s %6s %6s %6s %6s %7s %6s %6s %6s %6s %6s" , "Rank" , "total" , "w_send" , "w_recv" , "send" , "rcRdS" , "dRcRdCS" , "dRcCS" , "dRc" , "cS" , "rc" , "rcCS" );
INFO ( NCCL_INIT , "# %4s %6s %6s %6s %6s %6s %7s %6s %6s %6s %6s %6s" , "" , "(s)" , "(s)" , "(s)" , "(GB/s)" , "(GB/s)" , "(GB/s)" , "(GB/s)" , "(GB/s)" , "(GB/s)" , "(GB/s)" , "(GB/s)" , "(GB/s)" );
}
INFO ( NCCL_INIT , "# %4d %6.4f %6.4f %6.4f %6.2f %6.2f %7.2f %6.2f %6.2f %6.2f %6.2f %6.2f" ,
comm -> rank , ( double ) prof -> total_cycle / VEGA_GPU_RTC_FREQUENCY / comm -> nChannels ,
( double ) wait_send_cycle / VEGA_GPU_RTC_FREQUENCY / comm -> nChannels ,
( double ) wait_recv_cycle / VEGA_GPU_RTC_FREQUENCY / comm -> nChannels ,
( prof -> send_cycle ) ? ( double ) prof -> send_byte * comm -> nChannels / (( double ) prof -> send_cycle / VEGA_GPU_RTC_FREQUENCY * 1.0E9 ) : 0 ,
( prof -> recvReduceSend_cycle ) ? ( double ) prof -> recvReduceSend_byte * comm -> nChannels / (( double ) prof -> recvReduceSend_cycle / VEGA_GPU_RTC_FREQUENCY * 1.0E9 ) : 0 ,
( prof -> directRecvReduceCopySend_cycle ) ? ( double ) prof -> directRecvReduceCopySend_byte * comm -> nChannels / (( double ) prof -> directRecvReduceCopySend_cycle / VEGA_GPU_RTC_FREQUENCY * 1.0E9 ) : 0 ,
( prof -> directRecvCopySend_cycle ) ? ( double ) prof -> directRecvCopySend_byte * comm -> nChannels / (( double ) prof -> directRecvCopySend_cycle / VEGA_GPU_RTC_FREQUENCY * 1.0E9 ) : 0 ,
( prof -> directRecv_cycle ) ? ( double ) prof -> directRecv_byte * comm -> nChannels / (( double ) prof -> directRecv_cycle / VEGA_GPU_RTC_FREQUENCY * 1.0E9 ) : 0 ,
( prof -> copySend_cycle ) ? ( double ) prof -> copySend_byte * comm -> nChannels / (( double ) prof -> copySend_cycle / VEGA_GPU_RTC_FREQUENCY * 1.0E9 ) : 0 ,
( prof -> recv_cycle ) ? ( double ) prof -> recv_byte * comm -> nChannels / (( double ) prof -> recv_cycle / VEGA_GPU_RTC_FREQUENCY * 1.0E9 ) : 0 ,
( prof -> recvCopySend_cycle ) ? ( double ) prof -> recvCopySend_byte * comm -> nChannels / (( double ) prof -> recvCopySend_cycle / VEGA_GPU_RTC_FREQUENCY * 1.0E9 ) : 0 );
free ( prof );
CUDACHECK ( hipFree ( comm -> hostDevComm . devProf ));
#endif
2019-11-26 16:33:13 -08:00
#ifdef ENABLE_COLLTRACE
STORE ( & comm -> hostDevComm . collTraceExit , 1 );
if ( comm -> hostDevComm . collTraceThread ) pthread_join ( comm -> hostDevComm . collTraceThread , NULL );
CUDACHECK ( hipHostFree (( void * ) comm -> hostDevComm . collTrace ));
CUDACHECK ( hipHostFree (( void * ) comm -> hostDevComm . collTraceTail ));
#endif
2018-12-13 15:56:12 -08:00
free ( comm -> peerInfo );
2019-11-19 14:57:39 -08:00
ncclTopoFree ( comm -> topo );
2018-12-13 15:56:12 -08:00
if ( comm -> bootstrap )
NCCLCHECK ( bootstrapClose ( comm -> bootstrap ));
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipFree ( comm -> hostDevComm . channels ));
CUDACHECK ( hipFree ( comm -> devComm ));
2018-09-24 16:06:59 -07:00
2018-12-13 15:56:12 -08:00
for ( int channel = 0 ; channel < comm -> nChannels ; channel ++ )
NCCLCHECK ( freeChannel ( comm -> channels + channel , comm -> nRanks ));
2018-09-24 16:06:59 -07:00
if ( comm -> doneEvent != NULL )
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipEventDestroy ( comm -> doneEvent ));
2018-09-24 16:06:59 -07:00
if ( comm -> launchMode == ncclComm :: GROUP ) {
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipStreamDestroy ( comm -> groupStream ));
2018-09-24 16:06:59 -07:00
}
// Last rank frees shared resources between threads
int isLast ;
NCCLCHECK ( ncclCpuBarrierIn ( comm , & isLast ));
if ( isLast ) {
free ( comm -> intraBarrier );
free ( comm -> intraParams );
free ( comm -> intraCudaDevs );
free ( comm -> intraCGMode );
free ( comm -> intraCC );
}
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipHostFree (( void * ) comm -> abortFlag ));
CUDACHECK ( hipHostFree (( void * ) comm -> fatalDevError ));
2018-09-24 16:06:59 -07:00
2019-03-14 19:39:20 -07:00
// Poison comm to try and catch a double free
commPoison ( comm );
2018-09-24 16:06:59 -07:00
free ( comm );
return ncclSuccess ;
}
static ncclResult_t commAlloc ( ncclComm_t * comret , int ndev , int rank ) {
if ( ndev < 1 ) {
WARN ( "invalid device count (%d) requested" , ndev );
return ncclInvalidArgument ;
}
if ( rank >= ndev || rank < 0 ) {
WARN ( "rank %d exceeds ndev=%d" , rank , ndev );
return ncclInvalidArgument ;
}
// Try to create a CUDA object right away. If there is something wrong with
// the device we're on (failure cause #1) , better know it early.
2019-07-05 15:43:00 -07:00
hipEvent_t doneEvent ;
CUDACHECK ( hipEventCreateWithFlags ( & doneEvent , hipEventDisableTiming ));
2018-09-24 16:06:59 -07:00
struct ncclComm * comm ;
NCCLCHECK ( ncclCalloc ( & comm , 1 ));
2019-03-14 19:39:20 -07:00
comm -> rank = comm -> hostDevComm . rank = rank ;
comm -> nRanks = comm -> hostDevComm . nRanks = ndev ;
2019-07-05 15:43:00 -07:00
hipGetDevice ( & comm -> cudaDev );
2019-11-19 14:57:39 -08:00
NCCLCHECK ( getBusId ( comm -> cudaDev , & comm -> busId ));
TRACE ( NCCL_INIT , "comm %p rank %d nranks %d cudaDev %d busId %x" , comm , rank , ndev , comm -> cudaDev , comm -> busId );
2018-12-13 15:56:12 -08:00
2018-09-24 16:06:59 -07:00
comm -> doneEvent = doneEvent ;
comm -> checkPointers = ncclParamCheckPointers () == 1 ? true : false ;
2019-08-15 12:15:30 -04:00
#if CUDART_VERSION >= 9020 || defined(__HIP_PLATFORM_HCC__) || defined(__HCC__) || defined(__HIPCC__)
2018-09-24 16:06:59 -07:00
comm -> groupCudaStream = ncclParamGroupCudaStream ();
#else
// Don't allow the user to overload the default setting in older CUDA builds
comm -> groupCudaStream = NCCL_GROUP_CUDA_STREAM ;
#endif
2018-12-13 15:56:12 -08:00
comm -> fatalError = ncclSuccess ;
2019-03-14 19:39:20 -07:00
NCCLCHECK ( ncclCudaHostAlloc (( void ** ) & comm -> fatalDevError , ( void ** ) & comm -> hostDevComm . fatalDevError , sizeof ( ncclDevError_t )));
2019-07-05 15:43:00 -07:00
STORE ( comm -> fatalDevError , ncclDevSuccess );
2018-12-13 15:56:12 -08:00
2019-03-14 19:39:20 -07:00
NCCLCHECK ( ncclCudaHostAlloc (( void ** ) & comm -> abortFlag , ( void ** ) & comm -> hostDevComm . abortFlag , sizeof ( uint32_t )));
2019-07-05 15:43:00 -07:00
STORE ( comm -> abortFlag , 0 );
2018-09-24 16:06:59 -07:00
comm -> argsptr = & comm -> args ;
2019-07-05 15:43:00 -07:00
#ifdef ENABLE_PROFILING
NCCLCHECK ( ncclCudaCalloc ( & comm -> hostDevComm . devProf , 1 ));
#endif
2018-09-24 16:06:59 -07:00
2019-11-26 16:33:13 -08:00
#ifdef ENABLE_COLLTRACE
CUDACHECK ( hipHostMalloc (( void ** ) & comm -> hostDevComm . collTraceTail , sizeof ( uint32_t ), hipHostMallocMapped ));
CUDACHECK ( hipHostMalloc (( void ** ) & comm -> hostDevComm . collTrace , sizeof ( struct ncclCollTrace ) * COLLTRACE_NUM_ITEMS , hipHostMallocMapped ));
memset ( comm -> hostDevComm . collTrace , 0 , sizeof ( struct ncclCollTrace ) * COLLTRACE_NUM_ITEMS );
comm -> hostDevComm . collTraceExit = comm -> hostDevComm . collTraceHead = * comm -> hostDevComm . collTraceTail = 0 ;
if (( ncclDebugLevel >= NCCL_LOG_INFO ) && ( ncclDebugMask & NCCL_COLL ))
pthread_create ( & comm -> hostDevComm . collTraceThread , NULL , ncclCommThreadMain , ( void * ) comm );
else
comm -> hostDevComm . collTraceThread = 0 ;
#endif
2020-01-16 16:02:42 -08:00
comm -> collNetSupport = 0 ;
2019-11-26 16:33:13 -08:00
2018-09-24 16:06:59 -07:00
* comret = comm ;
return ncclSuccess ;
}
static ncclResult_t devCommSetup ( ncclComm_t comm ) {
2019-03-14 19:39:20 -07:00
// Duplicate the channels on the device
NCCLCHECK ( ncclCudaCalloc ( & comm -> hostDevComm . channels , comm -> nChannels ));
NCCLCHECK ( ncclCudaMemcpy ( comm -> hostDevComm . channels , comm -> channels , comm -> nChannels ));
// Copy userRanks and peers
2018-12-13 15:56:12 -08:00
for ( int r = 0 ; r < comm -> nChannels ; r ++ ) {
NCCLCHECK ( ncclCudaMemcpy ( comm -> channels [ r ]. ring . devUserRanks , comm -> channels [ r ]. ring . userRanks , comm -> nRanks ));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( ncclCudaMemcpy ( comm -> channels [ r ]. devPeers , comm -> channels [ r ]. peers , comm -> nRanks + 1 ));
2018-09-24 16:06:59 -07:00
}
2019-03-14 19:39:20 -07:00
// Duplicate the dev comm on the device
NCCLCHECK ( ncclCudaCalloc ( & comm -> devComm , 1 ));
NCCLCHECK ( ncclCudaMemcpy ( comm -> devComm , & comm -> hostDevComm , 1 ));
2018-09-24 16:06:59 -07:00
return ncclSuccess ;
}
// Pre-process the string so that running "strings" on the lib can quickly reveal the version.
2019-08-15 12:15:30 -04:00
#if defined(__HIP_PLATFORM_HCC__) || defined(__HCC__) || defined(__HIPCC__)
2020-05-20 18:15:32 +00:00
#define VERSION_STRING "RCCL version " STR(NCCL_MAJOR) "." STR(NCCL_MINOR) "." STR(NCCL_PATCH) NCCL_SUFFIX "+hip" STR(HIP_VERSION_MAJOR) "." STR(HIP_VERSION_MINOR)
2019-07-05 15:43:00 -07:00
#else
2018-09-24 16:06:59 -07:00
#define VERSION_STRING "NCCL version " STR(NCCL_MAJOR) "." STR(NCCL_MINOR) "." STR(NCCL_PATCH) NCCL_SUFFIX "+cuda" STR(CUDA_MAJOR) "." STR(CUDA_MINOR)
2019-07-05 15:43:00 -07:00
#endif
2018-09-24 16:06:59 -07:00
static void showVersion () {
static int shown = 0 ;
2018-11-13 10:37:20 -08:00
if ( shown == 0 && ncclDebugLevel >= NCCL_LOG_VERSION ) {
2018-09-24 16:06:59 -07:00
printf ( "%s \n " , VERSION_STRING );
fflush ( stdout );
if ( ncclDebugFile != stdout )
2018-11-13 10:37:20 -08:00
INFO ( NCCL_ALL , "%s" , VERSION_STRING ); // Also log NCCL version in one of the files
2018-09-24 16:06:59 -07:00
shown = 1 ;
}
}
2019-11-19 14:57:39 -08:00
static ncclResult_t fillInfo ( struct ncclComm * comm , struct ncclPeerInfo * info , uint64_t commHash ) {
info -> rank = comm -> rank ;
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipGetDevice ( & info -> cudaDev ));
info -> hostHash = getHostHash () + commHash ;
info -> pidHash = getPidHash () + commHash ;
2018-12-13 15:56:12 -08:00
2019-11-19 14:57:39 -08:00
// Get the device MAJOR:MINOR of /dev/shm so we can use that
// information to decide whether we can use SHM for inter-process
// communication in a container environment
struct stat statbuf ;
SYSCHECK ( stat ( "/dev/shm" , & statbuf ), "stat" );
info -> shmDev = statbuf . st_dev ;
info -> busId = comm -> busId ;
2020-01-16 16:02:42 -08:00
NCCLCHECK ( ncclGpuGdrSupport ( & info -> gdrSupport ));
2018-09-24 16:06:59 -07:00
return ncclSuccess ;
}
template < int type >
2019-11-19 14:57:39 -08:00
static ncclResult_t selectTransport ( struct ncclTopoSystem * topo , struct ncclTopoGraph * graph , struct ncclPeerInfo * myInfo , struct ncclPeerInfo * peerInfo , struct ncclConnect * connect , struct ncclConnector * connector , int buffSize , int channelId ) {
2018-09-24 16:06:59 -07:00
for ( int t = 0 ; t < NTRANSPORTS ; t ++ ) {
struct ncclTransport * transport = ncclTransports + t ;
struct ncclTransportComm * transportComm = type == 1 ? & transport -> send : & transport -> recv ;
2019-11-19 14:57:39 -08:00
int ret = 0 ;
NCCLCHECK ( transport -> canConnect ( & ret , topo , graph , myInfo , peerInfo ));
if ( ret ) {
2018-12-13 15:56:12 -08:00
connector -> transportComm = transportComm ;
2019-11-19 14:57:39 -08:00
NCCLCHECK ( transportComm -> setup ( topo , graph , myInfo , peerInfo , connect , connector , buffSize , channelId ));
2018-09-24 16:06:59 -07:00
return ncclSuccess ;
}
}
WARN ( "No transport found !" );
return ncclInternalError ;
}
2019-11-19 14:57:39 -08:00
static ncclResult_t setupChannel ( struct ncclComm * comm , int channelId , int rank , int nranks , int * ringRanks ) {
2018-12-13 15:56:12 -08:00
TRACE ( NCCL_INIT , "rank %d nranks %d" , rank , nranks );
NCCLCHECK ( initChannel ( comm , channelId ));
2019-11-19 14:57:39 -08:00
struct ncclRing * ring = & comm -> channels [ channelId ]. ring ;
2018-09-24 16:06:59 -07:00
// Reorganize ranks to start with rank.
int shift ;
for ( shift = 0 ; shift < nranks ; shift ++ ) {
if ( ringRanks [ shift ] == rank ) {
break ;
}
}
for ( int i = 0 ; i < nranks ; i ++ ) {
ring -> userRanks [ i ] = ringRanks [( i + shift ) % nranks ];
}
return ncclSuccess ;
}
void * waitForNonNullPtr ( void * p ) {
volatile void ** ptr = ( volatile void ** ) p ;
2019-07-05 15:43:00 -07:00
while ( LOAD ( ptr ) == NULL ) sched_yield ();
return ( void * )( LOAD ( ptr ));
2018-09-24 16:06:59 -07:00
}
ncclResult_t initParams ( struct ncclComm * comm ) {
2019-07-05 15:43:00 -07:00
hipLaunchParams * params = comm -> myParams = comm -> intraParams + comm -> intraRank ;
params -> args = ( void ** ) & comm -> argsptr ;
2018-09-24 16:06:59 -07:00
params -> stream = NULL ;
params -> sharedMem = 0 ;
params -> blockDim . x = 0 ; params -> blockDim . y = params -> blockDim . z = 1 ;
params -> gridDim . x = 0 ; params -> gridDim . y = params -> gridDim . z = 1 ;
return ncclSuccess ;
}
// Allocate/Set Intra Process Structures and set CG options
ncclResult_t ncclCommSetIntra ( struct ncclComm * comm , int rank , int ranks , struct ncclComm * comm0 ) {
comm -> intraRank = rank ;
comm -> intraRanks = ranks ;
comm -> intraPhase = 0 ;
// Alloc shared structures
if ( rank == 0 ) {
assert ( comm == comm0 );
int * bar ;
NCCLCHECK ( ncclCalloc ( & bar , 2 ));
bar [ 0 ] = bar [ 1 ] = 0 ;
comm -> intraBarrier = bar ;
NCCLCHECK ( ncclCalloc ( & comm -> intraParams , comm -> intraRanks ));
NCCLCHECK ( ncclCalloc ( & comm -> intraCudaDevs , comm -> intraRanks ));
int * CGMode ;
NCCLCHECK ( ncclCalloc ( & CGMode , 1 ));
* CGMode = 0x11 ;
comm -> intraCGMode = CGMode ;
int * CC ;
NCCLCHECK ( ncclCalloc ( & CC , 1 ));
2019-11-19 14:57:39 -08:00
* CC = ncclCudaCompCap ();
2018-09-24 16:06:59 -07:00
comm -> intraCC = CC ;
} else {
comm -> intraBarrier = ( int * ) waitForNonNullPtr ( & comm0 -> intraBarrier );
2019-07-05 15:43:00 -07:00
comm -> intraParams = ( hipLaunchParams * ) waitForNonNullPtr ( & comm0 -> intraParams );
2018-09-24 16:06:59 -07:00
comm -> intraCudaDevs = ( int * ) waitForNonNullPtr ( & comm0 -> intraCudaDevs );
comm -> intraCGMode = ( int * ) waitForNonNullPtr ( & comm0 -> intraCGMode );
comm -> intraCC = ( int * ) waitForNonNullPtr ( & comm0 -> intraCC );
}
comm -> intraCudaDevs [ comm -> intraRank ] = comm -> cudaDev ;
NCCLCHECK ( initParams ( comm ));
2019-07-05 15:43:00 -07:00
int cgMdLaunch = 1 ;
2018-09-24 16:06:59 -07:00
// Set CG Mode
comm -> launchMode = ncclComm :: GROUP ;
char * str = getenv ( "NCCL_LAUNCH_MODE" );
if ( comm -> intraRanks == 1 || ( str && strcmp ( str , "PARALLEL" ) == 0 )) {
comm -> launchMode = ncclComm :: PARALLEL ;
}
if ( comm -> launchMode == ncclComm :: GROUP ) {
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipStreamCreateWithFlags ( & comm -> groupStream , hipStreamNonBlocking ));
2018-12-13 15:22:17 -08:00
#if CUDART_VERSION >= 9000
2019-11-19 14:57:39 -08:00
if ( * comm -> intraCC && ( ncclCudaCompCap () == * comm -> intraCC )) {
2018-09-24 16:06:59 -07:00
// Check whether the GPU supports Cooperative Group Multi Device Launch
2019-07-05 15:43:00 -07:00
( void ) hipDeviceGetAttribute ( & cgMdLaunch , cudaDevAttrCooperativeMultiDeviceLaunch , comm -> cudaDev );
2018-09-24 16:06:59 -07:00
}
#endif
}
// Disable cgMdLaunch if any rank does not support it
if ( cgMdLaunch == 0 ) {
* comm -> intraCGMode = 0x10 ;
}
return ncclSuccess ;
}
2019-11-19 14:57:39 -08:00
static ncclResult_t p2pSetup ( struct ncclComm * comm , struct ncclTopoGraph * graph , struct ncclChannel * channel , int nrecv , int * peerRecv , int nsend , int * peerSend ) {
2018-12-13 15:56:12 -08:00
TRACE ( NCCL_INIT , "nsend %d nrecv %d" , nsend , nrecv );
uint32_t nSkippedSend = 0 , nSkippedRecv = 0 ; /* for tracing */
struct ncclConnect connect ;
struct ncclConnector * conn ;
for ( int i = 0 ; i < nrecv ; i ++ ) {
int peer = peerRecv [ i ];
2020-01-16 16:02:42 -08:00
if ( peer == - 1 || peer >= comm -> nRanks ) continue ;
2018-12-13 15:56:12 -08:00
conn = & channel -> peers [ peer ]. recv ;
if ( conn -> connected ) { ++ nSkippedRecv ; continue ; }
2019-03-14 19:39:20 -07:00
memset ( & connect , 0 , sizeof ( connect ));
2019-11-19 14:57:39 -08:00
NCCLCHECK ( selectTransport < 0 > ( comm -> topo , graph , comm -> peerInfo + comm -> rank , comm -> peerInfo + peer , & connect , conn , channel -> buffSize , channel -> id ));
2018-12-13 15:56:12 -08:00
NCCLCHECK ( bootstrapSend ( comm -> bootstrap , peer , & connect , sizeof ( struct ncclConnect )));
}
for ( int i = 0 ; i < nsend ; i ++ ) {
int peer = peerSend [ i ];
2020-01-16 16:02:42 -08:00
if ( peer == - 1 || peer >= comm -> nRanks ) continue ;
2018-12-13 15:56:12 -08:00
conn = & channel -> peers [ peer ]. send ;
if ( conn -> connected ) { ++ nSkippedSend ; continue ; }
2019-03-14 19:39:20 -07:00
memset ( & connect , 0 , sizeof ( connect ));
2019-11-19 14:57:39 -08:00
NCCLCHECK ( selectTransport < 1 > ( comm -> topo , graph , comm -> peerInfo + comm -> rank , comm -> peerInfo + peer , & connect , conn , channel -> buffSize , channel -> id ));
2018-12-13 15:56:12 -08:00
NCCLCHECK ( bootstrapSend ( comm -> bootstrap , peer , & connect , sizeof ( struct ncclConnect )));
}
for ( int i = 0 ; i < nsend ; i ++ ) {
int peer = peerSend [ i ];
2020-01-16 16:02:42 -08:00
if ( peer == - 1 || peer >= comm -> nRanks ) continue ;
2018-12-13 15:56:12 -08:00
conn = & channel -> peers [ peer ]. send ;
if ( conn -> connected ) { ++ nSkippedSend ; continue ; }
2019-03-14 19:39:20 -07:00
memset ( & connect , 0 , sizeof ( connect ));
2018-12-13 15:56:12 -08:00
NCCLCHECK ( bootstrapRecv ( comm -> bootstrap , peer , & connect , sizeof ( struct ncclConnect )));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( conn -> transportComm -> connect ( & connect , 1 , comm -> rank , conn ));
2018-12-13 15:56:12 -08:00
conn -> connected = 1 ;
}
for ( int i = 0 ; i < nrecv ; i ++ ) {
int peer = peerRecv [ i ];
2020-01-16 16:02:42 -08:00
if ( peer == - 1 || peer >= comm -> nRanks ) continue ;
2018-12-13 15:56:12 -08:00
conn = & channel -> peers [ peer ]. recv ;
if ( conn -> connected ) { ++ nSkippedRecv ; continue ; }
2019-03-14 19:39:20 -07:00
memset ( & connect , 0 , sizeof ( connect ));
2018-12-13 15:56:12 -08:00
NCCLCHECK ( bootstrapRecv ( comm -> bootstrap , peer , & connect , sizeof ( struct ncclConnect )));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( conn -> transportComm -> connect ( & connect , 1 , comm -> rank , conn ));
2018-12-13 15:56:12 -08:00
conn -> connected = 1 ;
}
TRACE ( NCCL_INIT , "nsend %d nrecv %d nSkippedSend %u nSkippedRecv %u - DONE" , nsend , nrecv , nSkippedSend , nSkippedRecv );
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
extern struct ncclTransport collNetTransport ;
// All ranks must participate in collNetSetup call
// type: 0 for send, 1 for recv
// return: 0 - unsupported, 1 - supported
static int collNetSetup ( struct ncclComm * comm , struct ncclTopoGraph * collNetGraph , struct ncclChannel * channel , int collNetChannels , int rank , int nranks , int masterRank , int masterPeer , int nMasters , int type ) {
int rankInCollNet = - 1 ;
int supported = 0 ;
int isMaster = ( rank == masterRank ) ? 1 : 0 ;
struct {
int collNetRank ;
ncclConnect connect ;
} sendrecvExchange ;
// check if we can connect to collnet, whose root is the nranks-th rank
struct ncclPeerInfo * myInfo = comm -> peerInfo + rank , * peerInfo = comm -> peerInfo + nranks ;
peerInfo -> rank = nranks ;
int ret = 1 ;
if ( isMaster ) {
NCCLCHECK ( collNetTransport . canConnect ( & ret , comm -> topo , collNetGraph , myInfo , peerInfo ));
}
// send master receives connect info from peer recv master
if ( isMaster && type == 0 ) {
NCCLCHECK ( bootstrapRecv ( comm -> bootstrap , masterPeer , & sendrecvExchange , sizeof ( sendrecvExchange )));
rankInCollNet = sendrecvExchange . collNetRank ;
INFO ( NCCL_INIT , "CollNet [send] : rank %d collNetRank %d collNetNranks %d received connect from rank %d" , rank , rankInCollNet , nMasters , masterPeer );
}
// select
struct ncclPeer * root = channel -> peers + nranks ;
struct ncclConnector * conn = ( type == 1 ) ? & root -> recv : & root -> send ;
struct ncclTransportComm * transportComm = ( type == 1 ) ? & ( collNetTransport . recv ) : & ( collNetTransport . send );
conn -> transportComm = transportComm ;
// setup
struct ncclConnect myConnect ;
if ( isMaster && ret > 0 ) {
NCCLCHECK ( transportComm -> setup ( comm -> topo , collNetGraph , myInfo , peerInfo , & myConnect , conn , channel -> buffSize , channel -> id ));
}
// prepare connect handles
ncclResult_t res ;
struct {
int isMaster ;
ncclConnect connect ;
} * allConnects = NULL ;
ncclConnect * masterConnects = NULL ;
NCCLCHECK ( ncclCalloc ( & masterConnects , nMasters ));
if ( type == 1 ) { // recv side: AllGather
// all ranks must participate
NCCLCHECK ( ncclCalloc ( & allConnects , nranks ));
allConnects [ rank ]. isMaster = isMaster ;
memcpy ( & ( allConnects [ rank ]. connect ), & myConnect , sizeof ( struct ncclConnect ));
NCCLCHECKGOTO ( bootstrapAllGather ( comm -> bootstrap , allConnects , sizeof ( * allConnects )), res , cleanup );
// consolidate
int c = 0 ;
for ( int r = 0 ; r < nranks ; r ++ ) {
if ( allConnects [ r ]. isMaster ) {
memcpy ( masterConnects + c , & ( allConnects [ r ]. connect ), sizeof ( struct ncclConnect ));
if ( r == rank ) rankInCollNet = c ;
c ++ ;
}
}
} else { // send side : copy in connect info received from peer recv master
if ( isMaster ) memcpy ( masterConnects + rankInCollNet , & ( sendrecvExchange . connect ), sizeof ( struct ncclConnect ));
}
// connect
if ( isMaster && ret > 0 ) {
NCCLCHECKGOTO ( transportComm -> connect ( masterConnects , nMasters , rankInCollNet , conn ), res , cleanup );
}
// recv side sends connect info to send side
if ( isMaster && type == 1 ) {
sendrecvExchange . collNetRank = rankInCollNet ;
memcpy ( & sendrecvExchange . connect , masterConnects + rankInCollNet , sizeof ( struct ncclConnect ));
NCCLCHECK ( bootstrapSend ( comm -> bootstrap , masterPeer , & sendrecvExchange , sizeof ( sendrecvExchange )));
INFO ( NCCL_INIT , "CollNet [recv] : rank %d collNetRank %d collNetNranks %d sent connect to rank %d" , rank , rankInCollNet , nMasters , masterPeer );
}
if ( ret > 0 ) {
supported = 1 ;
}
cleanup :
if ( allConnects != NULL ) free ( allConnects );
if ( masterConnects != NULL ) free ( masterConnects );
return supported ;
}
static ncclResult_t checkCollNetSetup ( struct ncclComm * comm , int rank , int collNetSetupFail ) {
int nranks = comm -> nRanks ;
// AllGather collNet setup results
int * allGatherFailures ;
NCCLCHECK ( ncclCalloc ( & allGatherFailures , nranks ));
allGatherFailures [ rank ] = collNetSetupFail ;
NCCLCHECK ( bootstrapAllGather ( comm -> bootstrap , allGatherFailures , sizeof ( int )));
for ( int i = 0 ; i < nranks ; i ++ ) {
if ( allGatherFailures [ i ] != 0 ) {
collNetSetupFail = 1 ;
break ;
}
}
free ( allGatherFailures );
if ( collNetSetupFail ) {
if ( rank == 0 ) WARN ( "Cannot initialize CollNet, using %s instead" , ncclNetName ());
// Free collNet resources
for ( int r = 0 ; r < comm -> nChannels ; r ++ ) {
struct ncclChannel * channel = comm -> channels + r ;
struct ncclPeer * peer = channel -> peers + nranks ;
if ( peer -> send . transportResources && peer -> send . transportComm ) NCCLCHECK ( peer -> send . transportComm -> free ( peer -> send . transportResources ));
if ( peer -> recv . transportResources && peer -> recv . transportComm ) NCCLCHECK ( peer -> recv . transportComm -> free ( peer -> recv . transportResources ));
peer -> send . transportResources = NULL ; // avoid double free
peer -> recv . transportResources = NULL ; // avoid double free
}
// Set support to 0
comm -> collNetSupport = 0 ;
} else {
comm -> collNetSupport = 1 ;
}
return ncclSuccess ;
}
2019-11-19 14:57:39 -08:00
NCCL_PARAM ( CrossNic , "CROSS_NIC" , 2 );
2020-01-16 16:02:42 -08:00
NCCL_PARAM ( GraphDumpFileRank , "GRAPH_DUMP_FILE_RANK" , 0 );
2019-11-19 14:57:39 -08:00
2018-09-24 16:06:59 -07:00
static ncclResult_t initTransportsRank ( struct ncclComm * comm , ncclUniqueId * commId ) {
2018-12-13 15:56:12 -08:00
// We use 3 AllGathers
// 1. { peerInfo, comm }
// 2. ConnectTransport[nranks], ConnectValue[nranks]
// 3. { nThreads, nrings, compCap, prev[MAXCHANNELS], next[MAXCHANNELS] }
2018-09-24 16:06:59 -07:00
int rank = comm -> rank ;
int nranks = comm -> nRanks ;
2019-08-14 10:08:39 -07:00
uint64_t commHash = getHash ( commId -> internal , NCCL_UNIQUE_ID_BYTES );
TRACE ( NCCL_INIT , "comm %p, commHash %lx, rank %d nranks %d - BEGIN" , comm , commHash , rank , nranks );
2018-12-13 15:56:12 -08:00
NCCLCHECK ( bootstrapInit ( commId , rank , nranks , & comm -> bootstrap ));
2018-09-24 16:06:59 -07:00
2018-12-13 15:56:12 -08:00
// AllGather1 - begin
struct {
struct ncclPeerInfo peerInfo ;
struct ncclComm * comm ;
} * allGather1Data ;
NCCLCHECK ( ncclCalloc ( & allGather1Data , nranks ));
allGather1Data [ rank ]. comm = comm ;
2019-11-19 14:57:39 -08:00
struct ncclPeerInfo * myInfo = & allGather1Data [ rank ]. peerInfo ;
NCCLCHECK ( fillInfo ( comm , myInfo , commHash ));
2018-12-13 15:56:12 -08:00
NCCLCHECK ( bootstrapAllGather ( comm -> bootstrap , allGather1Data , sizeof ( * allGather1Data )));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( ncclCalloc ( & comm -> peerInfo , nranks + 1 )); // Extra rank to represent CollNet root
2018-12-13 15:56:12 -08:00
for ( int i = 0 ; i < nranks ; i ++ ) {
memcpy ( comm -> peerInfo + i , & allGather1Data [ i ]. peerInfo , sizeof ( struct ncclPeerInfo ));
2019-11-19 14:57:39 -08:00
if (( i != rank ) && ( comm -> peerInfo [ i ]. hostHash == myInfo -> hostHash ) && ( comm -> peerInfo [ i ]. busId == myInfo -> busId )) {
WARN ( "Duplicate GPU detected : rank %d and rank %d both on CUDA device %x" , rank , i , myInfo -> busId );
return ncclInvalidUsage ;
}
2018-12-13 15:56:12 -08:00
}
// AllGather1 data is used again below
// AllGather1 - end
2019-11-19 14:57:39 -08:00
// Topo detection / System graph creation
NCCLCHECK ( ncclTopoGetSystem ( comm , & comm -> topo ));
// Compute paths between GPUs and NICs
NCCLCHECK ( ncclTopoComputePaths ( comm -> topo , comm -> peerInfo ));
// Remove inaccessible GPUs and unused NICs
NCCLCHECK ( ncclTopoTrimSystem ( comm -> topo , comm ));
// Recompute paths after trimming
NCCLCHECK ( ncclTopoComputePaths ( comm -> topo , comm -> peerInfo ));
2020-01-16 16:02:42 -08:00
// Init search
NCCLCHECK ( ncclTopoSearchInit ( comm -> topo ));
2019-11-19 14:57:39 -08:00
// Print final topology
NCCLCHECK ( ncclTopoPrint ( comm -> topo ));
// Get rings and trees
struct ncclTopoGraph ringGraph ;
2020-01-16 16:02:42 -08:00
ringGraph . id = 0 ;
2019-11-19 14:57:39 -08:00
ringGraph . pattern = NCCL_TOPO_PATTERN_RING ;
ringGraph . crossNic = ncclParamCrossNic ();
2020-01-16 16:02:42 -08:00
ringGraph . collNet = 0 ;
ringGraph . minChannels = 1 ;
ringGraph . maxChannels = MAXCHANNELS / 2 ;
2019-11-19 14:57:39 -08:00
NCCLCHECK ( ncclTopoCompute ( comm -> topo , & ringGraph ));
NCCLCHECK ( ncclTopoPrintGraph ( comm -> topo , & ringGraph ));
2018-09-24 16:06:59 -07:00
2020-01-16 16:02:42 -08:00
struct ncclTopoGraph treeGraph ;
treeGraph . id = 1 ;
treeGraph . pattern = NCCL_TOPO_PATTERN_SPLIT_TREE ;
treeGraph . crossNic = ncclParamCrossNic ();
treeGraph . collNet = 0 ;
2020-04-01 13:21:38 -07:00
treeGraph . minChannels = comm -> topo -> nodes [ NET ]. count != 0 ? 1 : ringGraph . nChannels ;
2020-01-16 16:02:42 -08:00
treeGraph . maxChannels = ringGraph . nChannels ;
NCCLCHECK ( ncclTopoCompute ( comm -> topo , & treeGraph ));
NCCLCHECK ( ncclTopoPrintGraph ( comm -> topo , & treeGraph ));
struct ncclTopoGraph collNetGraph ;
collNetGraph . id = 2 ;
collNetGraph . pattern = NCCL_TOPO_PATTERN_TREE ;
collNetGraph . collNet = 1 ;
collNetGraph . crossNic = ncclParamCrossNic ();
collNetGraph . minChannels = collNetGraph . maxChannels = ringGraph . nChannels ;
NCCLCHECK ( ncclTopoCompute ( comm -> topo , & collNetGraph ));
NCCLCHECK ( ncclTopoPrintGraph ( comm -> topo , & collNetGraph ));
if ( comm -> rank == ncclParamGraphDumpFileRank ()) {
struct ncclTopoGraph * graphs [ 3 ] = { & ringGraph , & treeGraph , & collNetGraph };
NCCLCHECK ( ncclTopoDumpGraphs ( comm -> topo , 3 , graphs ));
}
2018-12-13 15:56:12 -08:00
// AllGather3 - begin
2020-01-16 16:02:42 -08:00
struct ncclGraphInfo {
int sameChannels ;
float speedIntra ;
float speedInter ;
int typeIntra ;
};
2019-11-19 14:57:39 -08:00
2018-12-13 15:56:12 -08:00
struct {
int cudaCompCap ;
2019-11-19 14:57:39 -08:00
int fullCudaCompCap ;
int nChannels ;
2020-01-16 16:02:42 -08:00
struct ncclGraphInfo tree ;
struct ncclGraphInfo ring ;
struct ncclGraphInfo collNet ;
2019-11-19 14:57:39 -08:00
struct ncclTopoRanks topoRanks ;
2018-12-13 15:56:12 -08:00
} * allGather3Data ;
NCCLCHECK ( ncclCalloc ( & allGather3Data , nranks ));
allGather3Data [ rank ]. cudaCompCap = ncclCudaCompCap ();
2020-01-16 16:02:42 -08:00
allGather3Data [ rank ]. nChannels = comm -> nChannels = treeGraph . nChannels = ringGraph . nChannels =
std :: min ( treeGraph . nChannels , ringGraph . nChannels );
2019-11-19 14:57:39 -08:00
allGather3Data [ rank ]. tree . sameChannels = treeGraph . sameChannels ;
allGather3Data [ rank ]. tree . speedIntra = treeGraph . speedIntra ;
allGather3Data [ rank ]. tree . speedInter = treeGraph . speedInter ;
2020-01-16 16:02:42 -08:00
allGather3Data [ rank ]. tree . typeIntra = treeGraph . typeIntra ;
2019-11-19 14:57:39 -08:00
allGather3Data [ rank ]. ring . sameChannels = ringGraph . sameChannels ;
allGather3Data [ rank ]. ring . speedIntra = ringGraph . speedIntra ;
allGather3Data [ rank ]. ring . speedInter = ringGraph . speedInter ;
2020-01-16 16:02:42 -08:00
allGather3Data [ rank ]. ring . typeIntra = ringGraph . typeIntra ;
allGather3Data [ rank ]. collNet . sameChannels = collNetGraph . sameChannels ;
allGather3Data [ rank ]. collNet . speedIntra = collNetGraph . speedIntra ;
allGather3Data [ rank ]. collNet . speedInter = collNetGraph . speedInter ;
allGather3Data [ rank ]. collNet . typeIntra = collNetGraph . typeIntra ;
2019-11-19 14:57:39 -08:00
2020-01-16 16:02:42 -08:00
NCCLCHECK ( ncclTopoPreset ( comm , & treeGraph , & ringGraph , & collNetGraph , & allGather3Data [ rank ]. topoRanks ));
2019-11-19 14:57:39 -08:00
2018-12-13 15:56:12 -08:00
NCCLCHECK ( bootstrapAllGather ( comm -> bootstrap , allGather3Data , sizeof ( * allGather3Data )));
2019-11-19 14:57:39 -08:00
// Determine nNodes, firstRanks, ...
int * nodesFirstRank ;
NCCLCHECK ( ncclCalloc ( & nodesFirstRank , nranks ));
for ( int i = 0 ; i < nranks ; i ++ ) {
int node = - 1 ;
int firstRank = allGather3Data [ i ]. topoRanks . ringRecv [ 0 ];
for ( int n = 0 ; n < comm -> nNodes ; n ++ ) {
if ( nodesFirstRank [ n ] == firstRank ) node = n ;
}
if ( node == - 1 ) {
node = comm -> nNodes ++ ;
nodesFirstRank [ node ] = firstRank ;
}
if ( i == comm -> rank ) comm -> node = node ;
}
2018-09-24 16:06:59 -07:00
// Determine the minimum CUDA Compute capability of all GPUs
2018-12-13 15:56:12 -08:00
int myCompCap = allGather3Data [ rank ]. cudaCompCap ;
2019-11-19 14:57:39 -08:00
int minCompCap = myCompCap , maxCompCap = myCompCap ;
for ( int i = 0 ; i < nranks ; i ++ ) {
2018-12-13 15:56:12 -08:00
minCompCap = std :: min ( allGather3Data [ i ]. cudaCompCap , minCompCap );
2019-11-19 14:57:39 -08:00
maxCompCap = std :: max ( allGather3Data [ i ]. cudaCompCap , maxCompCap );
}
2018-12-13 15:56:12 -08:00
2019-11-19 14:57:39 -08:00
int nChannelsOrig = comm -> nChannels ;
struct ncclTopoRanks ** allTopoRanks ;
NCCLCHECK ( ncclCalloc ( & allTopoRanks , comm -> nRanks ));
for ( int i = 0 ; i < nranks ; i ++ ) {
allTopoRanks [ i ] = & allGather3Data [ i ]. topoRanks ;
// Make sure we align all ranks so that the tuning is consistent across ranks
treeGraph . nChannels = ringGraph . nChannels = comm -> nChannels = std :: min ( allGather3Data [ i ]. nChannels , comm -> nChannels );
treeGraph . sameChannels = std :: min ( allGather3Data [ i ]. tree . sameChannels , treeGraph . sameChannels );
treeGraph . speedIntra = std :: min ( allGather3Data [ i ]. tree . speedIntra , treeGraph . speedIntra );
treeGraph . speedInter = std :: min ( allGather3Data [ i ]. tree . speedInter , treeGraph . speedInter );
2020-01-16 16:02:42 -08:00
treeGraph . typeIntra = std :: min ( allGather3Data [ i ]. tree . typeIntra , treeGraph . typeIntra );
2019-11-19 14:57:39 -08:00
ringGraph . sameChannels = std :: min ( allGather3Data [ i ]. ring . sameChannels , ringGraph . sameChannels );
ringGraph . speedIntra = std :: min ( allGather3Data [ i ]. ring . speedIntra , ringGraph . speedIntra );
ringGraph . speedInter = std :: min ( allGather3Data [ i ]. ring . speedInter , ringGraph . speedInter );
2020-01-16 16:02:42 -08:00
ringGraph . typeIntra = std :: min ( allGather3Data [ i ]. ring . typeIntra , ringGraph . typeIntra );
collNetGraph . sameChannels = std :: min ( allGather3Data [ i ]. collNet . sameChannels , collNetGraph . sameChannels );
collNetGraph . speedIntra = std :: min ( allGather3Data [ i ]. collNet . speedIntra , collNetGraph . speedIntra );
collNetGraph . speedInter = std :: min ( allGather3Data [ i ]. collNet . speedInter , collNetGraph . speedInter );
collNetGraph . typeIntra = std :: min ( allGather3Data [ i ]. collNet . typeIntra , collNetGraph . typeIntra );
2019-11-19 14:57:39 -08:00
}
2018-12-13 15:56:12 -08:00
2019-11-19 14:57:39 -08:00
if ( comm -> nChannels < nChannelsOrig ) {
// We started duplicating channels during Preset(), so we need to move the
// duplicated channels since we have removed some.
for ( int i = 0 ; i < comm -> nChannels ; i ++ ) memcpy ( comm -> channels + comm -> nChannels + i , comm -> channels + nChannelsOrig + i , sizeof ( struct ncclChannel ));
2018-09-24 16:06:59 -07:00
}
2018-12-13 15:56:12 -08:00
2018-09-24 16:06:59 -07:00
int * rings ;
2018-12-13 15:56:12 -08:00
NCCLCHECK ( ncclCalloc ( & rings , nranks * MAXCHANNELS ));
2019-11-19 14:57:39 -08:00
NCCLCHECK ( ncclTopoPostset ( comm , nodesFirstRank , allTopoRanks , rings ));
2020-01-16 16:02:42 -08:00
if ( comm -> nNodes > 1 &&
ncclParamCollNetEnable () == 1 &&
collNetSupport ()) {
NCCLCHECK ( ncclTopoConnectCollNet ( comm , & collNetGraph , rank ));
}
2019-11-19 14:57:39 -08:00
free ( allTopoRanks );
free ( nodesFirstRank );
free ( allGather3Data );
// AllGather3 - end
TRACE ( NCCL_INIT , "rank %d nranks %d - BUILT %d TREES/RINGS" , rank , nranks , comm -> nChannels );
2020-01-16 16:02:42 -08:00
NCCLCHECK ( ncclTopoSetThresholds ( comm , minCompCap , maxCompCap , & treeGraph , & ringGraph , & collNetGraph ));
2019-11-19 14:57:39 -08:00
char line [ 1024 ];
line [ 0 ] = '\0' ;
for ( int c = 0 ; c < comm -> nChannels ; c ++ ) {
struct ncclTree * treeUp = & comm -> channels [ c ]. treeUp ;
struct ncclTree * treeDn = & comm -> channels [ c ]. treeDn ;
snprintf ( line + strlen ( line ), 1023 - strlen ( line ), " [%d] %d/%d/%d->%d->%d|%d->%d->%d/%d/%d" ,
c , treeUp -> down [ 0 ], treeUp -> down [ 1 ], treeUp -> down [ 2 ], rank , treeUp -> up ,
treeDn -> up , rank , treeDn -> down [ 0 ], treeDn -> down [ 1 ], treeDn -> down [ 2 ]);
}
line [ 1023 ] = '\0' ;
INFO ( NCCL_INIT , "Trees%s" , line );
2018-09-24 16:06:59 -07:00
2020-01-16 16:02:42 -08:00
// Set Affinity to a CPU local the our GPU, so that all memory we allocate
// on the host is local.
cpu_set_t affinitySave ;
sched_getaffinity ( 0 , sizeof ( cpu_set_t ), & affinitySave );
NCCLCHECK ( ncclTopoSetAffinity ( comm -> topo , comm -> rank ));
ncclResult_t ret ;
2018-09-24 16:06:59 -07:00
// Connect with prev/next for each ring
2018-12-13 15:56:12 -08:00
struct ncclConnect * connect ;
2020-01-16 16:02:42 -08:00
NCCLCHECKGOTO ( ncclCalloc ( & connect , 2 ), ret , affinity_restore );
2019-11-19 14:57:39 -08:00
for ( int c = 0 ; c < comm -> nChannels ; c ++ ) {
struct ncclChannel * channel = comm -> channels + c ;
2020-01-16 16:02:42 -08:00
NCCLCHECKGOTO ( setupChannel ( comm , c , rank , nranks , rings + c * nranks ), ret , affinity_restore );
2019-11-19 14:57:39 -08:00
if ( comm -> nRanks == 1 ) continue ;
2020-01-16 16:02:42 -08:00
NCCLCHECKGOTO ( p2pSetup ( comm , & ringGraph , channel , 1 , & channel -> ring . prev , 1 , & channel -> ring . next ), ret , affinity_restore );
NCCLCHECKGOTO ( p2pSetup ( comm , & treeGraph , channel , NCCL_MAX_TREE_ARITY , channel -> treeUp . down , 1 , & channel -> treeUp . up ), ret , affinity_restore );
NCCLCHECKGOTO ( p2pSetup ( comm , & treeGraph , channel , 1 , & channel -> treeDn . up , NCCL_MAX_TREE_ARITY , channel -> treeDn . down ), ret , affinity_restore );
}
// Check if we can setup CollNet
if ( comm -> nNodes > 1 &&
ncclParamCollNetEnable () == 1 &&
collNetSupport ()) {
int logicChannels = comm -> nChannels / 2 ;
int collNetSetupFail = 0 ;
const int recvIndex = 0 ; // recv GPU index is always 0
const int sendIndex = collNetGraph . pattern == NCCL_TOPO_PATTERN_TREE ? 0 : 1 ; // send GPU index depends on topo pattern
for ( int c = 0 ; c < logicChannels ; c ++ ) {
struct ncclChannel * channelRecv = comm -> channels + logicChannels + c ;
struct ncclChannel * channelSend = comm -> channels + c ;
NCCLCHECK ( p2pSetup ( comm , & collNetGraph , channelRecv , 1 , & channelRecv -> collTreeDn . up , 1 , channelRecv -> collTreeDn . down ));
NCCLCHECK ( p2pSetup ( comm , & collNetGraph , channelSend , 1 , channelSend -> collTreeUp . down , 1 , & channelSend -> collTreeUp . up ));
const int recvMaster = collNetGraph . intra [ c * comm -> localRanks + recvIndex ];
const int sendMaster = collNetGraph . intra [ c * comm -> localRanks + sendIndex ];
if ( collNetSetup ( comm , & collNetGraph , channelRecv , logicChannels , rank , nranks , recvMaster , sendMaster , comm -> nNodes , 1 ) != 1 )
collNetSetupFail = 1 ;
if ( collNetSetup ( comm , & collNetGraph , channelSend , logicChannels , rank , nranks , sendMaster , recvMaster , comm -> nNodes , 0 ) != 1 )
collNetSetupFail = 1 ;
}
// Verify CollNet setup across ranks
NCCLCHECK ( checkCollNetSetup ( comm , rank , collNetSetupFail ));
2019-11-19 14:57:39 -08:00
}
TRACE ( NCCL_INIT , "rank %d nranks %d - CONNECTED %d RINGS AND TREES" , rank , nranks , comm -> nChannels );
2018-12-13 15:56:12 -08:00
free ( connect );
free ( rings );
2018-09-24 16:06:59 -07:00
2020-01-16 16:02:42 -08:00
// We should have allocated all buffers, collective fifos, ... we can
// restore the affinity.
affinity_restore :
sched_setaffinity ( 0 , sizeof ( cpu_set_t ), & affinitySave );
if ( ret != ncclSuccess ) return ret ;
2018-12-13 15:56:12 -08:00
// Compute intra ranks (using AllGather1 data)
2018-09-24 16:06:59 -07:00
int intraRank0 = - 1 , intraRank = - 1 , intraRanks = 0 ;
2018-12-13 15:56:12 -08:00
for ( int i = 0 ; i < nranks ; i ++ ) {
if (( allGather1Data [ i ]. peerInfo . hostHash == allGather1Data [ rank ]. peerInfo . hostHash ) &&
( allGather1Data [ i ]. peerInfo . pidHash == allGather1Data [ rank ]. peerInfo . pidHash )) {
if ( intraRanks == 0 ) intraRank0 = i ;
if ( i == rank ) intraRank = intraRanks ;
2018-09-24 16:06:59 -07:00
intraRanks ++ ;
}
}
2018-11-13 10:37:20 -08:00
TRACE ( NCCL_INIT , "hostHash[%d] %lx intraRank %d intraRanks %d intraRank0 %d" ,
2018-12-13 15:56:12 -08:00
rank , allGather1Data [ rank ]. peerInfo . hostHash , intraRank , intraRanks , intraRank0 );
if ( intraRank == - 1 || intraRank0 == - 1 || allGather1Data [ intraRank0 ]. comm == NULL ) {
2018-09-24 16:06:59 -07:00
WARN ( "Failed to determine intra ranks hostHash[%d] %lx intraRank %d intraRanks %d intraRank0 %d" ,
2018-12-13 15:56:12 -08:00
rank , allGather1Data [ rank ]. peerInfo . hostHash , intraRank , intraRanks , intraRank0 );
2018-09-24 16:06:59 -07:00
return ncclInternalError ;
}
2018-12-13 15:56:12 -08:00
NCCLCHECK ( ncclCommSetIntra ( comm , intraRank , intraRanks , allGather1Data [ intraRank0 ]. comm ));
2018-09-24 16:06:59 -07:00
2018-12-13 15:56:12 -08:00
// Done with AllGather1 data
free ( allGather1Data );
2018-10-24 14:44:59 -07:00
2019-11-19 14:57:39 -08:00
if ( comm -> nNodes ) NCCLCHECK ( transportCreateProxy ( comm ));
2018-12-13 15:56:12 -08:00
TRACE ( NCCL_INIT , "rank %d nranks %d - DONE" , rank , nranks );
2018-09-24 16:06:59 -07:00
return ncclSuccess ;
}
2019-11-19 14:57:39 -08:00
ncclResult_t ncclCommInitRankSync ( ncclComm_t * newcomm , int nranks , ncclUniqueId commId , int myrank , int cudaDev ) {
2018-09-24 16:06:59 -07:00
ncclResult_t res ;
2020-04-01 13:21:38 -07:00
CUDACHECK ( hipSetDevice ( cudaDev ));
2018-10-24 14:44:59 -07:00
NCCLCHECKGOTO ( commAlloc ( newcomm , nranks , myrank ), res , cleanup );
2018-09-24 16:06:59 -07:00
NCCLCHECKGOTO ( initTransportsRank ( * newcomm , & commId ), res , cleanup );
NCCLCHECKGOTO ( devCommSetup ( * newcomm ), res , cleanup );
2019-11-19 14:57:39 -08:00
INFO ( NCCL_INIT , "comm %p rank %d nranks %d cudaDev %d busId %x - Init COMPLETE" , * newcomm , myrank , nranks , ( * newcomm ) -> cudaDev , ( * newcomm ) -> busId );
2018-10-24 14:44:59 -07:00
2018-09-24 16:06:59 -07:00
return ncclSuccess ;
cleanup :
2019-11-19 14:57:39 -08:00
if (( * newcomm ) && ( * newcomm ) -> bootstrap ) bootstrapAbort (( * newcomm ) -> bootstrap );
2018-09-24 16:06:59 -07:00
* newcomm = NULL ;
return res ;
}
2019-11-19 14:57:39 -08:00
static ncclResult_t ncclCommInitRankDev ( ncclComm_t * newcomm , int nranks , ncclUniqueId commId , int myrank , int cudaDev ) {
ncclResult_t res ;
2018-09-24 16:06:59 -07:00
char * env = getenv ( "NCCL_COMM_ID" );
if ( env && myrank == 0 ) {
2019-11-19 14:57:39 -08:00
NCCLCHECKGOTO ( bootstrapCreateRoot ( & commId , true ), res , end );
2018-09-24 16:06:59 -07:00
}
2019-11-19 14:57:39 -08:00
NCCLCHECKGOTO ( ncclInit (), res , end );
2018-09-24 16:06:59 -07:00
if ( myrank == 0 ) showVersion ();
// Make sure the CUDA runtime is initialized.
2019-11-21 13:41:10 -08:00
CUDACHECKGOTO ( hipFree ( NULL ), res , end );
2018-09-24 16:06:59 -07:00
2019-11-19 14:57:39 -08:00
NCCLCHECKGOTO ( PtrCheck ( newcomm , "CommInitRank" , "newcomm" ), res , end );
2018-09-24 16:06:59 -07:00
if ( nranks < 1 || myrank < 0 || myrank >= nranks ) {
WARN ( "Invalid rank requested : %d/%d" , myrank , nranks );
2019-11-19 14:57:39 -08:00
res = ncclInvalidArgument ;
goto end ;
2018-09-24 16:06:59 -07:00
}
if ( ncclAsyncMode ()) {
2019-11-19 14:57:39 -08:00
NCCLCHECKGOTO ( ncclAsyncInit ( ncclCommInitRankSync , newcomm , nranks , commId , myrank , cudaDev ), res , end );
2018-09-24 16:06:59 -07:00
} else {
2019-11-19 14:57:39 -08:00
NCCLCHECKGOTO ( ncclCommInitRankSync ( newcomm , nranks , commId , myrank , cudaDev ), res , end );
2018-09-24 16:06:59 -07:00
}
2019-11-19 14:57:39 -08:00
end :
if ( ncclAsyncMode ()) return ncclAsyncErrCheck ( res );
else return res ;
2018-09-24 16:06:59 -07:00
}
2019-11-19 14:57:39 -08:00
NCCL_API ( ncclResult_t , ncclCommInitRank , ncclComm_t * newcomm , int nranks , ncclUniqueId commId , int myrank );
ncclResult_t ncclCommInitRank ( ncclComm_t * newcomm , int nranks , ncclUniqueId commId , int myrank ) {
int cudaDev ;
2019-11-21 13:41:10 -08:00
CUDACHECK ( hipGetDevice ( & cudaDev ));
2019-11-19 14:57:39 -08:00
NCCLCHECK ( ncclCommInitRankDev ( newcomm , nranks , commId , myrank , cudaDev ));
2018-09-24 16:06:59 -07:00
return ncclSuccess ;
}
NCCL_API ( ncclResult_t , ncclCommInitAll , ncclComm_t * comms , int ndev , const int * devlist );
ncclResult_t ncclCommInitAll ( ncclComm_t * comms , int ndev , const int * devlist ) {
NCCLCHECK ( PtrCheck ( comms , "CommInitAll" , "comms" ));
2019-11-19 14:57:39 -08:00
if ( ndev < 0 ) {
2018-09-24 16:06:59 -07:00
WARN ( "Invalid device count requested : %d" , ndev );
return ncclInvalidArgument ;
}
2019-11-19 14:57:39 -08:00
ncclUniqueId uniqueId ;
NCCLCHECK ( ncclGetUniqueId ( & uniqueId ));
NCCLCHECK ( ncclGroupStart ());
2018-09-24 16:06:59 -07:00
for ( int i = 0 ; i < ndev ; i ++ ) {
2019-11-19 14:57:39 -08:00
// Ignore return codes .. we need to call ncclGroupEnd to clean up anyway
ncclCommInitRankDev ( comms + i , ndev , uniqueId , i , devlist ? devlist [ i ] : i );
2018-09-24 16:06:59 -07:00
}
2019-11-19 14:57:39 -08:00
NCCLCHECK ( ncclGroupEnd ());
return ncclSuccess ;
2018-09-24 16:06:59 -07:00
}
2018-12-13 15:56:12 -08:00
static ncclResult_t commDestroy ( ncclComm_t comm ) {
2018-09-24 16:06:59 -07:00
int savedDevice ;
2019-03-14 19:39:20 -07:00
#ifdef ENABLE_TRACE
int rank = comm -> rank ;
#endif
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipGetDevice ( & savedDevice ));
2018-09-24 16:06:59 -07:00
int commDevice = comm -> cudaDev ;
if ( savedDevice != commDevice ) {
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipSetDevice ( commDevice ));
2018-09-24 16:06:59 -07:00
}
2019-07-05 15:43:00 -07:00
TRACE ( NCCL_INIT , "Destroying comm %p rank %d abortFlag %d fatalError %d" , comm , rank , LOAD ( comm -> abortFlag ), comm -> fatalError );
2018-12-13 15:56:12 -08:00
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipStreamSynchronize ( comm -> groupStream ));
2018-12-13 15:56:12 -08:00
NCCLCHECK ( transportDestroyProxy ( comm ));
2018-09-24 16:06:59 -07:00
NCCLCHECK ( commFree ( comm ));
if ( savedDevice != commDevice )
2019-07-05 15:43:00 -07:00
CUDACHECK ( hipSetDevice ( savedDevice ));
2018-09-24 16:06:59 -07:00
2019-03-14 19:39:20 -07:00
TRACE ( NCCL_INIT , "Destroyed comm %p rank %d" , comm , rank );
2018-12-13 15:56:12 -08:00
2018-09-24 16:06:59 -07:00
return ncclSuccess ;
}
2018-12-13 15:56:12 -08:00
NCCL_API ( ncclResult_t , ncclCommDestroy , ncclComm_t comm );
ncclResult_t ncclCommDestroy ( ncclComm_t comm ) {
if ( comm == NULL )
return ncclSuccess ;
2019-11-19 14:57:39 -08:00
TRACE ( NCCL_INIT , "comm %p rank %d nRanks %d cudaDev %d busId %x" , comm , comm -> rank , comm -> nRanks , comm -> cudaDev , comm -> busId );
2019-03-14 19:39:20 -07:00
// Try and prevent a double free of the comm struct (user error)
2019-11-19 14:57:39 -08:00
if ( comm -> rank == - 1 || comm -> nRanks <= 0 || comm -> cudaDev == - 1 || comm -> busId == - 1 ) {
2019-03-14 19:39:20 -07:00
WARN ( "comm %p has already been destroyed" , comm );
return ncclInvalidArgument ;
}
2018-12-13 15:56:12 -08:00
return commDestroy ( comm );
}
NCCL_API ( ncclResult_t , ncclCommAbort , ncclComm_t comm );
ncclResult_t ncclCommAbort ( ncclComm_t comm ) {
if ( comm == NULL )
return ncclSuccess ;
// Ask anything that might still be running on the device to quit
2019-07-05 15:43:00 -07:00
STORE ( comm -> abortFlag , 1 );
2018-12-13 15:56:12 -08:00
2019-07-05 15:43:00 -07:00
// do not destroy comm because kernel maybe still running
// return commDestroy(comm);
return ncclSuccess ;
2018-12-13 15:56:12 -08:00
}
2018-09-24 16:06:59 -07:00
NCCL_API ( const char * , ncclGetErrorString , ncclResult_t code );
const char * ncclGetErrorString ( ncclResult_t code ) {
switch ( code ) {
case ncclSuccess : return "no error" ;
case ncclUnhandledCudaError : return "unhandled cuda error" ;
case ncclSystemError : return "unhandled system error" ;
case ncclInternalError : return "internal error" ;
case ncclInvalidArgument : return "invalid argument" ;
case ncclInvalidUsage : return "invalid usage" ;
default : return "unknown result code" ;
}
}
2018-12-13 15:56:12 -08:00
NCCL_API ( ncclResult_t , ncclCommGetAsyncError , ncclComm_t comm , ncclResult_t * asyncError );
ncclResult_t ncclCommGetAsyncError ( ncclComm_t comm , ncclResult_t * asyncError ) {
NCCLCHECK ( PtrCheck ( comm , "ncclGetAsyncError" , "comm" ));
NCCLCHECK ( PtrCheck ( asyncError , "ncclGetAsyncError" , "asyncError" ));
// Check device reported error
static ncclDevError_t printedDevErr = ncclDevSuccess ;
2019-07-05 15:43:00 -07:00
switch ( LOAD ( comm -> fatalDevError )) {
2018-12-13 15:56:12 -08:00
case ncclDevSuccess :
break ;
case ncclDevAssertedMismatch :
if ( printedDevErr != ncclDevAssertedMismatch ) {
WARN ( "Mismatched collective detected, please check your collective calls at and around rank %d. You can use NCCL_DEBUG=INFO and NCCL_DEBUG_SUBSYS=COLL to see the collective logs" , comm -> rank );
printedDevErr = ncclDevAssertedMismatch ;
}
if ( comm -> fatalError == ncclSuccess ) {
comm -> fatalError = ncclInvalidUsage ;
}
break ;
case ncclDevSuspectedMismatch :
if ( printedDevErr != ncclDevSuspectedMismatch ) {
WARN ( "Your program may be hanging, this may be caused by a collective mismatch around rank %d. Please check your collective calls at and around this rank. You can use NCCL_DEBUG=INFO and NCCL_DEBUG_SUBSYS=COLL to see the collective logs" , comm -> rank );
printedDevErr = ncclDevSuspectedMismatch ;
}
break ;
default :
WARN ( "Unknown device error %d" , * comm -> fatalDevError );
return ncclInternalError ;
}
* asyncError = comm -> fatalError ;
return ncclSuccess ;
}
2018-09-24 16:06:59 -07:00
NCCL_API ( ncclResult_t , ncclCommCount , const ncclComm_t comm , int * count );
ncclResult_t ncclCommCount ( const ncclComm_t comm , int * count ) {
NCCLCHECK ( PtrCheck ( comm , "CommCount" , "comm" ));
NCCLCHECK ( PtrCheck ( count , "CommCount" , "count" ));
* count = comm -> nRanks ;
return ncclSuccess ;
}
NCCL_API ( ncclResult_t , ncclCommCuDevice , const ncclComm_t comm , int * devid );
ncclResult_t ncclCommCuDevice ( const ncclComm_t comm , int * devid ) {
NCCLCHECK ( PtrCheck ( comm , "CommCuDevice" , "comm" ));
NCCLCHECK ( PtrCheck ( devid , "CommCuDevice" , "devid" ));
* devid = comm -> cudaDev ;
return ncclSuccess ;
}
NCCL_API ( ncclResult_t , ncclCommUserRank , const ncclComm_t comm , int * rank );
ncclResult_t ncclCommUserRank ( const ncclComm_t comm , int * rank ) {
NCCLCHECK ( PtrCheck ( comm , "CommUserRank" , "comm" ));
NCCLCHECK ( PtrCheck ( rank , "CommUserRank" , "rank" ));
* rank = comm -> rank ;
return ncclSuccess ;
}