2018-09-24 16:06:59 -07:00
/*************************************************************************
2018-12-13 15:56:12 -08:00
* Copyright (c) 2015-2019, NVIDIA CORPORATION. All rights reserved.
2019-07-05 15:43:00 -07:00
* Modifications Copyright (c) 2019 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"
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-12-13 15:56:12 -08:00
#include "cpuset.h"
2018-09-24 16:06:59 -07:00
#include <sched.h>
#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>
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 ;
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 ;
}
ncclResult_t initNetPlugin ( ncclNet_t ** net ) {
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." );
2018-11-13 10:37:20 -08:00
goto cleanup ;
}
if ( initNet ( extNet ) == ncclSuccess ) {
* net = extNet ;
return ncclSuccess ;
}
cleanup :
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
NCCLCHECK ( initNetPlugin ( & ncclNet ));
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
}
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 ();
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
void __attribute__ (( optimize ( "O0" ))) 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
}
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
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
* 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 ));
NCCLCHECK ( ncclCudaMemcpy ( comm -> channels [ r ]. devPeers , comm -> channels [ r ]. peers , comm -> nRanks ));
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__)
2019-07-05 15:43:00 -07:00
#define VERSION_STRING "NCCL version " STR(NCCL_MAJOR) "." STR(NCCL_MINOR) "." STR(NCCL_PATCH) NCCL_SUFFIX "+hip"
#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 ;
int netDevs ;
NCCLCHECK ( ncclNetDevices ( & netDevs ));
for ( int n = 0 ; n < netDevs ; n ++ ) {
int ptrSupport ;
NCCLCHECK ( ncclNetPtrSupport ( n , & ptrSupport ));
if ( ptrSupport & NCCL_PTR_CUDA ) info -> gdrSupport |= ( 1 << n );
}
2018-09-24 16:06:59 -07:00
return ncclSuccess ;
}
2019-07-05 15:43:00 -07:00
static ncclResult_t setCpuAffinity ( int cudaDev );
2018-09-24 16:06:59 -07:00
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 ) {
2019-07-05 15:43:00 -07:00
cpu_set_t affinitySave ;
sched_getaffinity ( 0 , sizeof ( cpu_set_t ), & affinitySave );
int cudaDev ;
CUDACHECK ( hipGetDevice ( & cudaDev ));
setCpuAffinity ( cudaDev );
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 ));
2019-07-05 15:43:00 -07:00
sched_setaffinity ( 0 , sizeof ( cpu_set_t ), & affinitySave );
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 ];
if ( peer == - 1 ) continue ;
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 ];
if ( peer == - 1 ) continue ;
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 ];
if ( peer == - 1 ) continue ;
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 )));
NCCLCHECK ( conn -> transportComm -> connect ( & connect , conn ));
conn -> connected = 1 ;
}
for ( int i = 0 ; i < nrecv ; i ++ ) {
int peer = peerRecv [ i ];
if ( peer == - 1 ) continue ;
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 )));
NCCLCHECK ( conn -> transportComm -> connect ( & connect , conn ));
conn -> connected = 1 ;
}
TRACE ( NCCL_INIT , "nsend %d nrecv %d nSkippedSend %u nSkippedRecv %u - DONE" , nsend , nrecv , nSkippedSend , nSkippedRecv );
return ncclSuccess ;
}
2019-11-19 14:57:39 -08:00
NCCL_PARAM ( CrossNic , "CROSS_NIC" , 2 );
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 )));
NCCLCHECK ( ncclCalloc ( & comm -> peerInfo , nranks ));
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 ));
// Compute max speed to accelerate search
NCCLCHECK ( ncclTopoGetMaxSpeed ( comm -> topo ));
// Print final topology
NCCLCHECK ( ncclTopoPrint ( comm -> topo ));
// Get rings and trees
struct ncclTopoGraph treeGraph ;
treeGraph . pattern = NCCL_TOPO_PATTERN_SPLIT_TREE ;
treeGraph . crossNic = ncclParamCrossNic ();
// We communicate only half the data between node with trees on 2 nodes.
NCCLCHECK ( ncclTopoCompute ( comm -> topo , & treeGraph ));
NCCLCHECK ( ncclTopoPrintGraph ( comm -> topo , & treeGraph ));
struct ncclTopoGraph ringGraph ;
ringGraph . pattern = NCCL_TOPO_PATTERN_RING ;
ringGraph . crossNic = ncclParamCrossNic ();
NCCLCHECK ( ncclTopoCompute ( comm -> topo , & ringGraph ));
NCCLCHECK ( ncclTopoPrintGraph ( comm -> topo , & ringGraph ));
2018-09-24 16:06:59 -07:00
2018-12-13 15:56:12 -08:00
// AllGather3 - begin
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 nvlink ;
int nChannels ;
struct {
int sameChannels ;
int speedIntra ;
int speedInter ;
int nvlink ;
} tree ;
struct {
int sameChannels ;
int speedIntra ;
int speedInter ;
int nvlink ;
} ring ;
struct ncclTopoRanks topoRanks ;
2018-12-13 15:56:12 -08:00
} * allGather3Data ;
NCCLCHECK ( ncclCalloc ( & allGather3Data , nranks ));
allGather3Data [ rank ]. cudaCompCap = ncclCudaCompCap ();
2019-11-19 14:57:39 -08:00
allGather3Data [ rank ]. nvlink = treeGraph . nvlink ;
allGather3Data [ rank ]. nChannels = comm -> nChannels = std :: min ( treeGraph . nChannels , ringGraph . nChannels );
allGather3Data [ rank ]. tree . sameChannels = treeGraph . sameChannels ;
allGather3Data [ rank ]. tree . speedIntra = treeGraph . speedIntra ;
allGather3Data [ rank ]. tree . speedInter = treeGraph . speedInter ;
allGather3Data [ rank ]. tree . nvlink = treeGraph . nvlink ;
allGather3Data [ rank ]. ring . sameChannels = ringGraph . sameChannels ;
allGather3Data [ rank ]. ring . speedIntra = ringGraph . speedIntra ;
allGather3Data [ rank ]. ring . speedInter = ringGraph . speedInter ;
allGather3Data [ rank ]. ring . nvlink = ringGraph . nvlink ;
NCCLCHECK ( ncclTopoPreset ( comm , & treeGraph , & ringGraph , & allGather3Data [ rank ]. topoRanks ));
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
comm -> nvlink = 1 ;
for ( int i = 0 ; i < nranks ; i ++ ) comm -> nvlink &= allGather3Data [ i ]. nvlink ;
2018-09-24 16:06:59 -07: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 );
treeGraph . nvlink = std :: min ( allGather3Data [ i ]. tree . nvlink , treeGraph . nvlink );
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 );
ringGraph . nvlink = std :: min ( allGather3Data [ i ]. ring . nvlink , ringGraph . nvlink );
}
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 ));
free ( allTopoRanks );
free ( nodesFirstRank );
free ( allGather3Data );
// AllGather3 - end
TRACE ( NCCL_INIT , "rank %d nranks %d - BUILT %d TREES/RINGS" , rank , nranks , comm -> nChannels );
NCCLCHECK ( ncclSetThresholds ( comm , minCompCap , maxCompCap , & treeGraph , & ringGraph ));
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
// Connect with prev/next for each ring
2018-12-13 15:56:12 -08:00
struct ncclConnect * connect ;
NCCLCHECK ( ncclCalloc ( & connect , 2 ));
2019-11-19 14:57:39 -08:00
for ( int c = 0 ; c < comm -> nChannels ; c ++ ) {
struct ncclChannel * channel = comm -> channels + c ;
NCCLCHECK ( setupChannel ( comm , c , rank , nranks , rings + c * nranks ));
if ( comm -> nRanks == 1 ) continue ;
NCCLCHECK ( p2pSetup ( comm , & ringGraph , channel , 1 , & channel -> ring . prev , 1 , & channel -> ring . next ));
NCCLCHECK ( p2pSetup ( comm , & treeGraph , channel , NCCL_MAX_TREE_ARITY , channel -> treeUp . down , 1 , & channel -> treeUp . up ));
NCCLCHECK ( p2pSetup ( comm , & treeGraph , channel , 1 , & channel -> treeDn . up , NCCL_MAX_TREE_ARITY , channel -> treeDn . down ));
}
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
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 ;
}
2018-12-13 15:56:12 -08:00
static ncclResult_t getCpuGpuAffinity ( int cudaDev , cpu_set_t * mask ) {
CPU_ZERO_S ( sizeof ( cpu_set_t ), mask );
char * cudaPath ;
2019-11-19 14:57:39 -08:00
NCCLCHECK ( ncclTopoCudaPath ( cudaDev , & cudaPath ));
2018-12-13 15:56:12 -08:00
char path [ PATH_MAX ];
strncpy ( path , cudaPath , PATH_MAX - 1 );
snprintf ( path + strlen ( path ), PATH_MAX - 1 - strlen ( path ), "/local_cpus" );
path [ PATH_MAX - 1 ] = '\0' ;
int fd ;
SYSCHECKVAL ( open ( path , O_RDONLY ), "open" , fd );
2019-06-21 01:25:08 -07:00
char affinityStr [ sizeof ( cpu_set_t ) * 2 + 1 ];
2018-12-13 15:56:12 -08:00
int r = read ( fd , affinityStr , sizeof ( cpu_set_t ) * 2 );
2019-06-21 01:25:08 -07:00
if ( r > 0 ) {
affinityStr [ r ] = '\0' ;
2018-12-13 15:56:12 -08:00
NCCLCHECK ( ncclStrToCpuset ( affinityStr , mask ));
2019-06-21 01:25:08 -07:00
}
2018-12-13 15:56:12 -08:00
close ( fd );
free ( cudaPath );
return ncclSuccess ;
}
2019-03-14 19:39:20 -07:00
NCCL_PARAM ( IgnoreCpuAffinity , "IGNORE_CPU_AFFINITY" , 0 );
2018-12-13 15:56:12 -08:00
static ncclResult_t setCpuAffinity ( int cudaDev ) {
2019-03-14 19:39:20 -07:00
// Query the CPU affinity set we were provided
2018-12-13 15:56:12 -08:00
cpu_set_t mask ;
SYSCHECK ( sched_getaffinity ( 0 , sizeof ( cpu_set_t ), & mask ), "sched_getaffinity" );
2019-03-14 19:39:20 -07:00
#ifdef ENABLE_TRACE
{
char affinityStr [ sizeof ( cpu_set_t ) * 2 ];
NCCLCHECK ( ncclCpusetToStr ( & mask , affinityStr ));
TRACE ( NCCL_INIT , "Current affinity for GPU %d is %s" , cudaDev , affinityStr );
}
#endif
// Find the CPUs that are local to the supplied GPU
2018-12-13 15:56:12 -08:00
cpu_set_t gpuMask ;
NCCLCHECK ( getCpuGpuAffinity ( cudaDev , & gpuMask ));
2019-03-14 19:39:20 -07:00
#ifdef ENABLE_TRACE
{
char affinityStr [ sizeof ( cpu_set_t ) * 2 ];
NCCLCHECK ( ncclCpusetToStr ( & gpuMask , affinityStr ));
TRACE ( NCCL_INIT , "CPU GPU affinity for GPU %d is %s" , cudaDev , affinityStr );
}
#endif
2018-12-13 15:56:12 -08:00
cpu_set_t finalMask ;
2019-03-14 19:39:20 -07:00
if ( ncclParamIgnoreCpuAffinity ())
// Ignore the CPU affinity set and use the GPU one instead
finalMask = gpuMask ;
else
// Use a subset of the GPU affinity set
CPU_AND ( & finalMask , & mask , & gpuMask );
2018-12-13 15:56:12 -08:00
2019-03-14 19:39:20 -07:00
// If there is a non empty set, use it to set affinity
2018-12-13 15:56:12 -08:00
if ( CPU_COUNT ( & finalMask )) {
char affinityStr [ sizeof ( cpu_set_t ) * 2 ];
NCCLCHECK ( ncclCpusetToStr ( & finalMask , affinityStr ));
INFO ( NCCL_INIT , "Setting affinity for GPU %d to %s" , cudaDev , affinityStr );
SYSCHECK ( sched_setaffinity ( 0 , sizeof ( cpu_set_t ), & finalMask ), "sched_setaffinity" );
2018-09-24 16:06:59 -07:00
}
2018-12-13 15:56:12 -08:00
return ncclSuccess ;
2018-09-24 16:06:59 -07:00
}
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
cpu_set_t affinitySave ;
sched_getaffinity ( 0 , sizeof ( cpu_set_t ), & affinitySave );
NCCLCHECK ( wrapNvmlSymbols ());
NCCLCHECK ( wrapNvmlInit ());
// Make sure all host memory allocation are close to the GPU
2019-11-21 13:41:10 -08:00
CUDACHECK ( hipSetDevice ( cudaDev ));
2018-12-13 15:56:12 -08:00
NCCLCHECK ( setCpuAffinity ( cudaDev ));
2018-09-24 16:06:59 -07:00
ncclResult_t res ;
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 );
sched_setaffinity ( 0 , sizeof ( cpu_set_t ), & affinitySave );
NCCLCHECKGOTO ( wrapNvmlShutdown (), res , cleanup );
2018-10-24 14:44:59 -07:00
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 ;
sched_setaffinity ( 0 , sizeof ( cpu_set_t ), & affinitySave );
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 ;
}