2019-11-19 14:57:39 -08:00
/*************************************************************************
2022-01-07 06:39:55 -08:00
* Copyright (c) 2016-2022, NVIDIA CORPORATION. All rights reserved.
2023-02-04 01:43:38 +00:00
* Modifications Copyright (c) 2019-2023 Advanced Micro Devices, Inc. All rights reserved.
2019-11-19 14:57:39 -08:00
*
* See LICENSE.txt for license information
************************************************************************/
#include "core.h"
#include "graph.h"
#include "topo.h"
#include "comm.h"
#include "nvmlwrap.h"
#include "net.h"
2020-01-16 16:02:42 -08:00
#include "coll_net.h"
2019-11-19 14:57:39 -08:00
#include <sys/stat.h>
#include <fcntl.h>
2020-01-16 16:02:42 -08:00
#include "xml.h"
#include "cpuset.h"
2019-11-19 14:57:39 -08:00
#define BUSID_SIZE (sizeof("0000:00:00.0"))
#define BUSID_REDUCED_SIZE (sizeof("0000:00"))
const char * topoNodeTypeStr [] = { "GPU" , "PCI" , "NVS" , "CPU" , "NIC" , "NET" };
2019-11-21 13:41:10 -08:00
#if defined(__HIP_PLATFORM_HCC__) || defined(__HCC__) || defined(__HIPCC__)
2022-04-18 11:14:51 -07:00
const char * topoLinkTypeStr [] = { "LOC" , "XGMI" , "" , "PCI" , "" , "" , "" , "SYS" , "NET" };
const char * topoPathTypeStr [] = { "LOC" , "XGMI" , "NVB" , "PIX" , "PXB" , "PXN" , "PHB" , "SYS" , "DIS" };
2019-11-21 13:41:10 -08:00
#else
2022-01-07 06:39:55 -08:00
const char * topoLinkTypeStr [] = { "LOC" , "NVL" , "" , "PCI" , "" , "" , "" , "SYS" , "NET" };
const char * topoPathTypeStr [] = { "LOC" , "NVL" , "NVB" , "PIX" , "PXB" , "PXN" , "PHB" , "SYS" , "DIS" };
2020-02-03 22:06:44 +00:00
#endif
2019-11-19 14:57:39 -08:00
/******************************************************************/
/******************* Graph Creation Functions *********************/
/******************************************************************/
// Get an int64 from a PCI path. For example, sys/class/pci0000:00/0000:00:02.0/0000:02:00.0/ will return 0x000002000.
ncclResult_t pciPathToInt64 ( char * path , int offset , int minOffset , int64_t * id ) {
char * str = path + offset ;
// Remove trailing "/"
if ( * str == '/' ) str -- ;
// Find next /
while ( * str != '/' ) str -- ;
str ++ ;
2020-01-16 16:02:42 -08:00
int64_t numid ;
NCCLCHECK ( busIdToInt64 ( str , & numid ));
// Ignore subdevice because those should use the same PCI link so we want to merge nodes.
numid -= numid & 0xf ;
* id = numid ;
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
static ncclResult_t findLocalCpu ( struct ncclTopoNode * node , struct ncclTopoNode ** cpu ) {
* cpu = NULL ;
if ( node -> type == CPU ) {
* cpu = node ;
return ncclSuccess ;
}
for ( int l = 0 ; l < node -> nlinks ; l ++ ) {
if ( node -> links [ l ]. type == LINK_PCI ) NCCLCHECK ( findLocalCpu ( node -> links [ l ]. remNode , cpu ));
if ( * cpu != NULL ) return ncclSuccess ;
2019-11-19 14:57:39 -08:00
}
return ncclSuccess ;
}
2022-08-18 02:53:17 -07:00
int interCpuBw = 0 ;
int cpuPciBw = 0 ;
2019-11-19 14:57:39 -08:00
2022-08-18 02:53:17 -07:00
static ncclResult_t ncclTopoGetInterCpuBw ( struct ncclTopoNode * cpu , float * bw ) {
* bw = LOC_BW ;
2020-01-16 16:02:42 -08:00
if ( cpu -> cpu . arch == NCCL_TOPO_CPU_ARCH_POWER ) {
2022-08-18 02:53:17 -07:00
* bw = P9_BW ;
2020-01-16 16:02:42 -08:00
return ncclSuccess ;
2019-11-26 16:33:13 -08:00
}
2020-01-16 16:02:42 -08:00
if ( cpu -> cpu . arch == NCCL_TOPO_CPU_ARCH_ARM ) {
2022-08-18 02:53:17 -07:00
* bw = ARM_BW ;
2020-01-16 16:02:42 -08:00
return ncclSuccess ;
}
if ( cpu -> cpu . arch == NCCL_TOPO_CPU_ARCH_X86 && cpu -> cpu . vendor == NCCL_TOPO_CPU_VENDOR_INTEL ) {
2022-08-18 02:53:17 -07:00
* bw = cpu -> cpu . model == NCCL_TOPO_CPU_TYPE_SKL ? SKL_QPI_BW : QPI_BW ;
2019-11-19 14:57:39 -08:00
}
2020-11-27 16:20:55 +08:00
if ( cpu -> cpu . arch == NCCL_TOPO_CPU_ARCH_X86 && cpu -> cpu . vendor == NCCL_TOPO_CPU_VENDOR_ZHAOXIN ) {
2022-08-18 02:53:17 -07:00
* bw = cpu -> cpu . model == NCCL_TOPO_CPU_TYPE_YONGFENG ? YONGFENG_ZPI_BW : ZPI_BW ;
2020-11-27 16:20:55 +08:00
}
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
enum ncclNvLinkDeviceType {
ncclNvLinkDeviceUnknown ,
ncclNvLinkDeviceGpu ,
ncclNvLinkDeviceSwitch ,
ncclNvLinkDeviceBridge , // IBM/Power NVLink bridge (Device 04ea)
};
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoGetNode ( struct ncclTopoSystem * system , struct ncclTopoNode ** node , int type , uint64_t id ) {
for ( int i = 0 ; i < system -> nodes [ type ]. count ; i ++ ) {
if ( system -> nodes [ type ]. nodes [ i ]. id == id ) {
* node = system -> nodes [ type ]. nodes + i ;
return ncclSuccess ;
}
2019-11-19 14:57:39 -08:00
}
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoCreateNode ( struct ncclTopoSystem * system , struct ncclTopoNode ** node , int type , uint64_t id ) {
if ( system -> nodes [ type ]. count == NCCL_TOPO_MAX_NODES ) {
2021-02-09 15:34:08 -08:00
WARN ( "Error : tried to create too many nodes of type %d" , type );
2020-01-16 16:02:42 -08:00
return ncclInternalError ;
2019-11-19 14:57:39 -08:00
}
2020-01-16 16:02:42 -08:00
struct ncclTopoNode * n = system -> nodes [ type ]. nodes + system -> nodes [ type ]. count ;
system -> nodes [ type ]. count ++ ;
n -> type = type ;
n -> id = id ;
if ( type == GPU ) {
// Create link to itself (used in some corner cases)
n -> nlinks = 1 ;
n -> links [ 0 ]. type = LINK_LOC ;
n -> links [ 0 ]. remNode = n ;
2022-08-18 02:53:17 -07:00
n -> links [ 0 ]. bw = LOC_BW ;
2020-01-16 16:02:42 -08:00
n -> gpu . dev = NCCL_TOPO_UNDEF ;
n -> gpu . rank = NCCL_TOPO_UNDEF ;
n -> gpu . cudaCompCap = NCCL_TOPO_UNDEF ;
} else if ( type == CPU ) {
n -> cpu . arch = NCCL_TOPO_UNDEF ;
n -> cpu . vendor = NCCL_TOPO_UNDEF ;
n -> cpu . model = NCCL_TOPO_UNDEF ;
} else if ( type == NET ) {
n -> net . asic = 0ULL ;
n -> net . port = NCCL_TOPO_UNDEF ;
2022-08-18 02:53:17 -07:00
n -> net . bw = 0.0 ;
2022-01-07 06:39:55 -08:00
n -> net . latency = 0.0 ;
2019-11-19 14:57:39 -08:00
}
2020-01-16 16:02:42 -08:00
* node = n ;
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoRemoveNode ( struct ncclTopoSystem * system , int type , int index ) {
struct ncclTopoNode * delNode = system -> nodes [ type ]. nodes + index ;
for ( int t = 0 ; t < NCCL_TOPO_NODE_TYPES ; t ++ ) {
free ( delNode -> paths [ t ]);
for ( int n = 0 ; n < system -> nodes [ t ]. count ; n ++ ) {
struct ncclTopoNode * node = system -> nodes [ t ]. nodes + n ;
if ( node == delNode ) continue ;
for ( int l = 0 ; l < node -> nlinks ; l ++ ) {
while ( l < node -> nlinks && node -> links [ l ]. remNode == delNode ) {
memmove ( node -> links + l , node -> links + l + 1 , ( node -> nlinks - l - 1 ) * sizeof ( struct ncclTopoLink ));
node -> nlinks -- ;
2019-11-21 13:41:10 -08:00
}
2020-01-16 16:02:42 -08:00
if ( l < node -> nlinks && node -> links [ l ]. remNode -> type == type && node -> links [ l ]. remNode >= delNode ) {
node -> links [ l ]. remNode -- ;
2019-11-19 14:57:39 -08:00
}
}
}
}
2020-01-16 16:02:42 -08:00
memmove ( delNode , delNode + 1 , ( system -> nodes [ type ]. count - index - 1 ) * sizeof ( struct ncclTopoNode ));
system -> nodes [ type ]. count -- ;
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2022-08-18 02:53:17 -07:00
ncclResult_t ncclTopoConnectNodes ( struct ncclTopoNode * node , struct ncclTopoNode * remNode , int type , float bw ) {
// Aggregate links into higher bw for NVLink
2020-01-16 16:02:42 -08:00
struct ncclTopoLink * link ;
for ( link = node -> links ; link -> remNode ; link ++ ) {
if ( link -> remNode == remNode && link -> type == type ) break ;
2019-11-19 14:57:39 -08:00
}
2020-01-16 16:02:42 -08:00
if ( link -> remNode == NULL ) node -> nlinks ++ ;
link -> type = type ;
link -> remNode = remNode ;
2022-08-18 02:53:17 -07:00
link -> bw += bw ;
2020-01-16 16:02:42 -08:00
// Sort links in BW descending order
struct ncclTopoLink linkSave ;
memcpy ( & linkSave , link , sizeof ( struct ncclTopoLink ));
while ( link != node -> links ) {
2022-08-18 02:53:17 -07:00
if (( link - 1 ) -> bw >= linkSave . bw ) break ;
2020-01-16 16:02:42 -08:00
memcpy ( link , link - 1 , sizeof ( struct ncclTopoLink ));
link -- ;
2019-11-19 14:57:39 -08:00
}
2020-01-16 16:02:42 -08:00
memcpy ( link , & linkSave , sizeof ( struct ncclTopoLink ));
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2021-04-12 16:00:11 -07:00
// BCM Gen4 Switches present themselves as a two-level hierarchical switch
// even though they're supposed to sustain full BW across all ports.
// Flatten the switch as this extra level can break the search and make
// NCCL take wrong topology decisions.
ncclResult_t ncclTopoFlattenBcmSwitches ( struct ncclTopoSystem * system ) {
for ( int s = 0 ; s < system -> nodes [ PCI ]. count ; s ++ ) {
struct ncclTopoNode * pciSwitch = system -> nodes [ PCI ]. nodes + s ;
uint64_t device = pciSwitch -> pci . device ;
// Only flatten PEX Gen 4 switches in base mode
if (( device & 0xfffffffffffff000 ) == 0x1000c0101000a000 ) {
// Find sub switches with the same device ID.
int64_t * subSwIds ;
NCCLCHECK ( ncclCalloc ( & subSwIds , pciSwitch -> nlinks ));
int subs = 0 ;
for ( int l = 0 ; l < pciSwitch -> nlinks ; l ++ ) {
struct ncclTopoNode * sub = pciSwitch -> links [ l ]. remNode ;
// Only fuse sub switches with the same device ID.
if ( sub -> type != PCI || sub -> pci . device != device ) continue ;
// Save sub switch for later
subSwIds [ subs ++ ] = sub -> id ;
// Remove link to that sub switch
memmove ( pciSwitch -> links + l , pciSwitch -> links + l + 1 , ( pciSwitch -> nlinks - l - 1 ) * ( sizeof ( struct ncclTopoLink )));
pciSwitch -> nlinks -- ;
// Don't increase l for the next iteration as we just shifted all links by one.
l -- ;
}
for ( int s = 0 ; s < subs ; s ++ ) {
// Find sub switch (system->nodes[PCI].nodes is changing every time we remove a node)
int index ;
NCCLCHECK ( ncclTopoIdToIndex ( system , PCI , subSwIds [ s ], & index ));
struct ncclTopoNode * sub = system -> nodes [ PCI ]. nodes + index ;
// Connect all sub PCI devices to the parent switch
for ( int l = 0 ; l < sub -> nlinks ; l ++ ) {
struct ncclTopoNode * remNode = sub -> links [ l ]. remNode ;
if ( remNode == pciSwitch ) continue ;
// Add link from parent PCI switch -> PCI device
memcpy ( pciSwitch -> links + pciSwitch -> nlinks , sub -> links + l , sizeof ( struct ncclTopoLink ));
pciSwitch -> nlinks ++ ;
// Update link from PCI device -> parent PCI switch
for ( int rl = 0 ; rl < remNode -> nlinks ; rl ++ ) {
if ( remNode -> links [ rl ]. remNode == sub ) {
remNode -> links [ rl ]. remNode = pciSwitch ;
break ;
}
}
}
NCCLCHECK ( ncclTopoRemoveNode ( system , PCI , index ));
}
// Set subdevice to 0x0000 to make sure we don't merge this switch again.
pciSwitch -> pci . device = 0x1000c01010000000 ;
free ( subSwIds );
// Restart, as system->nodes[PCI].nodes has changed.
s = 0 ;
}
}
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoConnectCpus ( struct ncclTopoSystem * system ) {
2019-11-19 14:57:39 -08:00
// And connect all CPU nodes together
for ( int n = 0 ; n < system -> nodes [ CPU ]. count ; n ++ ) {
for ( int p = 0 ; p < system -> nodes [ CPU ]. count ; p ++ ) {
if ( n == p ) continue ;
2022-08-18 02:53:17 -07:00
float bw ;
NCCLCHECK ( ncclTopoGetInterCpuBw ( system -> nodes [ CPU ]. nodes + n , & bw ));
NCCLCHECK ( ncclTopoConnectNodes ( system -> nodes [ CPU ]. nodes + n , system -> nodes [ CPU ]. nodes + p , LINK_SYS , bw ));
2019-11-19 14:57:39 -08:00
}
}
return ncclSuccess ;
}
static ncclResult_t ncclTopoPrintRec ( struct ncclTopoNode * node , struct ncclTopoNode * prevNode , char * line , int offset ) {
if ( node -> type == GPU ) {
2020-01-16 16:02:42 -08:00
sprintf ( line + offset , "%s/%lX (%d)" , topoNodeTypeStr [ node -> type ], node -> id , node -> gpu . rank );
} else if ( node -> type == CPU ) {
sprintf ( line + offset , "%s/%lX (%d/%d/%d)" , topoNodeTypeStr [ node -> type ], node -> id , node -> cpu . arch , node -> cpu . vendor , node -> cpu . model );
2021-04-12 16:00:11 -07:00
} else if ( node -> type == PCI ) {
sprintf ( line + offset , "%s/%lX (%lx)" , topoNodeTypeStr [ node -> type ], node -> id , node -> pci . device );
2019-11-19 14:57:39 -08:00
} else {
sprintf ( line + offset , "%s/%lX" , topoNodeTypeStr [ node -> type ], node -> id );
}
INFO ( NCCL_GRAPH , "%s" , line );
for ( int i = 0 ; i < offset ; i ++ ) line [ i ] = ' ' ;
for ( int l = 0 ; l < node -> nlinks ; l ++ ) {
struct ncclTopoLink * link = node -> links + l ;
if ( link -> type == LINK_LOC ) continue ;
2020-01-16 16:02:42 -08:00
if ( link -> type != LINK_PCI || link -> remNode != prevNode ) {
2022-08-18 02:53:17 -07:00
sprintf ( line + offset , "+ %s[%2.1f] - " , topoLinkTypeStr [ link -> type ], link -> bw );
2019-11-19 14:57:39 -08:00
int nextOffset = strlen ( line );
if ( link -> type == LINK_PCI ) {
NCCLCHECK ( ncclTopoPrintRec ( link -> remNode , node , line , nextOffset ));
} else {
if ( link -> remNode -> type == NET ) {
2022-08-18 02:53:17 -07:00
sprintf ( line + nextOffset , "%s/%lX (%lx/%d/%f)" , topoNodeTypeStr [ link -> remNode -> type ], link -> remNode -> id , link -> remNode -> net . asic , link -> remNode -> net . port , link -> remNode -> net . bw );
2019-11-19 14:57:39 -08:00
} else {
sprintf ( line + nextOffset , "%s/%lX" , topoNodeTypeStr [ link -> remNode -> type ], link -> remNode -> id );
}
INFO ( NCCL_GRAPH , "%s" , line );
}
}
}
return ncclSuccess ;
}
ncclResult_t ncclTopoPrint ( struct ncclTopoSystem * s ) {
2022-08-18 02:53:17 -07:00
INFO ( NCCL_GRAPH , "=== System : maxBw %2.1f totalBw %2.1f ===" , s -> maxBw , s -> totalBw );
2019-11-19 14:57:39 -08:00
char line [ 1024 ];
for ( int n = 0 ; n < s -> nodes [ CPU ]. count ; n ++ ) NCCLCHECK ( ncclTopoPrintRec ( s -> nodes [ CPU ]. nodes + n , NULL , line , 0 ));
INFO ( NCCL_GRAPH , "==========================================" );
NCCLCHECK ( ncclTopoPrintPaths ( s ));
return ncclSuccess ;
}
static ncclResult_t ncclTopoSort ( struct ncclTopoNode * node , struct ncclTopoNode * upNode ) {
// Shift all links to have upLink as last link
if ( upNode ) {
int l = 0 ;
while ( node -> links [ l ]. remNode != upNode ) l ++ ;
struct ncclTopoLink upLink ;
memcpy ( & upLink , node -> links + l , sizeof ( struct ncclTopoLink ));
while ( node -> links [ l + 1 ]. remNode ) {
memcpy ( node -> links + l , node -> links + l + 1 , sizeof ( struct ncclTopoLink ));
l ++ ;
}
memcpy ( node -> links + l , & upLink , sizeof ( struct ncclTopoLink ));
}
// Recursively sort the PCI tree
for ( int l = 0 ; l < node -> nlinks ; l ++ ) {
struct ncclTopoLink * link = node -> links + l ;
if ( link -> type == LINK_PCI && link -> remNode != upNode ) NCCLCHECK ( ncclTopoSort ( link -> remNode , node ));
}
return ncclSuccess ;
}
// We want the graph to be organized to ease/accelerate traversal :
// 1. NVLinks (already the case)
// 2. PCI down
// 3. PCI up
2020-01-16 16:02:42 -08:00
// 4. SYS (already the case)
2019-11-19 14:57:39 -08:00
ncclResult_t ncclTopoSortSystem ( struct ncclTopoSystem * system ) {
for ( int n = 0 ; n < system -> nodes [ CPU ]. count ; n ++ ) NCCLCHECK ( ncclTopoSort ( system -> nodes [ CPU ]. nodes + n , NULL ));
return ncclSuccess ;
}
2020-11-03 14:29:08 -08:00
ncclResult_t ncclTopoAddNet ( struct ncclXmlNode * xmlNet , struct ncclTopoSystem * system , struct ncclTopoNode * nic , int64_t busId ) {
2020-01-16 16:02:42 -08:00
int dev ;
NCCLCHECK ( xmlGetAttrInt ( xmlNet , "dev" , & dev ));
struct ncclTopoNode * net ;
NCCLCHECK ( ncclTopoCreateNode ( system , & net , NET , dev ));
const char * str ;
NCCLCHECK ( xmlGetAttr ( xmlNet , "guid" , & str ));
if ( str ) sscanf ( str , "0x%lx" , & net -> net . asic );
else net -> net . asic = dev ;
ncclDebugNoWarn = NCCL_GRAPH ;
int mbps ;
2022-01-07 06:39:55 -08:00
NCCLCHECK ( xmlGetAttrIntDefault ( xmlNet , "speed" , & mbps , 0 ));
2020-01-16 16:02:42 -08:00
if ( mbps <= 0 ) mbps = 10000 ; // Some NICs define speed = -1
2022-08-18 02:53:17 -07:00
net -> net . bw = mbps / 8000.0 ;
2022-01-07 06:39:55 -08:00
if ( xmlGetAttrFloat ( xmlNet , "latency" , & net -> net . latency ) != ncclSuccess ) net -> net . latency = 0 ;
NCCLCHECK ( xmlGetAttrIntDefault ( xmlNet , "port" , & net -> net . port , 0 ));
NCCLCHECK ( xmlGetAttrIntDefault ( xmlNet , "gdr" , & net -> net . gdrSupport , 0 ));
NCCLCHECK ( xmlGetAttrIntDefault ( xmlNet , "maxconn" , & net -> net . maxChannels , MAXCHANNELS ));
NCCLCHECK ( xmlGetAttrIntDefault ( xmlNet , "coll" , & net -> net . collSupport , 0 ));
2020-11-03 14:29:08 -08:00
net -> net . busId = busId ;
2020-01-16 16:02:42 -08:00
ncclDebugNoWarn = 0 ;
2022-08-18 02:53:17 -07:00
NCCLCHECK ( ncclTopoConnectNodes ( nic , net , LINK_NET , net -> net . bw ));
NCCLCHECK ( ncclTopoConnectNodes ( net , nic , LINK_NET , net -> net . bw ));
2020-01-16 16:02:42 -08:00
return ncclSuccess ;
}
2020-11-03 14:29:08 -08:00
ncclResult_t ncclTopoAddNic ( struct ncclXmlNode * xmlNic , struct ncclTopoSystem * system , struct ncclTopoNode * nic , int64_t busId ) {
2020-01-16 16:02:42 -08:00
for ( int s = 0 ; s < xmlNic -> nSubs ; s ++ ) {
struct ncclXmlNode * xmlNet = xmlNic -> subs [ s ];
if ( strcmp ( xmlNet -> name , "net" ) != 0 ) continue ;
int index ;
NCCLCHECK ( xmlGetAttrIndex ( xmlNet , "dev" , & index ));
if ( index == - 1 ) continue ;
2020-11-03 14:29:08 -08:00
NCCLCHECK ( ncclTopoAddNet ( xmlNet , system , nic , busId ));
2020-01-16 16:02:42 -08:00
}
return ncclSuccess ;
}
ncclResult_t ncclTopoAddGpu ( struct ncclXmlNode * xmlGpu , struct ncclTopoSystem * system , struct ncclTopoNode * gpu ) {
NCCLCHECK ( xmlGetAttrInt ( xmlGpu , "sm" , & gpu -> gpu . cudaCompCap ));
2023-10-26 12:09:15 -05:00
const char * gcnArch ;
const char * gcnArchName ;
NCCLCHECK ( xmlGetAttr ( xmlGpu , "gcn" , & gcnArch ));
2023-10-28 12:30:36 -05:00
convertGcnArchToGcnArchName ( gcnArch , & gcnArchName );
2023-10-26 12:09:15 -05:00
gpu -> gpu . gcn = strdup ( gcnArchName );
2020-08-12 15:48:38 -07:00
rcclHipDeviceArch_t arch ;
NCCLCHECK ( xmlGetAttrInt ( xmlGpu , "arch" , & arch . value ));
memcpy ( & gpu -> gpu . arch , & arch . arch , sizeof ( hipDeviceArch_t ));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( xmlGetAttrInt ( xmlGpu , "rank" , & gpu -> gpu . rank ));
NCCLCHECK ( xmlGetAttrInt ( xmlGpu , "dev" , & gpu -> gpu . dev ));
NCCLCHECK ( xmlGetAttrInt ( xmlGpu , "gdr" , & gpu -> gpu . gdrSupport ));
// Do not go any further, nvlinks will be added in a second pass
return ncclSuccess ;
}
2023-07-21 07:31:27 -07:00
struct kvDict kvDictPciClass [] = { { "0x060400" , PCI }, { "0x068000" , NVS }, { "0x068001" , CPU }, { "0x03" , GPU }, { "0x02" , NIC }, { "0x120000" , GPU }, { NULL , PCI /* Default fallback value */ } };
2021-09-08 13:56:25 -07:00
struct kvDict kvDictPciGen [] = {
2022-05-03 01:30:26 -07:00
{ "2.5 GT/s" , 15 }, { "5 GT/s" , 30 }, { "8 GT/s" , 60 }, { "16 GT/s" , 120 }, { "32 GT/s" , 240 }, /* Kernel 5.6 and earlier */
2021-09-08 13:56:25 -07:00
{ "2.5 GT/s PCIe" , 15 }, { "5.0 GT/s PCIe" , 30 }, { "8.0 GT/s PCIe" , 60 }, { "16.0 GT/s PCIe" , 120 }, { "32.0 GT/s PCIe" , 240 }, { "64.0 GT/s PCIe" , 480 },
{ NULL , 60 /* Default fallback */ } }; // x100 Mbps per lane
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoAddPci ( struct ncclXmlNode * xmlPci , struct ncclTopoSystem * system , struct ncclTopoNode * parent ) {
const char * str ;
int type ;
NCCLCHECK ( xmlGetAttrStr ( xmlPci , "class" , & str ));
NCCLCHECK ( kvConvertToInt ( str , & type , kvDictPciClass ));
int64_t busId ;
NCCLCHECK ( xmlGetAttrStr ( xmlPci , "busid" , & str ));
NCCLCHECK ( busIdToInt64 ( str , & busId ));
struct ncclTopoNode * node = NULL ;
2020-08-03 12:38:07 -07:00
struct ncclXmlNode * xmlGpu = NULL ;
NCCLCHECK ( xmlGetSub ( xmlPci , "gpu" , & xmlGpu ));
if ( xmlGpu != NULL ) {
2020-08-05 13:34:32 -07:00
type = GPU ;
2020-01-16 16:02:42 -08:00
int index ;
NCCLCHECK ( xmlGetAttrIndex ( xmlGpu , "rank" , & index ));
if ( index == - 1 ) return ncclSuccess ;
NCCLCHECK ( ncclTopoCreateNode ( system , & node , type , busId ));
NCCLCHECK ( ncclTopoAddGpu ( xmlGpu , system , node ));
}
2020-08-03 12:38:07 -07:00
struct ncclXmlNode * xmlNic = NULL ;
NCCLCHECK ( xmlGetSub ( xmlPci , "nic" , & xmlNic ));
if ( xmlNic != NULL ) {
type = NIC ;
2020-01-16 16:02:42 -08:00
// Ignore sub device ID and merge multi-port NICs into one PCI device.
busId &= 0xfffffffffffffff0 ;
struct ncclTopoNode * nicNode = NULL ;
NCCLCHECK ( ncclTopoGetNode ( system , & nicNode , type , busId ));
if ( nicNode == NULL ) {
NCCLCHECK ( ncclTopoCreateNode ( system , & nicNode , type , busId ));
node = nicNode ; // Connect it to parent later on
}
2020-11-03 14:29:08 -08:00
NCCLCHECK ( ncclTopoAddNic ( xmlNic , system , nicNode , busId ));
2020-01-16 16:02:42 -08:00
} else if ( type == PCI ) {
NCCLCHECK ( ncclTopoCreateNode ( system , & node , type , busId ));
2021-04-12 16:00:11 -07:00
NCCLCHECK ( xmlGetAttr ( xmlPci , "vendor" , & str ));
if ( str ) node -> pci . device += strtol ( str , NULL , 0 ) << 48 ;
NCCLCHECK ( xmlGetAttr ( xmlPci , "device" , & str ));
if ( str ) node -> pci . device += strtol ( str , NULL , 0 ) << 32 ;
NCCLCHECK ( xmlGetAttr ( xmlPci , "subsystem_vendor" , & str ));
if ( str ) node -> pci . device += strtol ( str , NULL , 0 ) << 16 ;
NCCLCHECK ( xmlGetAttr ( xmlPci , "subsystem_device" , & str ));
if ( str ) node -> pci . device += strtol ( str , NULL , 0 );
2020-01-16 16:02:42 -08:00
for ( int s = 0 ; s < xmlPci -> nSubs ; s ++ ) {
struct ncclXmlNode * xmlSubPci = xmlPci -> subs [ s ];
NCCLCHECK ( ncclTopoAddPci ( xmlSubPci , system , node ));
2019-11-19 14:57:39 -08:00
}
}
2020-01-16 16:02:42 -08:00
if ( node ) {
int width , speed ;
NCCLCHECK ( xmlGetAttrInt ( xmlPci , "link_width" , & width ));
NCCLCHECK ( xmlGetAttrStr ( xmlPci , "link_speed" , & str ));
// Manage cases where speed was not indicated in /sys
if ( width == 0 ) width = 16 ;
NCCLCHECK ( kvConvertToInt ( str , & speed , kvDictPciGen )); // Values in 100Mbps, per lane (we want GB/s in the end)
NCCLCHECK ( ncclTopoConnectNodes ( node , parent , LINK_PCI , width * speed / 80.0 ));
NCCLCHECK ( ncclTopoConnectNodes ( parent , node , LINK_PCI , width * speed / 80.0 ));
}
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
struct kvDict kvDictCpuArch [] = { { "x86_64" , NCCL_TOPO_CPU_ARCH_X86 }, { "arm64" , NCCL_TOPO_CPU_ARCH_ARM }, { "ppc64" , NCCL_TOPO_CPU_ARCH_POWER }, { NULL , 0 } };
2020-11-27 16:20:55 +08:00
struct kvDict kvDictCpuVendor [] = { { "GenuineIntel" , NCCL_TOPO_CPU_VENDOR_INTEL }, { "AuthenticAMD" , NCCL_TOPO_CPU_VENDOR_AMD }, { "CentaurHauls" , NCCL_TOPO_CPU_VENDOR_ZHAOXIN }, { " Shanghai " , NCCL_TOPO_CPU_VENDOR_ZHAOXIN }, { NULL , 0 } };
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoAddCpu ( struct ncclXmlNode * xmlCpu , struct ncclTopoSystem * system ) {
int numaId ;
NCCLCHECK ( xmlGetAttrInt ( xmlCpu , "numaid" , & numaId ));
struct ncclTopoNode * cpu ;
NCCLCHECK ( ncclTopoCreateNode ( system , & cpu , CPU , numaId ));
const char * str ;
NCCLCHECK ( xmlGetAttr ( xmlCpu , "affinity" , & str ));
if ( str != NULL ) {
NCCLCHECK ( ncclStrToCpuset ( str , & cpu -> cpu . affinity ));
}
NCCLCHECK ( xmlGetAttrStr ( xmlCpu , "arch" , & str ));
NCCLCHECK ( kvConvertToInt ( str , & cpu -> cpu . arch , kvDictCpuArch ));
if ( cpu -> cpu . arch == NCCL_TOPO_CPU_ARCH_X86 ) {
NCCLCHECK ( xmlGetAttrStr ( xmlCpu , "vendor" , & str ));
NCCLCHECK ( kvConvertToInt ( str , & cpu -> cpu . vendor , kvDictCpuVendor ));
if ( cpu -> cpu . vendor == NCCL_TOPO_CPU_VENDOR_INTEL ) {
int familyId , modelId ;
NCCLCHECK ( xmlGetAttrInt ( xmlCpu , "familyid" , & familyId ));
NCCLCHECK ( xmlGetAttrInt ( xmlCpu , "modelid" , & modelId ));
cpu -> cpu . model = ( familyId == 6 && modelId >= 0x55 ) ? NCCL_TOPO_CPU_TYPE_SKL : NCCL_TOPO_CPU_INTEL_BDW ;
2020-11-27 16:20:55 +08:00
} else if ( cpu -> cpu . vendor == NCCL_TOPO_CPU_VENDOR_ZHAOXIN ) {
int familyId , modelId ;
NCCLCHECK ( xmlGetAttrInt ( xmlCpu , "familyid" , & familyId ));
NCCLCHECK ( xmlGetAttrInt ( xmlCpu , "modelid" , & modelId ));
if ( familyId == 7 && modelId == 0x5B ) cpu -> cpu . model = NCCL_TOPO_CPU_TYPE_YONGFENG ;
2020-01-16 16:02:42 -08:00
}
2020-07-21 14:10:36 -07:00
if ( cpu -> cpu . vendor == NCCL_TOPO_CPU_VENDOR_AMD ) {
int familyId , modelId ;
NCCLCHECK ( xmlGetAttrInt ( xmlCpu , "familyid" , & familyId ));
NCCLCHECK ( xmlGetAttrInt ( xmlCpu , "modelid" , & modelId ));
2021-04-14 08:29:00 -07:00
// Treat "Milan" also as "Rome"
cpu -> cpu . model = (( familyId == 143 && modelId >= 49 ) || familyId == 175 ) ? NCCL_TOPO_CPU_TYPE_ROME : NCCL_TOPO_CPU_TYPE_ZEN ;
2020-07-21 14:10:36 -07:00
}
2020-01-16 16:02:42 -08:00
}
for ( int s = 0 ; s < xmlCpu -> nSubs ; s ++ ) {
struct ncclXmlNode * node = xmlCpu -> subs [ s ];
if ( strcmp ( node -> name , "pci" ) == 0 ) NCCLCHECK ( ncclTopoAddPci ( node , system , cpu ));
if ( strcmp ( node -> name , "nic" ) == 0 ) {
struct ncclTopoNode * nic = NULL ;
NCCLCHECK ( ncclTopoGetNode ( system , & nic , NIC , 0 ));
if ( nic == NULL ) {
NCCLCHECK ( ncclTopoCreateNode ( system , & nic , NIC , 0 ));
2022-08-18 02:53:17 -07:00
NCCLCHECK ( ncclTopoConnectNodes ( cpu , nic , LINK_PCI , LOC_BW ));
NCCLCHECK ( ncclTopoConnectNodes ( nic , cpu , LINK_PCI , LOC_BW ));
2020-01-16 16:02:42 -08:00
}
2020-11-03 14:29:08 -08:00
NCCLCHECK ( ncclTopoAddNic ( node , system , nic , 0 ));
2020-01-16 16:02:42 -08:00
}
}
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2020-04-01 13:21:38 -07:00
#if defined(__HIP_PLATFORM_HCC__) || defined(__HCC__) || defined(__HIPCC__)
ncclResult_t ncclTopoAddXGMI ( struct ncclXmlNode * node , struct ncclTopoSystem * system , const char * parentBusId ) {
if ( strcmp ( node -> name , "xgmi" ) == 0 ) {
struct ncclTopoNode * gpu = NULL ;
int64_t pBusId ;
NCCLCHECK ( busIdToInt64 ( parentBusId , & pBusId ));
NCCLCHECK ( ncclTopoGetNode ( system , & gpu , GPU , pBusId ));
if ( gpu == NULL ) {
WARN ( "Add XGMI error : could not find GPU %lx \n " , pBusId );
return ncclInternalError ;
}
int count ;
NCCLCHECK ( xmlGetAttrInt ( node , "count" , & count ));
const char * targetClass ;
NCCLCHECK ( xmlGetAttrStr ( node , "tclass" , & targetClass ));
int targetType ;
NCCLCHECK ( kvConvertToInt ( targetClass , & targetType , kvDictPciClass ));
struct ncclTopoNode * remote = NULL ;
if ( targetType == GPU ) {
// NVL P2P connection to another GPU
const char * target ;
NCCLCHECK ( xmlGetAttrStr ( node , "target" , & target ));
int64_t busId ;
NCCLCHECK ( busIdToInt64 ( target , & busId ));
NCCLCHECK ( ncclTopoGetNode ( system , & remote , GPU , busId ));
} else if ( targetType == CPU ) {
// NVL connection to the local CPU
NCCLCHECK ( findLocalCpu ( gpu , & remote ));
} else {
if ( system -> nodes [ NVS ]. count == 0 ) {
NCCLCHECK ( ncclTopoCreateNode ( system , & remote , NVS , 0 ));
} else {
remote = system -> nodes [ NVS ]. nodes ;
}
}
if ( remote ) {
2022-05-17 08:23:59 -07:00
float nvlSpeed = ncclTopoXGMISpeed ( gpu -> gpu . gcn );
2020-04-01 13:21:38 -07:00
NCCLCHECK ( ncclTopoConnectNodes ( gpu , remote , LINK_NVL , count * nvlSpeed ));
if ( remote -> type != GPU ) {
NCCLCHECK ( ncclTopoConnectNodes ( remote , gpu , LINK_NVL , count * nvlSpeed ));
}
}
} else {
const char * busId ;
NCCLCHECK ( xmlGetAttr ( node , "busid" , & busId ));
for ( int s = 0 ; s < node -> nSubs ; s ++ ) {
NCCLCHECK ( ncclTopoAddXGMI ( node -> subs [ s ], system , busId ? busId : parentBusId ));
}
}
return ncclSuccess ;
}
#else
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoAddNvLinks ( struct ncclXmlNode * node , struct ncclTopoSystem * system , const char * parentBusId ) {
if ( strcmp ( node -> name , "nvlink" ) == 0 ) {
struct ncclTopoNode * gpu = NULL ;
int64_t pBusId ;
NCCLCHECK ( busIdToInt64 ( parentBusId , & pBusId ));
NCCLCHECK ( ncclTopoGetNode ( system , & gpu , GPU , pBusId ));
if ( gpu == NULL ) {
2021-02-09 15:34:08 -08:00
WARN ( "Add NVLink error : could not find GPU %lx" , pBusId );
2020-01-16 16:02:42 -08:00
return ncclInternalError ;
}
int count ;
NCCLCHECK ( xmlGetAttrInt ( node , "count" , & count ));
const char * targetClass ;
NCCLCHECK ( xmlGetAttrStr ( node , "tclass" , & targetClass ));
int targetType ;
NCCLCHECK ( kvConvertToInt ( targetClass , & targetType , kvDictPciClass ));
struct ncclTopoNode * remote = NULL ;
if ( targetType == GPU ) {
// NVL P2P connection to another GPU
const char * target ;
NCCLCHECK ( xmlGetAttrStr ( node , "target" , & target ));
int64_t busId ;
NCCLCHECK ( busIdToInt64 ( target , & busId ));
NCCLCHECK ( ncclTopoGetNode ( system , & remote , GPU , busId ));
} else if ( targetType == CPU ) {
// NVL connection to the local CPU
NCCLCHECK ( findLocalCpu ( gpu , & remote ));
} else {
if ( system -> nodes [ NVS ]. count == 0 ) {
NCCLCHECK ( ncclTopoCreateNode ( system , & remote , NVS , 0 ));
} else {
remote = system -> nodes [ NVS ]. nodes ;
}
}
if ( remote ) {
2022-08-18 02:53:17 -07:00
float nvlBw = ncclTopoNVLinkBw ( gpu -> gpu . cudaCompCap );
NCCLCHECK ( ncclTopoConnectNodes ( gpu , remote , LINK_NVL , count * nvlBw ));
2020-01-16 16:02:42 -08:00
if ( remote -> type != GPU ) {
2022-08-18 02:53:17 -07:00
NCCLCHECK ( ncclTopoConnectNodes ( remote , gpu , LINK_NVL , count * nvlBw ));
2020-01-16 16:02:42 -08:00
}
}
} else {
const char * busId ;
NCCLCHECK ( xmlGetAttr ( node , "busid" , & busId ));
for ( int s = 0 ; s < node -> nSubs ; s ++ ) {
NCCLCHECK ( ncclTopoAddNvLinks ( node -> subs [ s ], system , busId ? busId : parentBusId ));
2019-11-19 14:57:39 -08:00
}
}
return ncclSuccess ;
}
2020-04-01 13:21:38 -07:00
#endif
2019-11-19 14:57:39 -08:00
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoGetSystemFromXml ( struct ncclXml * xml , struct ncclTopoSystem ** topoSystem ) {
NCCLCHECK ( ncclCalloc ( topoSystem , 1 ));
struct ncclXmlNode * topNode ;
NCCLCHECK ( xmlFindTag ( xml , "system" , & topNode ));
for ( int s = 0 ; s < topNode -> nSubs ; s ++ ) {
struct ncclXmlNode * node = topNode -> subs [ s ];
if ( strcmp ( node -> name , "cpu" ) == 0 ) NCCLCHECK ( ncclTopoAddCpu ( node , * topoSystem ));
2019-11-19 14:57:39 -08:00
}
2020-04-01 13:21:38 -07:00
#if defined(__HIP_PLATFORM_HCC__) || defined(__HCC__) || defined(__HIPCC__)
NCCLCHECK ( ncclTopoAddXGMI ( topNode , * topoSystem , NULL ));
#else
2020-01-16 16:02:42 -08:00
NCCLCHECK ( ncclTopoAddNvLinks ( topNode , * topoSystem , NULL ));
2020-04-01 13:21:38 -07:00
#endif
2020-01-16 16:02:42 -08:00
2021-04-12 16:00:11 -07:00
NCCLCHECK ( ncclTopoFlattenBcmSwitches ( * topoSystem ));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( ncclTopoConnectCpus ( * topoSystem ));
NCCLCHECK ( ncclTopoSortSystem ( * topoSystem ));
return ncclSuccess ;
2019-11-19 14:57:39 -08:00
}
2020-01-16 16:02:42 -08:00
NCCL_PARAM ( TopoDumpFileRank , "TOPO_DUMP_FILE_RANK" , 0 );
// Only set values if not already set
static ncclResult_t xmlInitAttrInt ( struct ncclXmlNode * node , const char * attrName , const int value ) {
int index ;
NCCLCHECK ( xmlGetAttrIndex ( node , attrName , & index ));
if ( index == - 1 ) {
index = node -> nAttrs ++ ;
strncpy ( node -> attrs [ index ]. key , attrName , MAX_STR_LEN );
snprintf ( node -> attrs [ index ]. value , MAX_STR_LEN , "%d" , value );
2019-11-19 14:57:39 -08:00
}
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
static ncclResult_t xmlInitAttrUint64 ( struct ncclXmlNode * node , const char * attrName , const uint64_t value ) {
int index ;
NCCLCHECK ( xmlGetAttrIndex ( node , attrName , & index ));
if ( index == - 1 ) {
index = node -> nAttrs ++ ;
strncpy ( node -> attrs [ index ]. key , attrName , MAX_STR_LEN );
snprintf ( node -> attrs [ index ]. value , MAX_STR_LEN , "0x%lx" , value );
}
return ncclSuccess ;
2019-11-19 14:57:39 -08:00
}
2022-01-07 06:39:55 -08:00
static ncclResult_t xmlInitAttrFloat ( struct ncclXmlNode * node , const char * attrName , const float value ) {
int index ;
NCCLCHECK ( xmlGetAttrIndex ( node , attrName , & index ));
if ( index == - 1 ) {
index = node -> nAttrs ++ ;
strncpy ( node -> attrs [ index ]. key , attrName , MAX_STR_LEN );
snprintf ( node -> attrs [ index ]. value , MAX_STR_LEN , "%f" , value );
}
return ncclSuccess ;
}
2019-11-19 14:57:39 -08:00
2020-01-16 16:02:42 -08:00
ncclResult_t ncclTopoGetSystem ( struct ncclComm * comm , struct ncclTopoSystem ** system ) {
struct ncclXml * xml ;
NCCLCHECK ( ncclCalloc ( & xml , 1 ));
char * xmlTopoFile = getenv ( "NCCL_TOPO_FILE" );
if ( xmlTopoFile ) {
2020-05-12 14:40:18 -07:00
INFO ( NCCL_ENV , "NCCL_TOPO_FILE set by environment to %s" , xmlTopoFile );
2021-07-08 14:12:04 -07:00
NCCLCHECK ( ncclTopoGetXmlFromFile ( xmlTopoFile , xml , 1 ));
} else {
// Try default XML topology location
NCCLCHECK ( ncclTopoGetXmlFromFile ( "/var/run/nvidia-topologyd/virtualTopology.xml" , xml , 0 ));
2020-01-16 16:02:42 -08:00
}
if ( xml -> maxIndex == 0 ) {
// Create top tag
struct ncclXmlNode * top ;
NCCLCHECK ( xmlAddNode ( xml , NULL , "system" , & top ));
NCCLCHECK ( xmlSetAttrInt ( top , "version" , NCCL_TOPO_XML_VERSION ));
}
// Auto-detect GPUs if needed
for ( int r = 0 ; r < comm -> nRanks ; r ++ ) {
if ( comm -> peerInfo [ r ]. hostHash == comm -> peerInfo [ comm -> rank ]. hostHash ) {
char busId [ NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE ];
NCCLCHECK ( int64ToBusId ( comm -> peerInfo [ r ]. busId , busId ));
struct ncclXmlNode * node ;
NCCLCHECK ( ncclTopoFillGpu ( xml , busId , & node ));
2020-04-17 10:03:14 -07:00
if ( node == NULL ) continue ;
2020-09-04 14:35:05 -07:00
NCCLCHECK ( xmlSetAttrInt ( node , "keep" , 1 ));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( xmlSetAttrInt ( node , "rank" , r ));
NCCLCHECK ( xmlInitAttrInt ( node , "gdr" , comm -> peerInfo [ r ]. gdrSupport ));
}
}
// Auto-detect NICs if needed. net/collnet share the same xml/graph nodes,
// so we start with collnet so that it has precedence.
int netDevCount = 0 ;
2022-05-24 02:02:31 -07:00
if ( collNetSupport ( comm )) {
NCCLCHECK ( collNetDevices ( comm , & netDevCount ));
2020-01-16 16:02:42 -08:00
for ( int n = 0 ; n < netDevCount ; n ++ ) {
ncclNetProperties_t props ;
2022-05-24 02:02:31 -07:00
NCCLCHECK ( collNetGetProperties ( comm , n , & props ));
2020-01-16 16:02:42 -08:00
struct ncclXmlNode * netNode ;
NCCLCHECK ( ncclTopoFillNet ( xml , props . pciPath , props . name , & netNode ));
2020-09-04 14:35:05 -07:00
NCCLCHECK ( xmlSetAttrInt ( netNode , "keep" , 1 ));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( xmlSetAttrInt ( netNode , "dev" , n ));
NCCLCHECK ( xmlInitAttrInt ( netNode , "speed" , props . speed ));
NCCLCHECK ( xmlInitAttrInt ( netNode , "port" , props . port ));
NCCLCHECK ( xmlInitAttrUint64 ( netNode , "guid" , props . guid ));
NCCLCHECK ( xmlInitAttrInt ( netNode , "maxconn" , props . maxComms ));
2022-05-24 02:02:31 -07:00
bool gdrSupport = ( props . ptrSupport & NCCL_PTR_CUDA ) || ( comm -> dmaBufSupport && ( props . ptrSupport & NCCL_PTR_DMABUF ));
INFO ( NCCL_NET , "NET/%s : GPU Direct RDMA %s for HCA %d '%s'" , comm -> ncclNet -> name , gdrSupport ? "Enabled" : "Disabled" , n , props . name );
NCCLCHECK ( xmlInitAttrInt ( netNode , "gdr" , gdrSupport ));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( xmlInitAttrInt ( netNode , "coll" , 1 ));
}
}
if ( netDevCount == 0 ) {
2023-04-03 05:32:07 -07:00
NCCLCHECK ( comm -> ncclNet -> devices ( & netDevCount ));
2020-01-16 16:02:42 -08:00
}
for ( int n = 0 ; n < netDevCount ; n ++ ) {
ncclNetProperties_t props ;
2023-04-03 05:32:07 -07:00
NCCLCHECK ( comm -> ncclNet -> getProperties ( n , & props ));
2020-01-16 16:02:42 -08:00
struct ncclXmlNode * netNode ;
NCCLCHECK ( ncclTopoFillNet ( xml , props . pciPath , props . name , & netNode ));
2020-09-04 14:35:05 -07:00
NCCLCHECK ( xmlSetAttrInt ( netNode , "keep" , 1 ));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( xmlSetAttrInt ( netNode , "dev" , n ));
NCCLCHECK ( xmlInitAttrInt ( netNode , "speed" , props . speed ));
NCCLCHECK ( xmlInitAttrInt ( netNode , "port" , props . port ));
2022-01-07 06:39:55 -08:00
NCCLCHECK ( xmlInitAttrFloat ( netNode , "latency" , props . latency ));
2020-01-16 16:02:42 -08:00
NCCLCHECK ( xmlInitAttrUint64 ( netNode , "guid" , props . guid ));
NCCLCHECK ( xmlInitAttrInt ( netNode , "maxconn" , props . maxComms ));
2022-05-24 02:02:31 -07:00
bool gdrSupport = ( props . ptrSupport & NCCL_PTR_CUDA ) || ( comm -> dmaBufSupport && ( props . ptrSupport & NCCL_PTR_DMABUF ));
INFO ( NCCL_NET , "NET/%s : GPU Direct RDMA %s for HCA %d '%s'" , comm -> ncclNet -> name , gdrSupport ? "Enabled" : "Disabled" , n , props . name );
NCCLCHECK ( xmlInitAttrInt ( netNode , "gdr" , gdrSupport ));
2020-01-16 16:02:42 -08:00
}
2020-09-04 14:35:05 -07:00
// Remove XML branches which don't have a node with keep="1" (typically when importing a topology)
NCCLCHECK ( ncclTopoTrimXml ( xml ));
2020-01-16 16:02:42 -08:00
xmlTopoFile = getenv ( "NCCL_TOPO_DUMP_FILE" );
if ( xmlTopoFile && comm -> rank == ncclParamTopoDumpFileRank ()) {
2020-05-12 14:40:18 -07:00
INFO ( NCCL_ENV , "NCCL_TOPO_DUMP_FILE set by environment to %s" , xmlTopoFile );
2020-01-16 16:02:42 -08:00
NCCLCHECK ( ncclTopoDumpXmlToFile ( xmlTopoFile , xml ));
}
NCCLCHECK ( ncclTopoGetSystemFromXml ( xml , system ));
free ( xml );
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2024-01-12 16:54:19 +00:00
static ncclResult_t getLocalNetMask ( struct ncclTopoSystem * system , int g , uint64_t * localNetMask , int * type ) {
2023-04-03 05:32:07 -07:00
int minType = PATH_DIS ;
2022-08-18 02:53:17 -07:00
float maxBw = 0 ;
2020-05-12 14:40:18 -07:00
int count = 0 ;
2024-01-12 16:54:19 +00:00
int * nets ;
NCCLCHECK ( ncclCalloc ( & nets , system -> nodes [ NET ]. count ));
for ( int n = 0 ; n < system -> nodes [ NET ]. count ; n ++ ) {
struct ncclTopoLinkList * path = system -> nodes [ NET ]. nodes [ n ]. paths [ GPU ] + g ;
if ( path -> bw > maxBw || ( path -> bw == maxBw && path -> type < minType )) {
maxBw = path -> bw ;
minType = path -> type ;
if ( type ) * type = minType ;
2020-05-12 14:40:18 -07:00
count = 0 ;
}
2024-01-12 16:54:19 +00:00
if ( path -> bw == maxBw && path -> type == minType ) nets [ count ++ ] = system -> nodes [ NET ]. nodes [ n ]. id ;
2020-05-12 14:40:18 -07:00
}
2024-01-12 16:54:19 +00:00
* localNetMask = 0ULL ;
for ( int n = 0 ; n < count ; n ++ ) {
if ( nets [ n ] >= 64 ) return ncclInternalError ;
* localNetMask |= 1ULL << nets [ n ];
}
free ( nets );
2020-05-12 14:40:18 -07:00
return ncclSuccess ;
}
2023-04-03 05:32:07 -07:00
ncclResult_t ncclTopoGetLocalNet ( struct ncclTopoSystem * system , int rank , int channelId , int * id ) {
2024-01-12 16:54:19 +00:00
uint64_t * localNetMasks ;
int ngpus = system -> nodes [ GPU ]. count ;
NCCLCHECK ( ncclCalloc ( & localNetMasks , ngpus ));
// Fill localNetMasks for all GPUs.
for ( int g = 0 ; g < ngpus ; g ++ ) {
NCCLCHECK ( getLocalNetMask ( system , g , localNetMasks + g , NULL ));
}
// Find GPUs which have the same mask as rank, i.e. share the same local Nets.
2023-04-03 05:32:07 -07:00
int gpu ;
NCCLCHECK ( ncclTopoRankToIndex ( system , rank , & gpu ));
2024-01-12 16:54:19 +00:00
int netLocalGpus = 0 , netLocalGpu = 0 ;
for ( int g = 0 ; g < ngpus ; g ++ ) {
if ( localNetMasks [ g ] == localNetMasks [ gpu ]) {
if ( g == gpu ) netLocalGpu = netLocalGpus ;
netLocalGpus ++ ;
}
}
uint64_t localNetMask = localNetMasks [ gpu ];
free ( localNetMasks );
if ( localNetMask == 0 ) return ncclInternalError ;
// Round robin on GPUs and channels
int gIndex = 0 , cId = 0 , n = 0 ;
while ( 1 ) {
if ( 1ULL << n & localNetMask ) {
if ( gIndex == netLocalGpu && cId == channelId ) {
* id = n ;
return ncclSuccess ;
}
gIndex ++ ;
if ( gIndex == netLocalGpus ) {
gIndex = 0 ;
cId ++ ;
}
}
n = ( n + 1 ) % 64 ;
2023-04-03 05:32:07 -07:00
}
}
ncclResult_t ncclTopoGetLocalGpu ( struct ncclTopoSystem * system , int net , int * gpuIndex ) {
2024-01-12 16:54:19 +00:00
int ngpus = system -> nodes [ GPU ]. count ;
int * gpus ;
NCCLCHECK ( ncclCalloc ( & gpus , ngpus ));
// Find localNetMask which includes net with the most local GPUs.
int netLocalGpus = 0 , minType = PATH_DIS ;
uint64_t localNetMask = 0ULL ;
for ( int g = 0 ; g < ngpus ; g ++ ) {
int type = PATH_DIS ;
uint64_t mask ;
NCCLCHECK ( getLocalNetMask ( system , g , & mask , & type ));
if (( 1ULL << net ) & mask ) {
if ( type < minType ) {
localNetMask = mask ;
netLocalGpus = 0 ;
minType = type ;
}
if ( type == minType ) {
if ( localNetMask && mask != localNetMask ) {
WARN ( "Gpus %d and %d both have a type of %d with net %d yet have different netMasks of %lx and %lx \n " , g , gpus [ netLocalGpus - 1 ], minType , net , mask , localNetMask );
free ( gpus );
return ncclInternalError ;
}
gpus [ netLocalGpus ] = g ;
netLocalGpus ++ ;
}
}
}
if ( localNetMask == 0ULL ) {
* gpuIndex = - 1 ;
free ( gpus );
return ncclSuccess ;
}
// Round robin on GPUs and channels
int gIndex = 0 , cId = 0 , n = 0 ;
while ( 1 ) {
if ( 1ULL << n & localNetMask ) {
if ( n == net ) {
* gpuIndex = gpus [ gIndex ];
free ( gpus );
2023-04-03 05:32:07 -07:00
return ncclSuccess ;
}
2024-01-12 16:54:19 +00:00
gIndex ++ ;
if ( gIndex == netLocalGpus ) {
gIndex = 0 ;
cId ++ ;
}
2023-04-03 05:32:07 -07:00
}
2024-01-12 16:54:19 +00:00
n = ( n + 1 ) % 64 ;
2023-04-03 05:32:07 -07:00
}
}
2020-01-16 16:02:42 -08:00
/****************************/
/* External query functions */
/****************************/
ncclResult_t ncclTopoCpuType ( struct ncclTopoSystem * system , int * arch , int * vendor , int * model ) {
* arch = system -> nodes [ CPU ]. nodes [ 0 ]. cpu . arch ;
* vendor = system -> nodes [ CPU ]. nodes [ 0 ]. cpu . vendor ;
* model = system -> nodes [ CPU ]. nodes [ 0 ]. cpu . model ;
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2020-01-16 16:02:42 -08:00
NCCL_PARAM ( IgnoreCpuAffinity , "IGNORE_CPU_AFFINITY" , 0 );
2021-07-08 14:12:04 -07:00
ncclResult_t ncclTopoGetCpuAffinity ( struct ncclTopoSystem * system , int rank , cpu_set_t * affinity ) {
2020-01-16 16:02:42 -08:00
struct ncclTopoNode * cpu = NULL , * gpu = NULL ;
for ( int g = 0 ; g < system -> nodes [ GPU ]. count ; g ++ ) {
if ( system -> nodes [ GPU ]. nodes [ g ]. gpu . rank == rank ) {
gpu = system -> nodes [ GPU ]. nodes + g ;
// Find closer CPU
int cpuIndex = - 1 , minHops = 0 ;
for ( int c = 0 ; c < system -> nodes [ CPU ]. count ; c ++ ) {
int nHops = system -> nodes [ GPU ]. nodes [ g ]. paths [ CPU ][ c ]. count ;
if ( cpuIndex == - 1 || nHops < minHops ) {
cpuIndex = c ;
minHops = nHops ;
}
}
cpu = system -> nodes [ CPU ]. nodes + cpuIndex ;
}
}
if ( cpu == NULL ) {
WARN ( "Set CPU affinity : unable to find GPU/CPU for rank %d" , rank );
return ncclInternalError ;
}
// Query the CPU affinity set we were provided
cpu_set_t mask ;
SYSCHECK ( sched_getaffinity ( 0 , sizeof ( cpu_set_t ), & mask ), "sched_getaffinity" );
#ifdef ENABLE_TRACE
{
char affinityStr [ sizeof ( cpu_set_t ) * 2 ];
NCCLCHECK ( ncclCpusetToStr ( & mask , affinityStr ));
TRACE ( NCCL_INIT , "Current affinity for GPU %d is %s" , gpu -> gpu . dev , affinityStr );
}
#endif
// Get the affinity of the CPU close to our GPU.
cpu_set_t cpuMask = cpu -> cpu . affinity ;
#ifdef ENABLE_TRACE
{
char affinityStr [ sizeof ( cpu_set_t ) * 2 ];
NCCLCHECK ( ncclCpusetToStr ( & cpuMask , affinityStr ));
TRACE ( NCCL_INIT , "CPU GPU affinity for GPU %d is %s" , gpu -> gpu . dev , affinityStr );
}
#endif
cpu_set_t finalMask ;
if ( ncclParamIgnoreCpuAffinity ())
// Ignore the CPU affinity set and use the GPU one instead
finalMask = cpuMask ;
else
// Use a subset of the GPU affinity set
CPU_AND ( & finalMask , & mask , & cpuMask );
2021-07-08 14:12:04 -07:00
memcpy ( affinity , & finalMask , sizeof ( cpu_set_t ));
2020-01-16 16:02:42 -08:00
// If there is a non empty set, use it to set affinity
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" , gpu -> gpu . dev , affinityStr );
}
2019-11-19 14:57:39 -08:00
return ncclSuccess ;
}
2020-09-04 14:35:05 -07:00
2023-04-03 05:32:07 -07:00
ncclResult_t ncclTopoGetGpuCount ( struct ncclTopoSystem * system , int * count ) {
* count = system -> nodes [ GPU ]. count ;
return ncclSuccess ;
}
2020-09-04 14:35:05 -07:00
ncclResult_t ncclTopoGetNetCount ( struct ncclTopoSystem * system , int * count ) {
* count = system -> nodes [ NET ]. count ;
return ncclSuccess ;
}
2022-08-18 02:53:17 -07:00
ncclResult_t ncclTopoGetNvsCount ( struct ncclTopoSystem * system , int * count ) {
* count = system -> nodes [ NVS ]. count ;
return ncclSuccess ;
}
2020-09-04 14:35:05 -07:00
ncclResult_t ncclTopoGetCompCap ( struct ncclTopoSystem * system , int * ccMin , int * ccMax ) {
if ( system -> nodes [ GPU ]. count == 0 ) return ncclInternalError ;
int min , max ;
min = max = system -> nodes [ GPU ]. nodes [ 0 ]. gpu . cudaCompCap ;
for ( int g = 1 ; g < system -> nodes [ GPU ]. count ; g ++ ) {
min = std :: min ( min , system -> nodes [ GPU ]. nodes [ g ]. gpu . cudaCompCap );
max = std :: max ( max , system -> nodes [ GPU ]. nodes [ g ]. gpu . cudaCompCap );
}
if ( ccMin ) * ccMin = min ;
if ( ccMax ) * ccMax = max ;
return ncclSuccess ;
}
2022-01-07 06:39:55 -08:00
ncclResult_t ncclTopoGetLocalRank ( struct ncclTopoSystem * system , int rank , int * localRank ) {
for ( int g = 0 ; g < system -> nodes [ GPU ]. count ; g ++ ) {
if ( system -> nodes [ GPU ]. nodes [ g ]. gpu . rank == rank ) {
* localRank = g ;
return ncclSuccess ;
}
}
2023-02-27 02:48:21 -08:00
WARN ( "Could not find local GPU with rank %d" , rank );
2022-01-07 06:39:55 -08:00
return ncclInternalError ;
}