Merge remote-tracking branch 'nccl/master' into develop
Этот коммит содержится в:
@@ -6,7 +6,9 @@
|
||||
************************************************************************/
|
||||
|
||||
#include "comm.h"
|
||||
#include "device.h"
|
||||
#include "graph.h"
|
||||
#include "transport.h"
|
||||
#include "trees.h"
|
||||
#include "rings.h"
|
||||
#include "topo.h"
|
||||
@@ -87,6 +89,7 @@ ncclResult_t ncclTopoPreset(struct ncclComm* comm, struct ncclTopoGraph** graphs
|
||||
topoRanks->nvlsHeads[topoRanks->nvlsHeadNum++] = nvlsIntra[0];
|
||||
}
|
||||
}
|
||||
memcpy(comm->nvlsHeads, topoRanks->nvlsHeads, sizeof(int) * topoRanks->nvlsHeadNum);
|
||||
|
||||
return ncclSuccess;
|
||||
}
|
||||
@@ -386,7 +389,7 @@ static ncclResult_t connectCollNet(struct ncclComm* comm, struct ncclTopoGraph*
|
||||
for (int c=0; c<comm->nChannels; c++) {
|
||||
struct ncclChannel* channel = comm->channels+c;
|
||||
char line[1024];
|
||||
sprintf(line, "CollNet channel %d rank %d ", c, rank);
|
||||
sprintf(line, "CollNetDirect channel %d rank %d ", c, rank);
|
||||
int nDown = 0;
|
||||
for (int i=0; i<nHeads; i++) {
|
||||
if (rank == heads[i]) { // is head
|
||||
@@ -532,10 +535,14 @@ int ncclMinNchannels() {
|
||||
if (minNchannels < 0) minNchannels = 0;
|
||||
return minNchannels;
|
||||
}
|
||||
|
||||
extern int64_t ncclParamWorkArgsBytes();
|
||||
|
||||
int ncclMaxNchannels() {
|
||||
int maxNchannels = MAXCHANNELS;
|
||||
if (ncclParamMaxNrings() != -2) maxNchannels = ncclParamMaxNrings();
|
||||
if (ncclParamMaxNchannels() != -2) maxNchannels = ncclParamMaxNchannels();
|
||||
maxNchannels = std::min(maxNchannels, ncclDevMaxChannelsForArgsBytes(ncclParamWorkArgsBytes()));
|
||||
if (maxNchannels > MAXCHANNELS) maxNchannels = MAXCHANNELS;
|
||||
if (maxNchannels < 1) {
|
||||
WARN("User asked for a maximum of %d channels, setting it to 1", maxNchannels);
|
||||
@@ -561,6 +568,8 @@ void exchangeValues(int* v0, int* v1) {
|
||||
*v0 = tmp;
|
||||
}
|
||||
|
||||
NCCL_PARAM(UnpackDoubleNChannels, "UNPACK_DOUBLE_NCHANNELS", 1);
|
||||
|
||||
ncclResult_t ncclTopoPostset(struct ncclComm* comm, int* firstRanks, int* treePatterns, struct ncclTopoRanks** allTopoRanks, int* rings, struct ncclTopoGraph** graphs, struct ncclComm* parent, int nc) {
|
||||
// Gather data from all ranks
|
||||
int *ringRecv, *ringSend, *ringPrev, *ringNext, *treeToParent, *treeToChild0, *treeToChild1, *nvlsHeads;
|
||||
@@ -661,19 +670,25 @@ ncclResult_t ncclTopoPostset(struct ncclComm* comm, int* firstRanks, int* treePa
|
||||
|
||||
// Setup CollNet
|
||||
if (comm->collNetSupport == 1) {
|
||||
struct ncclTopoGraph* collNetGraph = graphs[NCCL_ALGO_COLLNET_DIRECT];
|
||||
struct ncclTopoGraph* collNetChainGraph = graphs[NCCL_ALGO_COLLNET_CHAIN];
|
||||
// Add more channels to saturate intra-node bandwidth, except the 1 PPN case
|
||||
if (collNetGraph->bwIntra > collNetGraph->bwInter && comm->nRanks > comm->nNodes) {
|
||||
if (collNetChainGraph->bwIntra > collNetChainGraph->bwInter && comm->nRanks > comm->nNodes) {
|
||||
int collNetNchannels = std::min(maxChannels, nChannels+nChannels/2);
|
||||
nChannels = comm->nChannels = copyChannels(comm, nChannels, collNetNchannels, ringPrev, ringNext);
|
||||
}
|
||||
NCCLCHECK(connectCollNet(comm, collNetGraph));
|
||||
NCCLCHECK(connectCollNet(comm, graphs[NCCL_ALGO_COLLNET_DIRECT]));
|
||||
}
|
||||
|
||||
// Use 4 compute channels per search channel to reach peak BW on <8 PPN
|
||||
if (comm->minCompCap == 90 && comm->nNodes > 1 && graphs[NCCL_ALGO_RING]->bwIntra > 45.0 && 2*nChannels <= maxChannels) {
|
||||
nChannels = comm->nChannels = copyChannels(comm, nChannels, 2*nChannels, ringPrev, ringNext);
|
||||
}
|
||||
|
||||
// Double the number of channels when using unpack networking (greater than 1 node)
|
||||
// We won't automatically double past 16 channels, users can specify 32 if they want
|
||||
if (comm->netDeviceType == NCCL_NET_DEVICE_UNPACK && comm->nNodes > 1 && nChannels < 16 && ncclParamUnpackDoubleNChannels()) {
|
||||
nChannels = comm->nChannels = copyChannels(comm, nChannels, 2*nChannels, ringPrev, ringNext);
|
||||
}
|
||||
|
||||
int minNchannels = ncclMinNchannels();
|
||||
if (comm->nNodes > 1) {
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
#include "comm.h"
|
||||
#include "net.h"
|
||||
#include "channel.h"
|
||||
#include "transport.h"
|
||||
#include "device.h"
|
||||
#include "xml.h"
|
||||
|
||||
// Pre-compute GPU->NIC, GPU->GPU and NIC->GPU paths
|
||||
@@ -868,12 +870,7 @@ static ncclResult_t ncclTopoGetNchannels(struct ncclComm* comm, int g /*local gp
|
||||
|
||||
NCCL_PARAM(MinP2pNChannels, "MIN_P2P_NCHANNELS", 1);
|
||||
NCCL_PARAM(MaxP2pNChannels, "MAX_P2P_NCHANNELS", MAXCHANNELS);
|
||||
|
||||
static int nextPow2(int v) {
|
||||
int pow2 = 1;
|
||||
while (pow2 < v) pow2 <<= 1;
|
||||
return pow2;
|
||||
}
|
||||
extern int64_t ncclParamWorkArgsBytes();
|
||||
|
||||
ncclResult_t ncclTopoComputeP2pChannels(struct ncclComm* comm) {
|
||||
/* here we already honor comm->max/minCTAs for p2pnChannels. */
|
||||
@@ -904,24 +901,18 @@ ncclResult_t ncclTopoComputeP2pChannels(struct ncclComm* comm) {
|
||||
} else if (comm->topo->nodes[GPU].count == comm->topo->nRanks && (comm->topo->type & RCCL_TOPO_4P2H_ROME) && !(comm->topo->type & RCCL_TOPO_GDR_ALL) && !(comm->topo->type & RCCL_TOPO_XGMI_ALL)) {
|
||||
// Adjust P2P channels on Rome
|
||||
comm->p2pnChannelsPerPeer = 2;
|
||||
comm->p2pnChannels = 2;
|
||||
comm->p2pnChannels = std::min(pow2Up(comm->p2pnChannels), pow2Down(ncclDevMaxChannelsForArgsBytes(ncclParamWorkArgsBytes())));
|
||||
} else {
|
||||
// Round to next pow2 nChannelsPerPeer and nChannels
|
||||
comm->p2pnChannelsPerPeer = (ncclParamNChannelsPerPeer() == -2 ? nextPow2(minChannels) : ncclParamNChannelsPerPeer());
|
||||
comm->p2pnChannelsPerPeer = (ncclParamNChannelsPerPeer() == -2 ? pow2Up(minChannels) : ncclParamNChannelsPerPeer());
|
||||
// Doubling P2P channels per peer on single node
|
||||
if (comm->topo->nodes[GPU].count == comm->topo->nRanks && IsArchMatch(comm->topo->nodes[GPU].nodes[0].gpu.gcn, "gfx94")) comm->p2pnChannelsPerPeer *= 2;
|
||||
comm->p2pnChannels = std::min(nextPow2(comm->p2pnChannels), 4*CHANNEL_LIMIT);
|
||||
comm->p2pnChannels = std::min(pow2Up(comm->p2pnChannels), 4*CHANNEL_LIMIT);
|
||||
}
|
||||
|
||||
// Init channels that weren't used so far
|
||||
for (int c=comm->nChannels; c<std::max(comm->nChannels, comm->p2pnChannels); c++) NCCLCHECK(initChannel(comm, c));
|
||||
|
||||
// We want to spread channels used when there aren't many and progressively
|
||||
// fill the whole space of nChannels. To do so we mirror the bits in the
|
||||
// nChannels space.
|
||||
for (int c=0; c<comm->p2pnChannels; c++) {
|
||||
comm->p2pChannels[c] = mirrorBits(c, comm->p2pnChannels);
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "core.h"
|
||||
#include "graph.h"
|
||||
#include "topo.h"
|
||||
#include "transport.h"
|
||||
#include "xml.h"
|
||||
#include <math.h>
|
||||
#include <sys/time.h>
|
||||
@@ -55,6 +56,15 @@ ncclResult_t ncclTopoSearchInit(struct ncclTopoSystem* system) {
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoComputeCommCPU(struct ncclComm* comm) {
|
||||
// We assume there is at least one CPU and that the CPUs have the same
|
||||
// architecture and vendor.
|
||||
const struct ncclTopoNodeSet* cpus = &comm->topo->nodes[CPU];
|
||||
comm->cpuArch = cpus->nodes[0].cpu.arch;
|
||||
comm->cpuVendor = cpus->nodes[0].cpu.vendor;
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
static ncclResult_t findRevLink(struct ncclTopoNode* node1, struct ncclTopoNode* node2, int type, struct ncclTopoLink** revLink) {
|
||||
for (int l=0; l<node2->nlinks; l++) {
|
||||
struct ncclTopoLink* link = node2->links+l;
|
||||
@@ -108,7 +118,7 @@ static ncclResult_t followPath(struct ncclTopoLinkList* path, struct ncclTopoNod
|
||||
}
|
||||
|
||||
// Try to go from node type1/index1 to no type2/index2. mult indicates whether we are counting the bandwidth (1) or undoing (-1).
|
||||
static ncclResult_t ncclTopoFollowPath(struct ncclTopoSystem* system, struct ncclTopoGraph* graph, int type1, int index1, int type2, int index2, int mult, struct ncclTopoNode** node) {
|
||||
static ncclResult_t ncclTopoFollowPath(struct ncclTopoSystem* system, struct ncclTopoGraph* graph, int type1, int index1, int type2, int index2, float mult, struct ncclTopoNode** node) {
|
||||
// First handle easy cases
|
||||
*node = system->nodes[type2].nodes+index2;
|
||||
if (type1 == -1) return ncclSuccess;
|
||||
@@ -367,6 +377,42 @@ static int ncclTopoCountXGMI(struct ncclTopoSystem* system, struct ncclTopoGraph
|
||||
return count;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoSearchTryCollnetDirect(struct ncclTopoSystem* system, struct ncclTopoGraph* graph, struct ncclTopoGraph* saveGraph, int g, int ngpus, int *time) {
|
||||
int fwdg = 0;
|
||||
int bwdg = 0;
|
||||
struct ncclTopoNode* gpu = NULL;
|
||||
float mul = 1.0 / (float)(system->nodes[GPU].count - 1);
|
||||
do {
|
||||
NCCLCHECK(ncclTopoFollowPath(system, graph, GPU, g, GPU, fwdg, mul, &gpu));
|
||||
} while (gpu && ++fwdg < system->nodes[GPU].count);
|
||||
|
||||
if (gpu != NULL) {
|
||||
do {
|
||||
NCCLCHECK(ncclTopoFollowPath(system, graph, GPU, bwdg, GPU, g, mul, &gpu));
|
||||
} while (gpu && ++bwdg < system->nodes[GPU].count);
|
||||
if (gpu != NULL) {
|
||||
// Both directions worked. Now we already have head, so pop the all other intra ranks.
|
||||
int step = 1;
|
||||
for (int index = 0; index < ngpus; ++index) {
|
||||
if (index != g) {
|
||||
graph->intra[graph->nChannels * ngpus + step] = system->nodes[GPU].nodes[index].gpu.rank;
|
||||
step++;
|
||||
}
|
||||
}
|
||||
NCCLCHECK(ncclTopoSearchRecGpu(system, graph, saveGraph, NULL, ngpus, -1, -1, 0, time));
|
||||
}
|
||||
while (bwdg) {
|
||||
bwdg--;
|
||||
NCCLCHECK(ncclTopoFollowPath(system, graph, GPU, bwdg, GPU, g, -mul, &gpu));
|
||||
}
|
||||
}
|
||||
while (fwdg) {
|
||||
fwdg--;
|
||||
NCCLCHECK(ncclTopoFollowPath(system, graph, GPU, g, GPU, fwdg, -mul, &gpu));
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoSearchTryNvls(struct ncclTopoSystem* system, struct ncclTopoGraph* graph, struct ncclTopoGraph* saveGraph, int g, int ngpus, int *time) {
|
||||
struct ncclTopoNode* nvs;
|
||||
struct ncclTopoNode* gpu;
|
||||
@@ -551,6 +597,8 @@ ncclResult_t ncclTopoSearchRecGpu(struct ncclTopoSystem* system, struct ncclTopo
|
||||
}
|
||||
} else if (graph->pattern == NCCL_TOPO_PATTERN_NVLS) {
|
||||
NCCLCHECK(ncclTopoSearchTryNvls(system, graph, saveGraph, g, ngpus, time));
|
||||
} else if (graph->pattern == NCCL_TOPO_PATTERN_COLLNET_DIRECT) {
|
||||
NCCLCHECK(ncclTopoSearchTryCollnetDirect(system, graph, saveGraph, g, ngpus, time));
|
||||
} else if (step < system->nodes[GPU].count-1) {
|
||||
// Go to next GPU
|
||||
int next[NCCL_TOPO_MAX_NODES];
|
||||
@@ -589,9 +637,10 @@ ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopo
|
||||
int* nets;
|
||||
NCCLCHECK(ncclCalloc(&nets, system->nodes[NET].count));
|
||||
int netCount;
|
||||
int graphFound = 0;
|
||||
NCCLCHECK(ncclTopoSelectNets(system, graph->typeInter, -1, nets, &netCount));
|
||||
for (int i=0; i<netCount; i++) {
|
||||
if (graph->pattern == NCCL_TOPO_PATTERN_NVLS && i>0) continue;
|
||||
if ((graph->pattern == NCCL_TOPO_PATTERN_NVLS || graph->pattern == NCCL_TOPO_PATTERN_COLLNET_DIRECT) && graphFound) continue;
|
||||
int n = nets[(graph->nChannels+i)%netCount];
|
||||
struct ncclTopoNode* net = system->nodes[NET].nodes+n;
|
||||
if (graph->collNet && net->net.collSupport == 0) continue;
|
||||
@@ -608,12 +657,22 @@ ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopo
|
||||
}
|
||||
}
|
||||
|
||||
if (graph->pattern == NCCL_TOPO_PATTERN_NVLS) {
|
||||
if (graph->pattern == NCCL_TOPO_PATTERN_NVLS || graph->pattern == NCCL_TOPO_PATTERN_COLLNET_DIRECT) {
|
||||
// NVLS search only tries to find NIC:GPU combinations to compute the heads.
|
||||
if (graph->nChannels < netCount) {
|
||||
int gpu;
|
||||
int duplicate = 0;
|
||||
NCCLCHECK(ncclTopoGetLocalGpu(system, net->id, &gpu));
|
||||
// check whether there is duplicate head when one GPU connects with multiple NICs
|
||||
for (int gc = 0; gc < graph->nChannels; gc++) {
|
||||
if (graph->intra[gc * system->nodes[GPU].count] == system->nodes[GPU].nodes[gpu].gpu.rank) {
|
||||
duplicate = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (duplicate) continue;
|
||||
if (gpu != -1) NCCLCHECK(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, 0, time, NET, n, gpu));
|
||||
graphFound = 1;
|
||||
}
|
||||
} else {
|
||||
if (graph->nChannels > 0) {
|
||||
@@ -999,8 +1058,9 @@ ncclResult_t ncclTopoCompute(ncclTopoSystem* system, struct ncclTopoGraph* graph
|
||||
int ccMin;
|
||||
NCCLCHECK(ncclTopoGetCompCap(system, &ccMin, NULL));
|
||||
if (graph->pattern == NCCL_TOPO_PATTERN_NVLS && (system->nodes[NVS].count == 0 || ccMin < 90)) return ncclSuccess;
|
||||
// NVLS search must have ngpus heads at most.
|
||||
if (graph->pattern == NCCL_TOPO_PATTERN_NVLS) graph->maxChannels = system->nodes[GPU].count;
|
||||
// NVLS and COLLNET_DIRECT search must have ngpus heads at most.
|
||||
if (graph->pattern == NCCL_TOPO_PATTERN_NVLS || graph->pattern == NCCL_TOPO_PATTERN_COLLNET_DIRECT)
|
||||
graph->maxChannels = system->nodes[GPU].count;
|
||||
|
||||
if (ngpus == 1) if (graph->pattern != NCCL_TOPO_PATTERN_RING) graph->pattern = NCCL_TOPO_PATTERN_TREE;
|
||||
|
||||
@@ -1222,7 +1282,7 @@ ncclResult_t getNvlsNetDev(struct ncclComm* comm, struct ncclTopoGraph* graph, i
|
||||
exit:
|
||||
return ret;
|
||||
fail:
|
||||
WARN("Could not find NIC for rank %d in NVLS graph\n", comm->rank);
|
||||
WARN("Could not find NIC for rank %d in NVLS graph", comm->rank);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
|
||||
+107
-35
@@ -12,6 +12,7 @@
|
||||
#include "nvmlwrap.h"
|
||||
#include "net.h"
|
||||
#include "coll_net.h"
|
||||
#include "transport.h"
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include "xml.h"
|
||||
@@ -57,7 +58,12 @@ static ncclResult_t findLocalCpu(struct ncclTopoNode* node, struct ncclTopoNode*
|
||||
return ncclSuccess;
|
||||
}
|
||||
for (int l=0; l<node->nlinks; l++) {
|
||||
if (node->links[l].type == LINK_PCI) NCCLCHECK(findLocalCpu(node->links[l].remNode, cpu));
|
||||
// Go up the PCI tree to find the CPU. Follow only PCI switches.
|
||||
if (node->links[l].type == LINK_PCI
|
||||
&& (node->links[l].remNode->type == PCI
|
||||
|| node->links[l].remNode->type == CPU)) {
|
||||
NCCLCHECK(findLocalCpu(node->links[l].remNode, cpu));
|
||||
}
|
||||
if (*cpu != NULL) return ncclSuccess;
|
||||
}
|
||||
return ncclSuccess;
|
||||
@@ -115,11 +121,6 @@ ncclResult_t ncclTopoCreateNode(struct ncclTopoSystem* system, struct ncclTopoNo
|
||||
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;
|
||||
n->links[0].bw = LOC_BW;
|
||||
n->gpu.dev = NCCL_TOPO_UNDEF;
|
||||
n->gpu.rank = NCCL_TOPO_UNDEF;
|
||||
n->gpu.cudaCompCap = NCCL_TOPO_UNDEF;
|
||||
@@ -285,8 +286,10 @@ static ncclResult_t ncclTopoPrintRec(struct ncclTopoNode* node, struct ncclTopoN
|
||||
|
||||
for (int l=0; l<node->nlinks; l++) {
|
||||
struct ncclTopoLink* link = node->links+l;
|
||||
if (link->type == LINK_LOC) continue;
|
||||
if (link->type != LINK_PCI || link->remNode != prevNode) {
|
||||
if (link->type == LINK_LOC) {
|
||||
sprintf(line+offset, "+ %s[%2.1f] - %s/%lX", topoLinkTypeStr[link->type], link->bw, topoNodeTypeStr[link->remNode->type], link->remNode->id);
|
||||
INFO(NCCL_GRAPH, "%s", line);
|
||||
} else if (link->type != LINK_PCI || link->remNode != prevNode) {
|
||||
sprintf(line+offset, "+ %s[%2.1f] - ", topoLinkTypeStr[link->type], link->bw);
|
||||
int nextOffset = strlen(line);
|
||||
if (link->type == LINK_PCI) {
|
||||
@@ -466,7 +469,9 @@ ncclResult_t ncclTopoAddPci(struct ncclXmlNode* xmlPci, struct ncclTopoSystem* s
|
||||
|
||||
for (int s=0; s<xmlPci->nSubs; s++) {
|
||||
struct ncclXmlNode* xmlSubPci = xmlPci->subs[s];
|
||||
NCCLCHECK(ncclTopoAddPci(xmlSubPci, system, node, systemId));
|
||||
if (strcmp(xmlSubPci->name, "pcilink") != 0) { // PCI links will be added later
|
||||
NCCLCHECK(ncclTopoAddPci(xmlSubPci, system, node, systemId));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -662,6 +667,38 @@ ncclResult_t ncclTopoAddNvLinks(struct ncclXmlNode* node, struct ncclTopoSystem*
|
||||
}
|
||||
#endif
|
||||
|
||||
ncclResult_t ncclTopoAddPciLinks(struct ncclXmlNode* node, struct ncclTopoSystem* system, const char* parentBusId, int systemId) {
|
||||
if (strcmp(node->name, "pcilink") == 0) {
|
||||
struct ncclTopoNode* pci = NULL;
|
||||
int64_t pBusId;
|
||||
NCCLCHECK(busIdToInt64(parentBusId, &pBusId));
|
||||
pBusId = NCCL_TOPO_ID(systemId, pBusId);
|
||||
NCCLCHECK(ncclTopoGetNode(system, &pci, PCI, pBusId));
|
||||
if (pci == NULL) {
|
||||
WARN("Add PCI Link error : could not find PCI SW %lx", pBusId);
|
||||
return ncclInternalError;
|
||||
}
|
||||
struct ncclTopoNode* remote = NULL;
|
||||
const char* target;
|
||||
NCCLCHECK(xmlGetAttrStr(node, "target", &target));
|
||||
int64_t busId;
|
||||
NCCLCHECK(busIdToInt64(target, &busId));
|
||||
NCCLCHECK(ncclTopoGetNode(system, &remote, PCI, NCCL_TOPO_ID(systemId, busId)));
|
||||
if (remote) NCCLCHECK(ncclTopoConnectNodes(pci, remote, LINK_LOC, LOC_BW));
|
||||
} else {
|
||||
if (strcmp(node->name, "cpu") == 0) {
|
||||
NCCLCHECK(ncclGetSystemId(system, node, &systemId));
|
||||
}
|
||||
const char* busId;
|
||||
NCCLCHECK(xmlGetAttr(node, "busid", &busId));
|
||||
for (int s=0; s<node->nSubs; s++) {
|
||||
NCCLCHECK(ncclTopoAddPciLinks(node->subs[s], system, busId ? busId : parentBusId, systemId));
|
||||
}
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
|
||||
ncclResult_t ncclTopoAddC2c(struct ncclXmlNode* node, struct ncclTopoSystem* system, const char* parentBusId, int systemId) {
|
||||
if (strcmp(node->name, "c2c") == 0) {
|
||||
struct ncclTopoNode* gpu = NULL;
|
||||
@@ -713,6 +750,7 @@ ncclResult_t ncclTopoGetSystemFromXml(struct ncclXml* xml, struct ncclTopoSystem
|
||||
NCCLCHECK(ncclTopoAddNvLinks(topNode, *topoSystem, NULL, 0));
|
||||
#endif
|
||||
NCCLCHECK(ncclTopoAddC2c(topNode, *topoSystem, NULL, 0));
|
||||
NCCLCHECK(ncclTopoAddPciLinks(topNode, *topoSystem, NULL, 0));
|
||||
|
||||
NCCLCHECK(ncclTopoFlattenBcmSwitches(*topoSystem));
|
||||
NCCLCHECK(ncclTopoConnectCpus(*topoSystem));
|
||||
@@ -755,6 +793,18 @@ static ncclResult_t xmlInitAttrFloat(struct ncclXmlNode* node, const char* attrN
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoRefreshBcmP2pLinks(void) {
|
||||
//refresh the switch topology by reading the link below
|
||||
FILE *fp = fopen("/sys/kernel/pci_switch_link/refresh_switch_toplogy", "r");
|
||||
if (fp != NULL) {
|
||||
int tmp;
|
||||
size_t r = fread(&tmp, sizeof(tmp), 1, fp);
|
||||
if (r != 1)
|
||||
INFO(NCCL_GRAPH, "Failed to read refresh_switch_toplogy");
|
||||
fclose(fp);
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoGetSystem(struct ncclComm* comm, struct ncclTopoSystem** system) {
|
||||
struct ncclXml* xml;
|
||||
@@ -774,18 +824,17 @@ ncclResult_t ncclTopoGetSystem(struct ncclComm* comm, struct ncclTopoSystem** sy
|
||||
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));
|
||||
if (node == NULL) continue;
|
||||
NCCLCHECK(xmlSetAttrInt(node, "keep", 1));
|
||||
NCCLCHECK(xmlSetAttrInt(node, "rank", r));
|
||||
NCCLCHECK(xmlInitAttrInt(node, "gdr", comm->peerInfo[r].gdrSupport));
|
||||
}
|
||||
NCCLCHECK(ncclTopoRefreshBcmP2pLinks());
|
||||
|
||||
// Detect only the GPU managed by this process. We'll get any others through XML fusion.
|
||||
char busId[NVML_DEVICE_PCI_BUS_ID_BUFFER_SIZE];
|
||||
NCCLCHECK(int64ToBusId(comm->peerInfo[comm->rank].busId, busId));
|
||||
struct ncclXmlNode* node;
|
||||
NCCLCHECK(ncclTopoFillGpu(xml, busId, &node));
|
||||
if (node) {
|
||||
NCCLCHECK(xmlSetAttrInt(node, "keep", 1));
|
||||
NCCLCHECK(xmlSetAttrInt(node, "rank", comm->rank));
|
||||
NCCLCHECK(xmlInitAttrInt(node, "gdr", comm->peerInfo[comm->rank].gdrSupport));
|
||||
}
|
||||
// Auto-detect NICs if needed. net/collnet share the same xml/graph nodes,
|
||||
// so we start with collnet so that it has precedence.
|
||||
@@ -815,6 +864,7 @@ ncclResult_t ncclTopoGetSystem(struct ncclComm* comm, struct ncclTopoSystem** sy
|
||||
for (int n=0; n<netDevCount; n++) {
|
||||
ncclNetProperties_t props;
|
||||
NCCLCHECK(comm->ncclNet->getProperties(n, &props));
|
||||
comm->netDeviceType = props.netDeviceType;
|
||||
struct ncclXmlNode* netNode;
|
||||
NCCLCHECK(ncclTopoFillNet(xml, props.pciPath, props.name, &netNode));
|
||||
NCCLCHECK(xmlSetAttrInt(netNode, "keep", 1));
|
||||
@@ -832,24 +882,46 @@ ncclResult_t ncclTopoGetSystem(struct ncclComm* comm, struct ncclTopoSystem** sy
|
||||
// Remove XML branches which don't have a node with keep="1" (typically when importing a topology)
|
||||
NCCLCHECK(ncclTopoTrimXml(xml));
|
||||
|
||||
// XML topo fusion.
|
||||
int* localRanks;
|
||||
int localRank = -1, nLocalRanks = 0;
|
||||
if (comm->MNNVL) {
|
||||
// MNNVL clique support
|
||||
char* mem;
|
||||
NCCLCHECK(ncclCalloc(&mem, comm->clique.size * xmlMemSize(NCCL_TOPO_XML_MAX_NODES)));
|
||||
struct ncclXml* rankXml = (struct ncclXml*)(mem+xmlMemSize(NCCL_TOPO_XML_MAX_NODES)*comm->cliqueRank);
|
||||
memcpy(rankXml, xml, xmlMemSize(NCCL_TOPO_XML_MAX_NODES));
|
||||
NCCLCHECK(ncclTopoConvertXml(rankXml, (uintptr_t)xml->nodes, 1));
|
||||
NCCLCHECK(bootstrapIntraNodeAllGather(comm->bootstrap, comm->clique.ranks, comm->cliqueRank, comm->clique.size, mem, xmlMemSize(NCCL_TOPO_XML_MAX_NODES)));
|
||||
struct ncclXml* cliqueXml;
|
||||
NCCLCHECK(xmlAlloc(&cliqueXml, comm->clique.size*NCCL_TOPO_XML_MAX_NODES));
|
||||
for (int i = 0; i < comm->clique.size; i++) {
|
||||
struct ncclXml* peerXml = (struct ncclXml*)(mem+xmlMemSize(NCCL_TOPO_XML_MAX_NODES)*i);
|
||||
NCCLCHECK(ncclTopoConvertXml(peerXml, (uintptr_t)peerXml->nodes, 0));
|
||||
NCCLCHECK(ncclTopoFuseXml(cliqueXml, peerXml));
|
||||
nLocalRanks = comm->clique.size;
|
||||
localRank = comm->cliqueRank;
|
||||
localRanks = comm->clique.ranks;
|
||||
} else {
|
||||
// Intra-node fusion. Much of the comm is not initialized yet at this point so we need to do our own calculations.
|
||||
NCCLCHECK(ncclCalloc(&localRanks, comm->nRanks));
|
||||
for (int i = 0; i < comm->nRanks; i++) {
|
||||
if (comm->peerInfo[i].hostHash == comm->peerInfo[comm->rank].hostHash) {
|
||||
if (i == comm->rank)
|
||||
localRank = nLocalRanks;
|
||||
localRanks[nLocalRanks++] = i;
|
||||
}
|
||||
}
|
||||
free(xml);
|
||||
xml = cliqueXml;
|
||||
}
|
||||
char* mem;
|
||||
NCCLCHECK(ncclCalloc(&mem, nLocalRanks * xmlMemSize(NCCL_TOPO_XML_MAX_NODES)));
|
||||
struct ncclXml* rankXml = (struct ncclXml*)(mem+xmlMemSize(NCCL_TOPO_XML_MAX_NODES)*localRank);
|
||||
memcpy(rankXml, xml, xmlMemSize(NCCL_TOPO_XML_MAX_NODES));
|
||||
NCCLCHECK(ncclTopoConvertXml(rankXml, (uintptr_t)xml->nodes, 1));
|
||||
NCCLCHECK(bootstrapIntraNodeAllGather(comm->bootstrap, localRanks, localRank, nLocalRanks, mem, xmlMemSize(NCCL_TOPO_XML_MAX_NODES)));
|
||||
if (comm->MNNVL) {
|
||||
// Ensure that we have enough room when fusing topos from multiple nodes.
|
||||
free(xml);
|
||||
NCCLCHECK(xmlAlloc(&xml, nLocalRanks*NCCL_TOPO_XML_MAX_NODES));
|
||||
} else {
|
||||
// In the intra-node case there's no need to enlarge the topo xml.
|
||||
xml->maxIndex = 0;
|
||||
free(localRanks);
|
||||
}
|
||||
for (int i = 0; i < nLocalRanks; i++) {
|
||||
struct ncclXml* peerXml = (struct ncclXml*)(mem+xmlMemSize(NCCL_TOPO_XML_MAX_NODES)*i);
|
||||
NCCLCHECK(ncclTopoConvertXml(peerXml, (uintptr_t)peerXml->nodes, 0));
|
||||
NCCLCHECK(ncclTopoFuseXml(xml, peerXml));
|
||||
}
|
||||
free(mem);
|
||||
|
||||
xmlTopoFile = ncclGetEnv("NCCL_TOPO_DUMP_FILE");
|
||||
if (xmlTopoFile && comm->rank == ncclParamTopoDumpFileRank()) {
|
||||
|
||||
@@ -252,7 +252,7 @@ static ncclResult_t ncclTopoIdToNetDev(struct ncclTopoSystem* system, int64_t id
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
WARN("Could not find NET with id %lx\n", id);
|
||||
WARN("Could not find NET with id %lx", id);
|
||||
return ncclInternalError;
|
||||
}
|
||||
|
||||
|
||||
+20
-22
@@ -293,11 +293,9 @@ NCCL_PARAM(NetOverhead, "NET_OVERHEAD", -2);
|
||||
|
||||
static float getNetOverhead(struct ncclComm* comm) {
|
||||
if (ncclParamNetOverhead() != -2) return ncclParamNetOverhead() * .001;
|
||||
int cpuArch, cpuVendor, cpuModel;
|
||||
NCCLCHECK(ncclTopoCpuType(comm->topo, &cpuArch, &cpuVendor, &cpuModel));
|
||||
if (cpuArch == NCCL_TOPO_CPU_ARCH_X86 && cpuVendor == NCCL_TOPO_CPU_VENDOR_INTEL) return 1.0;
|
||||
if (cpuArch == NCCL_TOPO_CPU_ARCH_X86 && cpuVendor == NCCL_TOPO_CPU_VENDOR_AMD) return 2.0;
|
||||
else return 1.0;
|
||||
if (comm->cpuArch == NCCL_TOPO_CPU_ARCH_X86 && comm->cpuVendor == NCCL_TOPO_CPU_VENDOR_INTEL) return 1.0;
|
||||
if (comm->cpuArch == NCCL_TOPO_CPU_ARCH_X86 && comm->cpuVendor == NCCL_TOPO_CPU_VENDOR_AMD) return 2.0;
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoTuneModel(struct ncclComm* comm, int minCompCap, int maxCompCap, struct ncclTopoGraph** graphs) {
|
||||
@@ -531,6 +529,7 @@ ncclResult_t ncclTopoTuneModel(struct ncclComm* comm, int minCompCap, int maxCom
|
||||
}
|
||||
if (pEnable == 0) comm->bandwidths[c][a][p] = 0;
|
||||
if (algoEnable[a] == 0) comm->bandwidths[c][a][p] = 0;
|
||||
//if (a == NCCL_ALGO_RING && pEnable == 0) comm->ringbdw[c][p] = 0;
|
||||
}
|
||||
|
||||
for (int c = 0; c < NCCL_NUM_FUNCTIONS; c++) {
|
||||
@@ -629,15 +628,15 @@ static float treeCorrectionFactor[NCCL_NUM_PROTOCOLS][23] = {
|
||||
{ .9, .9, .9, .9, .9, .9, .9, .8, .7, .6, .6, .5, .5, .5, .5, .6, .7, .8, .7, .7, .8, .9, .9 }
|
||||
};
|
||||
|
||||
ncclResult_t ncclTopoGetAlgoTime(struct ncclInfo* info, int algorithm, int protocol, int numPipeOps, float* time, bool* backup) {
|
||||
float bw = info->comm->bandwidths[info->coll][algorithm][protocol];
|
||||
float lat = info->comm->latencies[info->coll][algorithm][protocol];
|
||||
ncclResult_t ncclTopoGetAlgoTime(struct ncclComm* comm, int coll, int algorithm, int protocol, size_t nBytes, int numPipeOps, float* time, bool* backup) {
|
||||
float bw = comm->bandwidths[coll][algorithm][protocol];
|
||||
float lat = comm->latencies[coll][algorithm][protocol];
|
||||
|
||||
if (backup) {
|
||||
*backup = false;
|
||||
if (algorithm == NCCL_ALGO_RING && bw == 0.0f) {
|
||||
/* try back up RING algorithm */
|
||||
bw = info->comm->ringbdw[info->coll][protocol];
|
||||
bw = comm->ringbdw[coll][protocol];
|
||||
*backup = true;
|
||||
}
|
||||
}
|
||||
@@ -645,27 +644,26 @@ ncclResult_t ncclTopoGetAlgoTime(struct ncclInfo* info, int algorithm, int proto
|
||||
if (bw == 0) {
|
||||
*time = -1.0; return ncclSuccess;
|
||||
}
|
||||
int logSize = log2i(info->nBytes>>6);
|
||||
int logSize = log2i(nBytes>>6);
|
||||
|
||||
#if defined(__HIP_PLATFORM_AMD__) || defined(__HIPCC__)
|
||||
if (algorithm == NCCL_ALGO_TREE) {
|
||||
if (logSize < 27) bw *= rcclTuningModel[info->comm->topo->tuning].treeCorrectionFactor[protocol][logSize];
|
||||
else bw *= rcclTuningModel[info->comm->topo->tuning].treeCorrectionFactor[protocol][26];
|
||||
if (logSize < 27) bw *= rcclTuningModel[comm->topo->tuning].treeCorrectionFactor[protocol][logSize];
|
||||
else bw *= rcclTuningModel[comm->topo->tuning].treeCorrectionFactor[protocol][26];
|
||||
}
|
||||
else if (algorithm == NCCL_ALGO_RING && info->comm->nNodes > 1) {
|
||||
if(logSize < 27) bw *= rcclTuningModel[info->comm->topo->tuning].ringCorrectionFactor[protocol][logSize];
|
||||
else bw *= rcclTuningModel[info->comm->topo->tuning].ringCorrectionFactor[protocol][26];
|
||||
else if (algorithm == NCCL_ALGO_RING && comm->nNodes > 1) {
|
||||
if(logSize < 27) bw *= rcclTuningModel[comm->topo->tuning].ringCorrectionFactor[protocol][logSize];
|
||||
else bw *= rcclTuningModel[comm->topo->tuning].ringCorrectionFactor[protocol][26];
|
||||
}
|
||||
#else
|
||||
if (algorithm == NCCL_ALGO_TREE && logSize < 23) bw *= treeCorrectionFactor[protocol][logSize];
|
||||
if (info->nChannels != 0) bw = bw / info->comm->nChannels * info->nChannels;
|
||||
if (algorithm == NCCL_ALGO_RING && protocol == NCCL_PROTO_SIMPLE && info->comm->nNodes > 1
|
||||
&& info->coll == ncclFuncAllReduce && info->nBytes/(info->comm->nChannels*info->comm->nRanks) >= 64) {
|
||||
lat *= info->comm->minCompCap < 80 ? 1.9 : 1.4; // Plateau effect of ring
|
||||
if (algorithm == NCCL_ALGO_TREE && logSize >= 0 && logSize < 23) bw *= treeCorrectionFactor[protocol][logSize];
|
||||
if (algorithm == NCCL_ALGO_RING && protocol == NCCL_PROTO_SIMPLE && comm->nNodes > 1
|
||||
&& coll == ncclFuncAllReduce && nBytes/(comm->nChannels*comm->nRanks) >= 64) {
|
||||
lat *= comm->minCompCap < 80 ? 1.9 : 1.4; // Plateau effect of ring
|
||||
}
|
||||
#endif
|
||||
// Tree pipelining saves latency in aggregation cases
|
||||
int latCount = algorithm == NCCL_ALGO_RING ? numPipeOps : DIVUP(numPipeOps, NCCL_MAX_WORK_ELEMENTS);
|
||||
*time = lat * latCount + (info->nBytes) / (1000 * bw);
|
||||
int latCount = algorithm == NCCL_ALGO_RING ? numPipeOps : DIVUP(numPipeOps, NCCL_MAX_DEV_WORK_BATCH_COLLS);
|
||||
*time = lat * latCount + nBytes / (1000 * bw);
|
||||
return ncclSuccess;
|
||||
}
|
||||
+85
-53
@@ -275,56 +275,34 @@ ncclResult_t ncclTopoDumpXmlToFile(const char* xmlTopoFile, struct ncclXml* xml)
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoFuseXml(struct ncclXml* dst, struct ncclXml* src) {
|
||||
struct ncclXmlNode* topNode;
|
||||
NCCLCHECK(xmlFindTag(dst, "system", &topNode));
|
||||
static ncclResult_t xmlTopoFuseXmlRecursive(struct ncclXml* dst, struct ncclXmlNode* dstParent, struct ncclXmlNode* srcParent) {
|
||||
for (int i = 0; i < srcParent->nSubs; i++) {
|
||||
struct ncclXmlNode* srcNode = srcParent->subs[i];
|
||||
struct ncclXmlNode* dstNode;
|
||||
NCCLCHECK(xmlFindNode(dstParent, srcNode, &dstNode));
|
||||
if (dstNode == NULL) {
|
||||
NCCLCHECK(xmlAddTree(dst, dstParent, srcNode));
|
||||
} else {
|
||||
NCCLCHECK(xmlTopoFuseXmlRecursive(dst, dstNode, srcNode));
|
||||
}
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
if (topNode == NULL) {
|
||||
ncclResult_t ncclTopoFuseXml(struct ncclXml* dst, struct ncclXml* src) {
|
||||
struct ncclXmlNode* topNodeDst;
|
||||
NCCLCHECK(xmlFindTag(dst, "system", &topNodeDst));
|
||||
|
||||
if (topNodeDst == NULL) {
|
||||
xmlAddTree(dst, NULL, src->nodes);
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
// Fuse the CPUs with the first XML
|
||||
struct ncclXmlNode* srcCpu;
|
||||
NCCLCHECK(xmlFindTag(src, "cpu", &srcCpu));
|
||||
while (srcCpu) {
|
||||
const char* srcNumaId;
|
||||
const char* srcHostHash;
|
||||
NCCLCHECK(xmlGetAttr(srcCpu, "numaid", &srcNumaId));
|
||||
if (srcNumaId == NULL) {
|
||||
WARN("TopoFuseXmls : could not find CPU numa ID.");
|
||||
return ncclInternalError;
|
||||
}
|
||||
xmlGetAttr(srcCpu, "host_hash", &srcHostHash);
|
||||
if (srcHostHash == NULL)
|
||||
srcHostHash = "0";
|
||||
struct ncclXmlNode* topNodeSrc;
|
||||
NCCLCHECK(xmlFindTag(src, "system", &topNodeSrc));
|
||||
|
||||
// Search through the destination for a duplicate. Note that
|
||||
// this makes the complexity of this whole function O(n^2), but n
|
||||
// is expected to be small.
|
||||
struct ncclXmlNode* dstCpu;
|
||||
NCCLCHECK(xmlFindTag(dst, "cpu", &dstCpu));
|
||||
while (dstCpu) {
|
||||
const char* dstNumaId;
|
||||
const char* dstHostHash;
|
||||
NCCLCHECK(xmlGetAttr(dstCpu, "numaid", &dstNumaId));
|
||||
if (dstNumaId == NULL) {
|
||||
WARN("TopoFuseXmls : could not find CPU numa ID.");
|
||||
return ncclInternalError;
|
||||
}
|
||||
xmlGetAttr(dstCpu, "host_hash", &dstHostHash);
|
||||
if (dstHostHash == NULL)
|
||||
dstHostHash = "0";
|
||||
if (strcmp(srcNumaId, dstNumaId) == 0 && strcmp(srcHostHash, dstHostHash) == 0)
|
||||
break;
|
||||
NCCLCHECK(xmlTopoFuseXmlRecursive(dst, topNodeDst, topNodeSrc));
|
||||
|
||||
NCCLCHECK(xmlFindNextTag(dst, "cpu", dstCpu, &dstCpu));
|
||||
}
|
||||
// Only add the CPU if no duplicate was found
|
||||
if (dstCpu == NULL)
|
||||
NCCLCHECK(xmlAddTree(dst, topNode, srcCpu));
|
||||
NCCLCHECK(xmlFindNextTag(src, "cpu", srcCpu, &srcCpu));
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
@@ -338,6 +316,11 @@ ncclResult_t ncclTopoXmlLoadNvlink(FILE* file, struct ncclXml* xml, struct ncclX
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoXmlLoadPciLink(FILE* file, struct ncclXml* xml, struct ncclXmlNode* head) {
|
||||
NCCLCHECK(xmlLoadSub(file, xml, head, NULL, 0));
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoXmlLoadC2c(FILE* file, struct ncclXml* xml, struct ncclXmlNode* head) {
|
||||
NCCLCHECK(xmlLoadSub(file, xml, head, NULL, 0));
|
||||
return ncclSuccess;
|
||||
@@ -364,8 +347,8 @@ ncclResult_t ncclTopoXmlLoadNic(FILE* file, struct ncclXml* xml, struct ncclXmlN
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoXmlLoadPci(FILE* file, struct ncclXml* xml, struct ncclXmlNode* head) {
|
||||
struct xmlHandler handlers[] = { { "pci", ncclTopoXmlLoadPci }, { "gpu", ncclTopoXmlLoadGpu }, { "nic", ncclTopoXmlLoadNic} };
|
||||
NCCLCHECK(xmlLoadSub(file, xml, head, handlers, 3));
|
||||
struct xmlHandler handlers[] = { { "pci", ncclTopoXmlLoadPci }, { "gpu", ncclTopoXmlLoadGpu }, { "nic", ncclTopoXmlLoadNic}, { "pcilink", ncclTopoXmlLoadPciLink} };
|
||||
NCCLCHECK(xmlLoadSub(file, xml, head, handlers, 4));
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
@@ -435,6 +418,28 @@ static ncclResult_t getPciPath(const char* busId, char** path) {
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
#include <dirent.h>
|
||||
static ncclResult_t getBcmLinks(const char* busId, int* nlinks, char** peers) {
|
||||
*nlinks = 0;
|
||||
*peers = NULL;
|
||||
char dirPath[] = "/sys/kernel/pci_switch_link/virtual_switch_links/0000:00:00.0";
|
||||
memcpylower(dirPath+sizeof("/sys/kernel/pci_switch_link/virtual_switch_links/")-1, busId, BUSID_SIZE-1);
|
||||
DIR *dir = opendir(dirPath);
|
||||
if (dir) {
|
||||
struct dirent* file;
|
||||
while ((file = readdir(dir)) != NULL) {
|
||||
if (strlen(file->d_name) != BUSID_SIZE-1) continue;
|
||||
char* path;
|
||||
if (getPciPath(file->d_name, &path) == ncclSystemError) continue;
|
||||
free(path);
|
||||
NCCLCHECK(ncclRealloc(peers, (*nlinks)*BUSID_SIZE, ((*nlinks)+1)*BUSID_SIZE));
|
||||
memcpy((*peers)+BUSID_SIZE*(*nlinks)++, file->d_name, BUSID_SIZE);
|
||||
}
|
||||
closedir(dir);
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoGetStrFromSys(const char* path, const char* fileName, char* strValue) {
|
||||
char filePath[PATH_MAX];
|
||||
sprintf(filePath, "%s/%s", path, fileName);
|
||||
@@ -553,10 +558,11 @@ ncclResult_t ncclTopoGetPciNode(struct ncclXml* xml, const char* busId, struct n
|
||||
// There can be trailing chars.
|
||||
int isHex(char c) { return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); }
|
||||
int checkBDFFormat(char* bdf) {
|
||||
if (bdf[4] != ':' || bdf[7] != ':' || bdf[10] != '.') return 0;
|
||||
if (isHex(bdf[0]) == 0 || isHex(bdf[1] == 0) || isHex(bdf[2] == 0) || isHex(bdf[3] == 0) ||
|
||||
isHex(bdf[5] == 0) || isHex(bdf[6] == 0) || isHex(bdf[8] == 0) || isHex(bdf[9] == 0) ||
|
||||
isHex(bdf[11] == 0)) return 0;
|
||||
if (strlen(bdf) != 12) return 0;
|
||||
if ((bdf[4] != ':') || (bdf[7] != ':') || (bdf[10] != '.')) return 0;
|
||||
if ((isHex(bdf[0]) == 0) || (isHex(bdf[1]) == 0) || (isHex(bdf[2]) == 0) || (isHex(bdf[3]) == 0) ||
|
||||
(isHex(bdf[5]) == 0) || (isHex(bdf[6]) == 0) || (isHex(bdf[8]) == 0) || (isHex(bdf[9]) == 0) ||
|
||||
(isHex(bdf[11]) == 0)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -627,6 +633,24 @@ ncclResult_t ncclTopoGetXmlFromSys(struct ncclXmlNode* pciNode, struct ncclXml*
|
||||
NCCLCHECK(xmlSetAttr(pciNode, "link_width", ""));
|
||||
}
|
||||
}
|
||||
|
||||
const char* vendor;
|
||||
NCCLCHECK(xmlGetAttr(pciNode, "vendor", &vendor));
|
||||
if (vendor != NULL && strcmp(vendor, "0x1000") == 0) { // BCM switch, look for P2P connections
|
||||
int nlinks;
|
||||
char* peers;
|
||||
NCCLCHECK(getBcmLinks(busId, &nlinks, &peers));
|
||||
for (int l=0; l<nlinks; l++) {
|
||||
char* target = peers+l*BUSID_SIZE;
|
||||
struct ncclXmlNode* linkNode;
|
||||
NCCLCHECK(xmlGetSubKv(pciNode, "pcilink", &linkNode, "target", target));
|
||||
if (linkNode == NULL) {
|
||||
NCCLCHECK(xmlAddNode(xml, pciNode, "pcilink", &linkNode));
|
||||
NCCLCHECK(xmlSetAttr(linkNode, "target", target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ncclXmlNode* parent = pciNode->parent;
|
||||
if (parent == NULL) {
|
||||
if (path) {
|
||||
@@ -1002,25 +1026,33 @@ ncclResult_t ncclTopoFillNet(struct ncclXml* xml, const char* pciPath, const cha
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoTrimXmlRec(struct ncclXmlNode* node) {
|
||||
ncclResult_t ncclTopoTrimXmlRec(struct ncclXmlNode* node, int* keep) {
|
||||
const char* str;
|
||||
NCCLCHECK(xmlGetAttr(node, "keep", &str));
|
||||
if (str && strcmp(str, "1") == 0) {
|
||||
NCCLCHECK(xmlUnsetAttr(node, "keep"));
|
||||
*keep = 1;
|
||||
} else {
|
||||
// Copy nSubs and subs as they could change as we trim recursively.
|
||||
struct ncclXmlNode* subs[MAX_SUBS];
|
||||
int nSubs = node->nSubs;
|
||||
memcpy(subs, node->subs, node->nSubs*sizeof(struct ncclXmlNode*));
|
||||
*keep = 0;
|
||||
for (int s=0; s<nSubs; s++) {
|
||||
NCCLCHECK(ncclTopoTrimXmlRec(subs[s]));
|
||||
int k = 0;
|
||||
NCCLCHECK(ncclTopoTrimXmlRec(subs[s], &k));
|
||||
*keep += k;
|
||||
}
|
||||
if (*keep == 0 && // Trim PCI switches or CPU with no used GPU/NIC under them.
|
||||
(strcmp(node->name, "pci") == 0 || strcmp(node->name, "cpu") == 0)) {
|
||||
NCCLCHECK(xmlRemoveNode(node));
|
||||
}
|
||||
if (node->nSubs == 0) NCCLCHECK(xmlRemoveNode(node));
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
ncclResult_t ncclTopoTrimXml(struct ncclXml* xml) {
|
||||
NCCLCHECK(ncclTopoTrimXmlRec(xml->nodes));
|
||||
int keep = 0;
|
||||
NCCLCHECK(ncclTopoTrimXmlRec(xml->nodes, &keep));
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
|
||||
+24
-1
@@ -57,7 +57,7 @@ ncclResult_t ncclTopoFillNet(struct ncclXml* xml, const char* pciPath, const cha
|
||||
/* Remove unneeded parts */
|
||||
ncclResult_t ncclTopoTrimXml(struct ncclXml* xml);
|
||||
|
||||
/* Fuse multiple system XMLs into one, skipping duplicate CPUs */
|
||||
/* Fuse multiple system XMLs into one, skipping duplicate entries */
|
||||
ncclResult_t ncclTopoFuseXml(struct ncclXml* dst, struct ncclXml* src);
|
||||
/* Relocate pointers in XML to (de-)serialize the structure */
|
||||
ncclResult_t ncclTopoConvertXml(struct ncclXml* xml, uintptr_t base, int exp);
|
||||
@@ -176,6 +176,29 @@ static ncclResult_t xmlFindTagKv(struct ncclXml* xml, const char* tagName, struc
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
static ncclResult_t xmlFindNode(struct ncclXmlNode* parentNode, struct ncclXmlNode* searchNode, struct ncclXmlNode** node) {
|
||||
*node = NULL;
|
||||
// Search for the node at the current level only.
|
||||
for (int i=0; i<parentNode->nSubs; i++) {
|
||||
struct ncclXmlNode* n = parentNode->subs[i];
|
||||
if (strcmp(n->name, searchNode->name) == 0 && n->type == searchNode->type && n->nAttrs == searchNode->nAttrs) {
|
||||
int a;
|
||||
// Ensure that all the attributes are the same.
|
||||
for (a=0; a<searchNode->nAttrs; a++) {
|
||||
const char* val;
|
||||
NCCLCHECK(xmlGetAttr(n, searchNode->attrs[a].key, &val));
|
||||
if (!val || strcmp(val, searchNode->attrs[a].value))
|
||||
break;
|
||||
}
|
||||
if (a == searchNode->nAttrs) {
|
||||
*node = n;
|
||||
return ncclSuccess;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
static ncclResult_t xmlSetAttr(struct ncclXmlNode* node, const char* attrName, const char* value) {
|
||||
int index;
|
||||
NCCLCHECK(xmlGetAttrIndex(node, attrName, &index));
|
||||
|
||||
Ссылка в новой задаче
Block a user