Merge remote-tracking branch 'nccl/master' into develop
Этот коммит содержится в:
@@ -731,7 +731,7 @@ ncclResult_t ncclTopoPostset(struct ncclComm* comm, int* firstRanks, int* treePa
|
||||
NCCLCHECKGOTO(ncclCalloc(&nvlsHeads, nNodes*MAXCHANNELS), ret, fail);
|
||||
|
||||
// Alternate rings to avoid crossing rails
|
||||
if (graphs[NCCL_ALGO_RING]->crossNic && (nChannels % 2) == 0) {
|
||||
if (graphs[NCCL_ALGO_RING]->crossNic == 2 && (nChannels % 2) == 0) {
|
||||
for (int r=0; r<comm->nRanks; r++) {
|
||||
if (comm->rankToNode[r] % 2 == 1) {
|
||||
// Exchange rings
|
||||
|
||||
+78
-20
@@ -380,8 +380,8 @@ ncclResult_t ncclTopoCheckMNNVL(struct ncclTopoSystem* system, struct ncclPeerIn
|
||||
if ((((long *)&fabricInfo2->clusterUuid)[0]|((long *)fabricInfo2->clusterUuid)[1]) == 0) return ncclSuccess;
|
||||
if ((memcmp(fabricInfo1->clusterUuid, fabricInfo2->clusterUuid, NVML_GPU_FABRIC_UUID_LEN) == 0) &&
|
||||
(fabricInfo1->cliqueId == fabricInfo2->cliqueId)) {
|
||||
INFO(NCCL_NET, "MNNVL matching peer 0x%lx UUID %lx.%lx cliqueId 0x%x",
|
||||
info2->busId, ((long *)fabricInfo2->clusterUuid)[0], ((long *)fabricInfo2->clusterUuid)[1], fabricInfo2->cliqueId);
|
||||
TRACE(NCCL_NET, "MNNVL matching peer 0x%lx UUID %lx.%lx cliqueId 0x%x",
|
||||
info2->busId, ((long *)fabricInfo2->clusterUuid)[0], ((long *)fabricInfo2->clusterUuid)[1], fabricInfo2->cliqueId);
|
||||
*ret = 1;
|
||||
}
|
||||
return ncclSuccess;
|
||||
@@ -389,9 +389,12 @@ ncclResult_t ncclTopoCheckMNNVL(struct ncclTopoSystem* system, struct ncclPeerIn
|
||||
|
||||
NCCL_PARAM(NetGdrRead, "NET_GDR_READ", -2);
|
||||
int ncclTopoUserGdrLevel = -1;
|
||||
const char* ncclTopoGdrModeStr[ncclTopoGdrModeNum] = { "Disabled", "Default", "PCI" };
|
||||
|
||||
ncclResult_t ncclTopoCheckGdr(struct ncclTopoSystem* system, int rank, int64_t netId, int read, int* useGdr) {
|
||||
*useGdr = 0;
|
||||
NCCL_PARAM(NetGdrC2c, "NET_GDR_C2C", 0);
|
||||
|
||||
ncclResult_t ncclTopoCheckGdr(struct ncclTopoSystem* system, int rank, int64_t netId, int read, enum ncclTopoGdrMode* gdrMode) {
|
||||
*gdrMode = ncclTopoGdrModeDisable;
|
||||
|
||||
// Get GPU and NET
|
||||
int n, g;
|
||||
@@ -434,7 +437,7 @@ ncclResult_t ncclTopoCheckGdr(struct ncclTopoSystem* system, int rank, int64_t n
|
||||
else {
|
||||
int arch, vendor, model;
|
||||
NCCLCHECK(ncclTopoCpuType(system, &arch, &vendor, &model));
|
||||
if (arch == NCCL_TOPO_CPU_ARCH_X86 && vendor == NCCL_TOPO_CPU_VENDOR_AMD && model == NCCL_TOPO_CPU_TYPE_ROME) {
|
||||
if (arch == NCCL_TOPO_CPU_ARCH_X86 && vendor == NCCL_TOPO_CPU_VENDOR_AMD && model == NCCL_TOPO_CPU_MODEL_AMD_ROME) {
|
||||
int i, d1 = -1, d2 = -1;
|
||||
for (i = 0; i < system->nodes[CPU].count; i++)
|
||||
if (system->nodes[GPU].nodes[g].paths[CPU][i].count == 2) break;
|
||||
@@ -452,25 +455,37 @@ ncclResult_t ncclTopoCheckGdr(struct ncclTopoSystem* system, int rank, int64_t n
|
||||
int distance = gpu->paths[NET][n].type;
|
||||
if (distance == PATH_PXN) {
|
||||
// In case of PXN, use the intermediate GPU distance instead
|
||||
int proxyRank, g;
|
||||
int proxyRank;
|
||||
NCCLCHECK(ncclTopoGetIntermediateRank(system, gpu->gpu.rank, netId, &proxyRank));
|
||||
NCCLCHECK(ncclTopoRankToIndex(system, proxyRank, &g));
|
||||
struct ncclTopoNode* proxyGpu = system->nodes[GPU].nodes+g;
|
||||
distance = proxyGpu->paths[NET][n].type;
|
||||
gpu = system->nodes[GPU].nodes+g;
|
||||
distance = gpu->paths[NET][n].type;
|
||||
}
|
||||
|
||||
int c;
|
||||
NCCLCHECK(ncclGetLocalCpu(system, g, &c));
|
||||
if (ncclParamNetGdrC2c() && distance == PATH_PHB && gpu->paths[CPU][c].type == PATH_C2C) {
|
||||
// On C2C platforms we can still use GDRDMA on NICs connected to the CPUs
|
||||
INFO(NCCL_NET, "GPU %d / HCA %lx connected to CPU %d via C2C link", rank, netId, c);
|
||||
distance = PATH_C2C;
|
||||
}
|
||||
|
||||
if (distance > netGdrLevel) {
|
||||
INFO(NCCL_NET,"GPU Direct RDMA Disabled for GPU %d / HCA %lx (distance %d > %d)", rank, netId, distance, netGdrLevel);
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
*useGdr = 1;
|
||||
INFO(NCCL_NET,"GPU Direct RDMA Enabled for GPU %d / HCA %lx (distance %d <= %d), read %d", rank, netId, distance, netGdrLevel, read);
|
||||
// Force PCIe mapping if path goes through PCI on a C2C system
|
||||
if (gpu->paths[CPU][c].type == PATH_C2C && distance != PATH_C2C) *gdrMode = ncclTopoGdrModePci;
|
||||
else *gdrMode = ncclTopoGdrModeDefault;
|
||||
|
||||
INFO(NCCL_NET,"GPU Direct RDMA Enabled for GPU %d / HCA %lx (distance %d <= %d), read %d mode %s", rank, netId, distance, netGdrLevel, read, ncclTopoGdrModeStr[*gdrMode]);
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoIsGdrAvail(struct ncclTopoSystem* system, int rank, bool *avail) {
|
||||
int netNum = system->nodes[NET].count;
|
||||
int useGdr = 0;
|
||||
enum ncclTopoGdrMode useGdr = ncclTopoGdrModeDisable;
|
||||
*avail = false;
|
||||
for (int n = 0; n < netNum; n++) {
|
||||
int64_t netId = system->nodes[NET].nodes[n].id;
|
||||
@@ -492,7 +507,7 @@ ncclResult_t ncclTopoIsGdrAvail(struct ncclTopoSystem* system, int rank, bool *a
|
||||
NCCL_PARAM(NetForceFlush, "NET_FORCE_FLUSH", 0);
|
||||
|
||||
// Determine whether we need to flush the GDR recv buffers
|
||||
ncclResult_t ncclTopoNeedFlush(struct ncclComm* comm, int netDev, int rank, int* flush) {
|
||||
ncclResult_t ncclTopoNeedFlush(struct ncclComm* comm, int64_t netId, int netDev, int rank, int* flush) {
|
||||
*flush = 1;
|
||||
ncclNetProperties_t props;
|
||||
NCCLCHECK(comm->ncclNet->getProperties(netDev, &props));
|
||||
@@ -506,6 +521,14 @@ ncclResult_t ncclTopoNeedFlush(struct ncclComm* comm, int netDev, int rank, int*
|
||||
#else
|
||||
// Flush is required on Ampere and earlier
|
||||
if (gpu->gpu.cudaCompCap >= 90) *flush = 0;
|
||||
// On C2C platforms, data could go through a PCI switch while completions and
|
||||
// flags would go through C2C. In that case, force a flush.
|
||||
int c, n;
|
||||
NCCLCHECK(ncclGetLocalCpu(system, g, &c));
|
||||
NCCLCHECK(ncclTopoIdToIndex(system, NET, netId, &n));
|
||||
if (gpu->paths[NET][n].type <= PATH_PXB && gpu->paths[CPU][c].type == PATH_C2C) {
|
||||
*flush = 1;
|
||||
}
|
||||
#endif
|
||||
return ncclSuccess;
|
||||
}
|
||||
@@ -576,7 +599,7 @@ NCCL_PARAM(PxnDisable, "PXN_DISABLE", 1);
|
||||
int ncclPxnDisable(struct ncclComm* comm) {
|
||||
static int pxnDisable = -1;
|
||||
if (pxnDisable == -1) {
|
||||
if (comm && ncclNetVersion(comm) == 4) {
|
||||
if (comm && comm->ncclNetVer == 4) {
|
||||
INFO(NCCL_INIT, "PXN Disabled as plugin is v4");
|
||||
pxnDisable = 1;
|
||||
} else {
|
||||
@@ -599,9 +622,9 @@ ncclResult_t ncclTopoGetPxnRanks(struct ncclComm* comm, int** intermediateRanks,
|
||||
int proxyRank;
|
||||
NCCLCHECK(ncclTopoGetNetDev(comm, comm->rank, NULL, 0, rank, &netId, NULL, &proxyRank));
|
||||
if (proxyRank == comm->rank) continue;
|
||||
int useGdr;
|
||||
enum ncclTopoGdrMode useGdr;
|
||||
NCCLCHECK(ncclTopoCheckGdr(comm->topo, comm->rank, netId, 1, &useGdr));
|
||||
if (useGdr == 0) continue;
|
||||
if (useGdr == ncclTopoGdrModeDisable) continue;
|
||||
int found = 0;
|
||||
for (int r=0; r<nr; r++) {
|
||||
if (ranks[r] == proxyRank) found = 1;
|
||||
@@ -746,7 +769,7 @@ ncclResult_t ncclTopoComputePaths(struct ncclTopoSystem* system, struct ncclComm
|
||||
}
|
||||
if (gpu->paths[NET][n].type < PATH_PHB) {
|
||||
// Update path when we dont want to / can't use GPU Direct RDMA.
|
||||
int gdr;
|
||||
enum ncclTopoGdrMode gdr;
|
||||
NCCLCHECK(ncclTopoCheckGdr(system, system->nodes[GPU].nodes[g].gpu.rank, netNode->id, 0, &gdr));
|
||||
if (gdr == 0) {
|
||||
// We cannot use GPU Direct RDMA, divert all traffic through the CPU local to the GPU
|
||||
@@ -770,7 +793,7 @@ ncclResult_t ncclTopoTrimSystem(struct ncclTopoSystem* system, struct ncclComm*
|
||||
int myDomain = 0;
|
||||
int ngpus = system->nodes[GPU].count;
|
||||
int remove = 1;
|
||||
int gdr = 1;
|
||||
enum ncclTopoGdrMode useGdr = ncclTopoGdrModeDefault;
|
||||
bool allXgmi = true;
|
||||
NCCLCHECK(ncclCalloc(&domains, system->nodes[GPU].count));
|
||||
NCCLCHECKGOTO(ncclCalloc(&ids, system->nodes[GPU].count), ret, fail);
|
||||
@@ -839,10 +862,10 @@ ncclResult_t ncclTopoTrimSystem(struct ncclTopoSystem* system, struct ncclComm*
|
||||
for (int g = 0; g < system->nodes[GPU].count; g++) {
|
||||
int64_t netId;
|
||||
NCCLCHECKGOTO(ncclTopoGetLocalNet(system, system->nodes[GPU].nodes[g].gpu.rank, 0, &netId, nullptr), ret, fail);
|
||||
NCCLCHECKGOTO(ncclTopoCheckGdr(system, system->nodes[GPU].nodes[g].gpu.rank, netId, 1, &gdr), ret, fail);
|
||||
if (!gdr) break;
|
||||
NCCLCHECKGOTO(ncclTopoCheckGdr(system, system->nodes[GPU].nodes[g].gpu.rank, netId, 1, &useGdr), ret, fail);
|
||||
if (!useGdr) break;
|
||||
}
|
||||
if (gdr && !allXgmi) {
|
||||
if (useGdr && !allXgmi) {
|
||||
remove = 0;
|
||||
system->type |= RCCL_TOPO_GDR_ALL;
|
||||
INFO(NCCL_GRAPH, "GDR is available on all GPUs");
|
||||
@@ -1014,3 +1037,38 @@ ncclResult_t ncclTopoPathAllNVLink(struct ncclTopoSystem* system, int* allNvLink
|
||||
*allNvLink = maxPath >= PATH_PIX ? 0 : 1;
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
// Check whether we are in a split NVLink situation, with two NVLink domains, not
|
||||
// connected through NVLink (e.g. QPI).
|
||||
ncclResult_t ncclTopoSplitNvLink(struct ncclTopoSystem* system, int* splitNvLink) {
|
||||
ncclResult_t res = ncclSuccess;
|
||||
int nvlDomains = 0;
|
||||
int *nvlDomain = NULL, *nvlDomainCount = NULL;
|
||||
// Compute NVLink domains
|
||||
NCCLCHECKGOTO(ncclCalloc(&nvlDomain, system->nodes[GPU].count), res, exit);
|
||||
for (int g=0; g<system->nodes[GPU].count; g++) nvlDomain[g] = g;
|
||||
for (int g=0; g<system->nodes[GPU].count; g++) {
|
||||
struct ncclTopoNode* gpu = system->nodes[GPU].nodes+g;
|
||||
int domain = nvlDomain[g];
|
||||
for (int p=g+1; p<system->nodes[GPU].count; p++) {
|
||||
if (gpu->paths[GPU][p].type == PATH_NVL) {
|
||||
nvlDomain[p] = domain;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Compute number of GPUs per NVLink domain.
|
||||
NCCLCHECKGOTO(ncclCalloc(&nvlDomainCount, system->nodes[GPU].count), res, exit);
|
||||
for (int g=0; g<system->nodes[GPU].count; g++) {
|
||||
nvlDomainCount[nvlDomain[g]]++;
|
||||
}
|
||||
// Count the number of NVLink domains
|
||||
for (int g=0; g<system->nodes[GPU].count; g++) {
|
||||
if (nvlDomainCount[g] > 1) nvlDomains++;
|
||||
}
|
||||
*splitNvLink = nvlDomains == 2 ? 1 : 0;
|
||||
|
||||
exit:
|
||||
if(nvlDomain) free(nvlDomain);
|
||||
if(nvlDomainCount) free(nvlDomainCount);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -2075,7 +2075,7 @@ ncclResult_t parseRome4P2H(struct ncclTopoSystem* system, struct ncclTopoGraph*
|
||||
|
||||
for (i = 0; i < sizeof(romeTopoModels)/sizeof(romeTopoModels[0]); i++) {
|
||||
bool ignore_cpu = checkOption(romeTopoModels[i].options, "noCpuCheck");
|
||||
if (!ignore_cpu && (arch != NCCL_TOPO_CPU_ARCH_X86 || vendor != NCCL_TOPO_CPU_VENDOR_AMD || model != NCCL_TOPO_CPU_TYPE_ROME))
|
||||
if (!ignore_cpu && (arch != NCCL_TOPO_CPU_ARCH_X86 || vendor != NCCL_TOPO_CPU_VENDOR_AMD || model != NCCL_TOPO_CPU_MODEL_AMD_ROME))
|
||||
continue;
|
||||
bool ignore_numa = checkOption(romeTopoModels[i].options, "disableNumaMatching");
|
||||
if (!ignore_numa && romeTopo.nCpus != romeTopoModels[i].nCpus) continue;
|
||||
@@ -2225,7 +2225,7 @@ ncclResult_t parse1H16P(struct ncclTopoSystem* system, struct ncclTopoGraph* gra
|
||||
// only valid on Rome
|
||||
int arch, vendor, model;
|
||||
NCCLCHECK(ncclTopoCpuType(system, &arch, &vendor, &model));
|
||||
if (arch != NCCL_TOPO_CPU_ARCH_X86 || vendor != NCCL_TOPO_CPU_VENDOR_AMD || model != NCCL_TOPO_CPU_TYPE_ROME)
|
||||
if (arch != NCCL_TOPO_CPU_ARCH_X86 || vendor != NCCL_TOPO_CPU_VENDOR_AMD || model != NCCL_TOPO_CPU_MODEL_AMD_ROME)
|
||||
return ncclSuccess;
|
||||
|
||||
// number of GPUs and NICs on each numa node is used as first screening pattern
|
||||
@@ -2396,7 +2396,7 @@ ncclResult_t parse4H4P(struct ncclTopoSystem* system, struct ncclTopoGraph* grap
|
||||
// only valid on Rome
|
||||
int arch, vendor, model;
|
||||
NCCLCHECK(ncclTopoCpuType(system, &arch, &vendor, &model));
|
||||
if (arch != NCCL_TOPO_CPU_ARCH_X86 || vendor != NCCL_TOPO_CPU_VENDOR_AMD || model != NCCL_TOPO_CPU_TYPE_ROME)
|
||||
if (arch != NCCL_TOPO_CPU_ARCH_X86 || vendor != NCCL_TOPO_CPU_VENDOR_AMD || model != NCCL_TOPO_CPU_MODEL_AMD_ROME)
|
||||
return ncclSuccess;
|
||||
|
||||
// number of GPUs and NICs on each numa node is used as first screening pattern
|
||||
@@ -2460,7 +2460,7 @@ ncclResult_t parse4H4P(struct ncclTopoSystem* system, struct ncclTopoGraph* grap
|
||||
}
|
||||
}
|
||||
INFO(NCCL_GRAPH, "%s", line);
|
||||
if (arch == NCCL_TOPO_CPU_ARCH_X86 && vendor == NCCL_TOPO_CPU_VENDOR_AMD && model == NCCL_TOPO_CPU_TYPE_ROME)
|
||||
if (arch == NCCL_TOPO_CPU_ARCH_X86 && vendor == NCCL_TOPO_CPU_VENDOR_AMD && model == NCCL_TOPO_CPU_MODEL_AMD_ROME)
|
||||
system->type |= RCCL_TOPO_4P2H_ROME;
|
||||
parseOptions(system, rome_model_68.options);
|
||||
// create 4P4H based on reference and remapped ids
|
||||
|
||||
+35
-43
@@ -483,12 +483,11 @@ ncclResult_t ncclTopoCompareGraphs(struct ncclTopoSystem* system, struct ncclTop
|
||||
// 1. Select NETs starting with those close to GPU(s), based on paths[n].type.
|
||||
// 2. add other NETs satisfying typeInter but not already in the list.
|
||||
|
||||
ncclResult_t ncclTopoSelectNets(struct ncclTopoSystem* system, int typeInter, int gpu, int* nets, int* netCountRet) {
|
||||
ncclResult_t ncclTopoSelectNets(struct ncclTopoSystem* system, int typeInter, int gpu, int nets[NCCL_TOPO_MAX_NODES], int* netCountRet) {
|
||||
ncclResult_t ret = ncclSuccess;
|
||||
int netCount = 0;
|
||||
int localNetCount;
|
||||
int* localNets;
|
||||
NCCLCHECK(ncclCalloc(&localNets, MAXCHANNELS));
|
||||
int localNets[MAXCHANNELS];
|
||||
|
||||
// First add the preferred NICs
|
||||
for (int g=0; g<system->nodes[GPU].count; g++) {
|
||||
@@ -497,8 +496,8 @@ ncclResult_t ncclTopoSelectNets(struct ncclTopoSystem* system, int typeInter, in
|
||||
struct ncclTopoNode* gpu = system->nodes[GPU].nodes+g;
|
||||
for (int c = 0; c<MAXCHANNELS; c++) {
|
||||
int64_t netId;
|
||||
NCCLCHECKGOTO(ncclTopoGetLocalNet(system, gpu->gpu.rank, c, &netId, NULL), ret, fail);
|
||||
NCCLCHECKGOTO(ncclTopoIdToIndex(system, NET, netId, localNets+localNetCount), ret, fail);
|
||||
NCCLCHECK(ncclTopoGetLocalNet(system, gpu->gpu.rank, c, &netId, NULL));
|
||||
NCCLCHECK(ncclTopoIdToIndex(system, NET, netId, localNets+localNetCount));
|
||||
if (localNetCount > 0 && localNets[localNetCount] == localNets[0]) break;
|
||||
localNetCount++;
|
||||
}
|
||||
@@ -506,7 +505,7 @@ ncclResult_t ncclTopoSelectNets(struct ncclTopoSystem* system, int typeInter, in
|
||||
for (int i=0; i<localNetCount; i++) {
|
||||
int n = localNets[i];
|
||||
int found = 0;
|
||||
while (nets[found] != n && found<netCount) found++;
|
||||
while (found<netCount && nets[found] != n) found++;
|
||||
if (found == netCount) nets[netCount++] = n;
|
||||
}
|
||||
}
|
||||
@@ -525,22 +524,17 @@ ncclResult_t ncclTopoSelectNets(struct ncclTopoSystem* system, int typeInter, in
|
||||
for (int i=0; i<localNetCount; i++) {
|
||||
int n = localNets[i];
|
||||
int found = 0;
|
||||
while (nets[found] != n && found<netCount) found++;
|
||||
while (found<netCount && nets[found] != n) found++;
|
||||
if (found == netCount) nets[netCount++] = n;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*netCountRet = netCount;
|
||||
exit:
|
||||
free(localNets);
|
||||
return ret;
|
||||
fail:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoSearchRecGpu(struct ncclTopoSystem* system, struct ncclTopoGraph* graph, struct ncclTopoGraph* saveGraph, struct ncclTopoNode* gpu, int step, int backToNet, int backToFirstRank, int forcedOrder, int *time) {
|
||||
ncclResult_t ret = ncclSuccess;
|
||||
if ((*time) <= 0) return ncclSuccess;
|
||||
(*time)--;
|
||||
|
||||
@@ -562,7 +556,7 @@ ncclResult_t ncclTopoSearchRecGpu(struct ncclTopoSystem* system, struct ncclTopo
|
||||
}
|
||||
graph->intra[graph->nChannels*ngpus+step] = gpu->gpu.rank;
|
||||
int g = gpu - system->nodes[GPU].nodes;
|
||||
int* nets = NULL;
|
||||
int nets[NCCL_TOPO_MAX_NODES];
|
||||
if (step == backToNet) {
|
||||
// first get back to NIC
|
||||
if (system->nodes[NET].count) {
|
||||
@@ -570,8 +564,7 @@ ncclResult_t ncclTopoSearchRecGpu(struct ncclTopoSystem* system, struct ncclTopo
|
||||
NCCLCHECK(getNetIndex(system, graph->inter[graph->nChannels*2], &startNetIndex));
|
||||
struct ncclTopoNode* startNet = system->nodes[NET].nodes+startNetIndex;
|
||||
int netCount;
|
||||
NCCLCHECK(ncclCalloc(&nets, system->nodes[NET].count));
|
||||
NCCLCHECKGOTO(ncclTopoSelectNets(system, graph->typeInter, g, nets, &netCount), ret, fail);
|
||||
NCCLCHECK(ncclTopoSelectNets(system, graph->typeInter, g, nets, &netCount));
|
||||
for (int i=0; i<netCount; i++) {
|
||||
int n = nets[i];
|
||||
struct ncclTopoNode* net = system->nodes[NET].nodes+n;
|
||||
@@ -592,14 +585,14 @@ ncclResult_t ncclTopoSearchRecGpu(struct ncclTopoSystem* system, struct ncclTopo
|
||||
graph->bwInter /= 2;
|
||||
}
|
||||
|
||||
NCCLCHECKGOTO(ncclTopoFollowPath(system, graph, GPU, g, NET, n, 1, &net), ret, fail);
|
||||
NCCLCHECK(ncclTopoFollowPath(system, graph, GPU, g, NET, n, 1, &net));
|
||||
graph->bwInter = bwInterSave;
|
||||
if (net) {
|
||||
graph->inter[graph->nChannels*2+1] = net->id;
|
||||
NCCLCHECKGOTO(ncclTopoSearchRecGpu(system, graph, saveGraph, gpu, step, nextBackToNet, backToFirstRank, forcedOrder, time), ret, fail);
|
||||
NCCLCHECK(ncclTopoSearchRecGpu(system, graph, saveGraph, gpu, step, nextBackToNet, backToFirstRank, forcedOrder, time));
|
||||
|
||||
if (graph->pattern == NCCL_TOPO_PATTERN_BALANCED_TREE) graph->bwInter /= 2;
|
||||
NCCLCHECKGOTO(ncclTopoFollowPath(system, graph, GPU, g, NET, n, -1, &net), ret, fail);
|
||||
NCCLCHECK(ncclTopoFollowPath(system, graph, GPU, g, NET, n, -1, &net));
|
||||
graph->bwInter = bwInterSave;
|
||||
}
|
||||
}
|
||||
@@ -638,21 +631,15 @@ ncclResult_t ncclTopoSearchRecGpu(struct ncclTopoSystem* system, struct ncclTopo
|
||||
// Next path
|
||||
NCCLCHECK(ncclTopoSearchRecGpu(system, graph, saveGraph, gpu, ngpus, -1, -1, forcedOrder, time));
|
||||
}
|
||||
exit:
|
||||
if (nets) free(nets);
|
||||
return ret;
|
||||
fail:
|
||||
goto exit;
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopoGraph* graph, struct ncclTopoGraph* saveGraph, int backToNet, int backToFirstRank, int* time) {
|
||||
ncclResult_t ret = ncclSuccess;
|
||||
const int bw = graph->bwInter;
|
||||
int* nets;
|
||||
NCCLCHECK(ncclCalloc(&nets, system->nodes[NET].count));
|
||||
int nets[NCCL_TOPO_MAX_NODES];
|
||||
int netCount;
|
||||
int graphFound = 0;
|
||||
NCCLCHECKGOTO(ncclTopoSelectNets(system, graph->typeInter, -1, nets, &netCount), ret, fail);
|
||||
NCCLCHECK(ncclTopoSelectNets(system, graph->typeInter, -1, nets, &netCount));
|
||||
for (int i=0; i<netCount; i++) {
|
||||
if ((graph->pattern == NCCL_TOPO_PATTERN_NVLS || graph->pattern == NCCL_TOPO_PATTERN_COLLNET_DIRECT) && graphFound) break;
|
||||
int n = nets[(graph->nChannels+i)%netCount];
|
||||
@@ -676,7 +663,7 @@ ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopo
|
||||
// NVLS search only tries to find NIC:GPU combinations to compute the heads.
|
||||
if (graph->nChannels < netCount) {
|
||||
int gpu;
|
||||
NCCLCHECKGOTO(ncclTopoGetLocalGpu(system, net->id, &gpu), ret, fail);
|
||||
NCCLCHECK(ncclTopoGetLocalGpu(system, net->id, &gpu));
|
||||
if (gpu != -1) {
|
||||
int duplicate = 0;
|
||||
// check whether there is duplicate head when one GPU connects with multiple NICs
|
||||
@@ -687,7 +674,7 @@ ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopo
|
||||
}
|
||||
}
|
||||
if (!duplicate) {
|
||||
NCCLCHECKGOTO(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, 0, time, NET, n, gpu), ret, fail);
|
||||
NCCLCHECK(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, 0, time, NET, n, gpu));
|
||||
graphFound = 1;
|
||||
}
|
||||
}
|
||||
@@ -696,8 +683,8 @@ ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopo
|
||||
if (graph->nChannels > 0) {
|
||||
// Try to replay the last channel
|
||||
int g;
|
||||
NCCLCHECKGOTO(ncclTopoReplayGetGpu(system, graph, -1, &g), ret, fail);
|
||||
NCCLCHECKGOTO(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, FORCED_ORDER_REPLAY, time, NET, n, g), ret, fail);
|
||||
NCCLCHECK(ncclTopoReplayGetGpu(system, graph, -1, &g));
|
||||
NCCLCHECK(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, FORCED_ORDER_REPLAY, time, NET, n, g));
|
||||
}
|
||||
if (graph->nChannels == 0 || graph->sameChannels == 0) {
|
||||
if (graph->nChannels == 0 && system->nodes[NVS].count == 0) {
|
||||
@@ -708,16 +695,16 @@ ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopo
|
||||
for (int i = 0; i<system->nodes[GPU].count; i++) {
|
||||
if (paths[i].count <= paths[f].count) {
|
||||
// prefer GPU direct RDMA
|
||||
int gdr;
|
||||
NCCLCHECK(ncclTopoCheckGdr(system, system->nodes[GPU].nodes[i].id, net->id, 0, &gdr));
|
||||
if (paths[i].count < paths[f].count || (paths[i].count == paths[f].count && !f_gdr && gdr)) {
|
||||
enum ncclTopoGdrMode useGdr;
|
||||
NCCLCHECK(ncclTopoCheckGdr(system, system->nodes[GPU].nodes[i].id, net->id, 0, &useGdr));
|
||||
if (paths[i].count < paths[f].count || (paths[i].count == paths[f].count && !f_gdr && useGdr)) {
|
||||
f = i;
|
||||
f_gdr = gdr;
|
||||
f_gdr = useGdr;
|
||||
}
|
||||
}
|
||||
}
|
||||
int t = 1 << 10;
|
||||
NCCLCHECKGOTO(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, FORCED_ORDER_PCI, &t, NET, n, 0), ret, fail);
|
||||
NCCLCHECK(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, FORCED_ORDER_PCI, &t, NET, n, 0));
|
||||
if (t == -1) *time = -1;
|
||||
}
|
||||
|
||||
@@ -737,7 +724,7 @@ ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopo
|
||||
for (int i=0; i<system->nodes[GPU].count; i++) {
|
||||
int g = (graph->nChannels+i)%system->nodes[GPU].count;
|
||||
if (paths[g].bw == maxBw && paths[g].count == minHops) {
|
||||
NCCLCHECKGOTO(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, 0, time, NET, n, g), ret, fail);
|
||||
NCCLCHECK(ncclTopoSearchTryGpu(system, graph, saveGraph, 0, backToNet, backToFirstRank, 0, time, NET, n, g));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -751,11 +738,7 @@ ncclResult_t ncclTopoSearchRecNet(struct ncclTopoSystem* system, struct ncclTopo
|
||||
}
|
||||
}
|
||||
}
|
||||
exit:
|
||||
free(nets);
|
||||
return ret;
|
||||
fail:
|
||||
goto exit;
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
/* Search Patterns
|
||||
@@ -1061,7 +1044,7 @@ ncclResult_t ncclTopoCompute(ncclTopoSystem* system, struct ncclTopoGraph* graph
|
||||
graph->pattern == NCCL_TOPO_PATTERN_RING ? system->hostIdx % 2 : 0));
|
||||
int arch, vendor, model;
|
||||
NCCLCHECK(ncclTopoCpuType(system, &arch, &vendor, &model));
|
||||
if (graph->nChannels && arch == NCCL_TOPO_CPU_ARCH_X86 && vendor == NCCL_TOPO_CPU_VENDOR_AMD && model == NCCL_TOPO_CPU_TYPE_ROME) {
|
||||
if (graph->nChannels && arch == NCCL_TOPO_CPU_ARCH_X86 && vendor == NCCL_TOPO_CPU_VENDOR_AMD && model == NCCL_TOPO_CPU_MODEL_AMD_ROME) {
|
||||
system->type |= RCCL_TOPO_4P2H_ROME;
|
||||
}
|
||||
}
|
||||
@@ -1107,6 +1090,15 @@ ncclResult_t ncclTopoCompute(ncclTopoSystem* system, struct ncclTopoGraph* graph
|
||||
graph->minChannels = graph->maxChannels;
|
||||
}
|
||||
|
||||
int splitNvLink;
|
||||
NCCLCHECK(ncclTopoSplitNvLink(system, &splitNvLink));
|
||||
if (graph->pattern == NCCL_TOPO_PATTERN_RING && splitNvLink) {
|
||||
// We have two sockets with NVLink and a slower link in between (typically QPI).
|
||||
// Tree is likely going to work better but it needs at least 2 channels.
|
||||
// Since Tree needs to have the same number of channels as Ring, also force Ring to use 2 channels.
|
||||
if (graph->maxChannels >= 2 && graph->minChannels == 1) graph->minChannels = 2;
|
||||
}
|
||||
|
||||
struct ncclTopoGraph tmpGraph;
|
||||
memcpy(&tmpGraph, graph, sizeof(struct ncclTopoGraph));
|
||||
|
||||
|
||||
+84
-42
@@ -24,11 +24,11 @@
|
||||
|
||||
const char* topoNodeTypeStr[] = { "GPU", "PCI", "NVS", "CPU", "NIC", "NET" };
|
||||
#if defined(__HIP_PLATFORM_AMD__) || defined(__HIPCC__)
|
||||
const char* topoLinkTypeStr[] = { "LOC", "XGMI", "", "PCI", "", "", "", "SYS", "NET" };
|
||||
const char* topoPathTypeStr[] = { "LOC", "XGMI", "NVB", "PIX", "PXB", "PXN", "PHB", "SYS", "DIS" };
|
||||
const char* topoLinkTypeStr[] = { "LOC", "XGMI", "", "C2C", "PCI", "", "", "", "SYS", "NET" };
|
||||
const char* topoPathTypeStr[] = { "LOC", "XGMI", "NVB", "C2C", "PIX", "PXB", "PXN", "PHB", "SYS", "DIS" };
|
||||
#else
|
||||
const char* topoLinkTypeStr[] = { "LOC", "NVL", "", "PCI", "", "", "", "SYS", "NET" };
|
||||
const char* topoPathTypeStr[] = { "LOC", "NVL", "NVB", "PIX", "PXB", "PXN", "PHB", "SYS", "NET", "DIS" };
|
||||
const char* topoLinkTypeStr[] = { "LOC", "NVL", "", "C2C", "PCI", "", "", "", "SYS", "NET" };
|
||||
const char* topoPathTypeStr[] = { "LOC", "NVL", "NVB", "C2C", "PIX", "PXB", "PXN", "PHB", "SYS", "NET", "DIS" };
|
||||
#endif
|
||||
|
||||
/******************************************************************/
|
||||
@@ -51,7 +51,7 @@ ncclResult_t pciPathToInt64(char* path, int offset, int minOffset, int64_t* id)
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
static ncclResult_t findLocalCpu(struct ncclTopoNode* node, struct ncclTopoNode** cpu) {
|
||||
static ncclResult_t findLocalCpu(struct ncclTopoNode* node, struct ncclTopoNode** cpu, struct ncclTopoNode* from) {
|
||||
*cpu = NULL;
|
||||
if (node->type == CPU) {
|
||||
*cpu = node;
|
||||
@@ -60,9 +60,10 @@ static ncclResult_t findLocalCpu(struct ncclTopoNode* node, struct ncclTopoNode*
|
||||
for (int l=0; l<node->nlinks; l++) {
|
||||
// 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));
|
||||
&& node->links[l].remNode != from
|
||||
&& (node->links[l].remNode->type == PCI
|
||||
|| node->links[l].remNode->type == CPU)) {
|
||||
NCCLCHECK(findLocalCpu(node->links[l].remNode, cpu, node));
|
||||
}
|
||||
if (*cpu != NULL) return ncclSuccess;
|
||||
}
|
||||
@@ -83,13 +84,17 @@ static ncclResult_t ncclTopoGetInterCpuBw(struct ncclTopoNode* cpu, float* bw) {
|
||||
return ncclSuccess;
|
||||
}
|
||||
if (cpu->cpu.arch == NCCL_TOPO_CPU_ARCH_X86 && cpu->cpu.vendor == NCCL_TOPO_CPU_VENDOR_INTEL) {
|
||||
*bw = cpu->cpu.model == NCCL_TOPO_CPU_TYPE_SKL ? SKL_QPI_BW : QPI_BW;
|
||||
*bw =
|
||||
cpu->cpu.model == NCCL_TOPO_CPU_MODEL_INTEL_ERP ? ERP_QPI_BW :
|
||||
cpu->cpu.model == NCCL_TOPO_CPU_MODEL_INTEL_SRP ? SRP_QPI_BW :
|
||||
cpu->cpu.model == NCCL_TOPO_CPU_MODEL_INTEL_SKL ? SKL_QPI_BW :
|
||||
BDW_QPI_BW;
|
||||
}
|
||||
if (cpu->cpu.arch == NCCL_TOPO_CPU_ARCH_X86 && cpu->cpu.vendor == NCCL_TOPO_CPU_VENDOR_AMD) {
|
||||
*bw = AMD_BW;
|
||||
}
|
||||
if (cpu->cpu.arch == NCCL_TOPO_CPU_ARCH_X86 && cpu->cpu.vendor == NCCL_TOPO_CPU_VENDOR_ZHAOXIN) {
|
||||
*bw = cpu->cpu.model == NCCL_TOPO_CPU_TYPE_YONGFENG ? YONGFENG_ZPI_BW : ZPI_BW;
|
||||
*bw = cpu->cpu.model == NCCL_TOPO_CPU_MODEL_YONGFENG ? YONGFENG_ZPI_BW : ZPI_BW;
|
||||
}
|
||||
return ncclSuccess;
|
||||
}
|
||||
@@ -534,19 +539,23 @@ ncclResult_t ncclTopoAddCpu(struct ncclXmlNode* xmlCpu, struct ncclTopoSystem* s
|
||||
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;
|
||||
cpu->cpu.model =
|
||||
(familyId == 6 && modelId >= 0xCF) ? NCCL_TOPO_CPU_MODEL_INTEL_ERP :
|
||||
(familyId == 6 && modelId >= 0x8F) ? NCCL_TOPO_CPU_MODEL_INTEL_SRP :
|
||||
(familyId == 6 && modelId >= 0x55) ? NCCL_TOPO_CPU_MODEL_INTEL_SKL :
|
||||
NCCL_TOPO_CPU_MODEL_INTEL_BDW;
|
||||
} 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;
|
||||
if (familyId == 7 && modelId == 0x5B) cpu->cpu.model = NCCL_TOPO_CPU_MODEL_YONGFENG;
|
||||
}
|
||||
if (cpu->cpu.vendor == NCCL_TOPO_CPU_VENDOR_AMD) {
|
||||
int familyId, modelId;
|
||||
NCCLCHECK(xmlGetAttrInt(xmlCpu, "familyid", &familyId));
|
||||
NCCLCHECK(xmlGetAttrInt(xmlCpu, "modelid", &modelId));
|
||||
// Treat "Milan" also as "Rome"
|
||||
cpu->cpu.model = ((familyId == 143 && modelId >= 49) || familyId == 175) ? NCCL_TOPO_CPU_TYPE_ROME : NCCL_TOPO_CPU_TYPE_ZEN;
|
||||
cpu->cpu.model = ((familyId == 143 && modelId >= 49) || familyId == 175) ? NCCL_TOPO_CPU_MODEL_AMD_ROME : NCCL_TOPO_CPU_MODEL_AMD_ZEN;
|
||||
}
|
||||
}
|
||||
for (int s=0; s<xmlCpu->nSubs; s++) {
|
||||
@@ -595,7 +604,7 @@ ncclResult_t ncclTopoAddXGMI(struct ncclXmlNode* node, struct ncclTopoSystem* sy
|
||||
NCCLCHECK(ncclTopoGetNode(system, &remote, GPU, busId));
|
||||
} else if (targetType == CPU) {
|
||||
// NVL connection to the local CPU
|
||||
NCCLCHECK(findLocalCpu(gpu, &remote));
|
||||
NCCLCHECK(findLocalCpu(gpu, &remote, NULL));
|
||||
} else {
|
||||
if (system->nodes[NVS].count == 0) {
|
||||
NCCLCHECK(ncclTopoCreateNode(system, &remote, NVS, 0));
|
||||
@@ -647,7 +656,7 @@ ncclResult_t ncclTopoAddNvLinks(struct ncclXmlNode* node, struct ncclTopoSystem*
|
||||
NCCLCHECK(ncclTopoGetNode(system, &remote, GPU, NCCL_TOPO_ID(systemId, busId)));
|
||||
} else if (targetType == CPU) {
|
||||
// NVL connection to the local CPU
|
||||
NCCLCHECK(findLocalCpu(gpu, &remote));
|
||||
NCCLCHECK(findLocalCpu(gpu, &remote, NULL));
|
||||
} else {
|
||||
if (system->nodes[NVS].count == 0) {
|
||||
NCCLCHECK(ncclTopoCreateNode(system, &remote, NVS, 0));
|
||||
@@ -725,10 +734,10 @@ ncclResult_t ncclTopoAddC2c(struct ncclXmlNode* node, struct ncclTopoSystem* sys
|
||||
NCCLCHECK(xmlGetAttrInt(node, "bw", &bw));
|
||||
double c2cBw = (bw*count)/1000.0;
|
||||
struct ncclTopoNode* cpu = NULL;
|
||||
NCCLCHECK(findLocalCpu(gpu, &cpu));
|
||||
NCCLCHECK(findLocalCpu(gpu, &cpu, NULL));
|
||||
if (cpu == NULL) return ncclSuccess;
|
||||
NCCLCHECK(ncclTopoConnectNodes(gpu, cpu, LINK_NVL, c2cBw));
|
||||
NCCLCHECK(ncclTopoConnectNodes(cpu, gpu, LINK_NVL, c2cBw));
|
||||
NCCLCHECK(ncclTopoConnectNodes(gpu, cpu, LINK_C2C, c2cBw));
|
||||
NCCLCHECK(ncclTopoConnectNodes(cpu, gpu, LINK_C2C, c2cBw));
|
||||
} else {
|
||||
if (strcmp(node->name, "cpu") == 0) {
|
||||
NCCLCHECK(ncclGetSystemId(system, node, &systemId));
|
||||
@@ -1048,26 +1057,31 @@ struct ncclXmlNode** physNetNodes, struct ncclXmlNode** netNode, ncclResult_t (*
|
||||
// Trigger the merge, then get the new device's properties
|
||||
int vDevIndex = 0;
|
||||
ncclResult_t ret = makeVDevice(&vDevIndex, vProps);
|
||||
if (ret == ncclInvalidUsage) {
|
||||
WARN("TOPO/NET : Tried merging multiple devices together and failed. Try setting NCCL_NET_MERGE_LEVEL=LOC");
|
||||
NCCLCHECK(ret);
|
||||
if (ret != ncclSuccess) {
|
||||
INFO(NCCL_GRAPH|NCCL_INIT|NCCL_NET, "TOPO/NET : Tried merging multiple devices together and failed. vProps={ndevs=%d, devs=[%d %d %d %d]}. Set NCCL_NET_MERGE_LEVEL=LOC to disable NIC fusion.",
|
||||
vProps->ndevs, vProps->devs[0], vProps->devs[1], vProps->devs[2], vProps->devs[3]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
INFO(NCCL_GRAPH, "TOPO/NET : Made vNic %d", vDevIndex);
|
||||
return ncclSuccess;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoForceMerge(ncclComm_t comm, struct ncclXml* xml, char* str, int* placedDevs, ncclNetProperties_t* propsList, struct ncclXmlNode** physNetNodes, int nPhysDevs, ncclResult_t (*makeVDevice)(int*, ncclNetVDeviceProps_t*)) {
|
||||
ncclResult_t ncclTopoForceMerge(ncclComm_t comm, struct ncclXml* xml, const char* str, int* placedDevs, ncclNetProperties_t* propsList, struct ncclXmlNode** physNetNodes, int nPhysDevs, ncclResult_t (*makeVDevice)(int*, ncclNetVDeviceProps_t*)) {
|
||||
ncclResult_t ret = ncclSuccess;
|
||||
INFO(NCCL_ENV|NCCL_NET, "TOPO/NET : Force-fusing NICs using NCCL_NET_FORCE_MERGE=%s", str);
|
||||
char* ncStr;
|
||||
NCCLCHECK(ncclCalloc(&ncStr, strlen(str)+1));
|
||||
strcpy(ncStr, str);
|
||||
char* semi_token;
|
||||
char* semi = strtok_r(str, ";", &semi_token);
|
||||
char* semi = strtok_r(ncStr, ";", &semi_token);
|
||||
while (semi) {
|
||||
TRACE(NCCL_NET, "Fusing %s", semi);
|
||||
struct netIf userIfs[NCCL_NET_MAX_DEVS_PER_NIC];
|
||||
int nUserIfs = parseStringList(semi, userIfs, NCCL_NET_MAX_DEVS_PER_NIC);
|
||||
if (nUserIfs == 0) {
|
||||
INFO(NCCL_NET, "NET/IB : Invalid NCCL_NET_FORCE_MERGE specified %s. Couldn't parse substring %s. Please provide a semicolon-delimited list of comma-delimited NIC groups.",
|
||||
str, semi);
|
||||
ncStr, semi);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1081,26 +1095,37 @@ ncclResult_t ncclTopoForceMerge(ncclComm_t comm, struct ncclXml* xml, char* str,
|
||||
if (vProps.ndevs != nUserIfs) {
|
||||
WARN("TOPO/NET : Only matched %d devices, %d requested from %s",
|
||||
vProps.ndevs, nUserIfs, semi);
|
||||
return ncclInvalidUsage;
|
||||
ret = ncclInvalidUsage;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (vProps.ndevs > NCCL_NET_MAX_DEVS_PER_NIC) {
|
||||
WARN("Specified fused NIC %s which has too many devices (%d). Max %d", semi, vProps.ndevs, NCCL_NET_MAX_DEVS_PER_NIC);
|
||||
return ncclInvalidUsage;
|
||||
ret = ncclInvalidUsage;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
struct ncclXmlNode* netNode;
|
||||
NCCLCHECK(ncclTopoMakeVnic(comm, xml, &vProps, physNetNodes, &netNode, makeVDevice));
|
||||
|
||||
// Only set that a device is "placed" after successfully making a vNic (it's possible to exit before this)
|
||||
for (int i = 0; i < vProps.ndevs; i++) {
|
||||
placedDevs[vProps.devs[i]] = 1;
|
||||
ret = ncclTopoMakeVnic(comm, xml, &vProps, physNetNodes, &netNode, makeVDevice);
|
||||
if (ret == ncclSuccess) {
|
||||
// Only set that a device is "placed" after successfully making a vNic (it's possible to exit before this)
|
||||
for (int i = 0; i < vProps.ndevs; i++) {
|
||||
placedDevs[vProps.devs[i]] = 1;
|
||||
}
|
||||
} else {
|
||||
WARN("TOPO/NET : Could not force merge NICs %s. Please specify a valid NCCL_NET_FORCE_MERGE string.", semi);
|
||||
ret = ncclInvalidUsage;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
semi = strtok_r(NULL, ";", &semi_token);;
|
||||
}
|
||||
|
||||
return ncclSuccess;
|
||||
exit:
|
||||
free(ncStr);
|
||||
return ret;
|
||||
fail:
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ncclResult_t ncclTopoAutoMerge(ncclComm_t comm, struct ncclXml* xml, int mergeLevel, int* placedDevs, ncclNetProperties_t* propsList, struct ncclXmlNode** physNetNodes, int nPhysDevs, ncclResult_t (*makeVDevice)(int*, ncclNetVDeviceProps_t*)) {
|
||||
@@ -1148,7 +1173,24 @@ ncclResult_t ncclTopoAutoMerge(ncclComm_t comm, struct ncclXml* xml, int mergeLe
|
||||
}
|
||||
|
||||
struct ncclXmlNode* netNode;
|
||||
NCCLCHECKGOTO(ncclTopoMakeVnic(comm, xml, &vProps, physNetNodes, &netNode, makeVDevice), res, out);
|
||||
ncclResult_t ret = ncclTopoMakeVnic(comm, xml, &vProps, physNetNodes, &netNode, makeVDevice);
|
||||
|
||||
// Merging failed.
|
||||
// Mark all as unplaced and increase their distance to disconnected (PATH_DIS)
|
||||
// Set i to 0 to restart the automatic merging process and ensure all are placed
|
||||
if (ret != ncclSuccess) {
|
||||
INFO(NCCL_GRAPH|NCCL_INIT|NCCL_NET, "Marking physical devices as unplaced, increasing distance and restarting search.");
|
||||
placedDevs[i] = 0;
|
||||
TRACE(NCCL_GRAPH, "Setting dev %d as unplaced, keeping distance -> self as PATH_LOC", i);
|
||||
for (int k = 1; k < vProps.ndevs; k++) {
|
||||
int dev = vProps.devs[k];
|
||||
placedDevs[dev] = 0;
|
||||
paths[i*nPhysDevs + dev] = PATH_DIS;
|
||||
paths[dev*nPhysDevs + i] = PATH_DIS;
|
||||
TRACE(NCCL_GRAPH, "Setting dev %d as unplaced, setting distance -> %d as PATH_DIS", dev, i);
|
||||
}
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1212,16 +1254,16 @@ ncclResult_t ncclTopoMakeVNics(ncclComm_t comm, struct ncclXml* xml, ncclResult_
|
||||
// By default, don't merge any devices
|
||||
int mergeLevel;
|
||||
mergeLevel = PATH_PORT;
|
||||
char* mergeLevelEnv;
|
||||
mergeLevelEnv = getenv("NCCL_NET_MERGE_LEVEL");
|
||||
if (mergeLevelEnv) kvConvertToInt(mergeLevelEnv, &mergeLevel, nicPathKvList);
|
||||
char* forceMerge;
|
||||
forceMerge = getenv("NCCL_NET_FORCE_MERGE");
|
||||
NCCLCHECK(ncclCalloc(&placedDevs, physicalDevs));
|
||||
memset(placedDevs, 0, sizeof(int)*physicalDevs);
|
||||
{ // Avoids warnings related to jumping to "out"
|
||||
const char* mergeLevelEnv = ncclGetEnv("NCCL_NET_MERGE_LEVEL");
|
||||
if (mergeLevelEnv) kvConvertToInt(mergeLevelEnv, &mergeLevel, nicPathKvList);
|
||||
const char* forceMerge = ncclGetEnv("NCCL_NET_FORCE_MERGE");
|
||||
NCCLCHECK(ncclCalloc(&placedDevs, physicalDevs));
|
||||
memset(placedDevs, 0, sizeof(int)*physicalDevs);
|
||||
|
||||
if (forceMerge) {
|
||||
NCCLCHECKGOTO(ncclTopoForceMerge(comm, xml, forceMerge, placedDevs, props, physNetNodes, physicalDevs, makeVDevice), res, out);
|
||||
if (forceMerge) {
|
||||
NCCLCHECKGOTO(ncclTopoForceMerge(comm, xml, forceMerge, placedDevs, props, physNetNodes, physicalDevs, makeVDevice), res, out);
|
||||
}
|
||||
}
|
||||
NCCLCHECKGOTO(ncclTopoAutoMerge(comm, xml, mergeLevel, placedDevs, props, physNetNodes, physicalDevs, makeVDevice), res, out);
|
||||
|
||||
|
||||
+21
-17
@@ -21,9 +21,11 @@
|
||||
#define SM86_NVLINK_BW 12.0
|
||||
#define SM100_NVLINK_BW 40.0
|
||||
#define PCI_BW 12.0 // PCI Gen3 x16
|
||||
#define QPI_BW 6.0
|
||||
#define AMD_BW 16.0
|
||||
#define BDW_QPI_BW 6.0
|
||||
#define SKL_QPI_BW 10.0
|
||||
#define SRP_QPI_BW 22.0
|
||||
#define ERP_QPI_BW 40.0
|
||||
#define ZPI_BW 6.0
|
||||
#define YONGFENG_ZPI_BW 9.0
|
||||
#define P9_BW 32.0
|
||||
@@ -51,12 +53,13 @@ extern const char* topoNodeTypeStr[];
|
||||
#define LINK_LOC 0
|
||||
#define LINK_NVL 1
|
||||
// Skipping 2 for PATH_NVB
|
||||
#define LINK_PCI 3
|
||||
// Skipping 4 for PATH_PXB
|
||||
// Skipping 5 for PATH_PXN
|
||||
// Skipping 6 for PATH_PHB
|
||||
#define LINK_SYS 7
|
||||
#define LINK_NET 8
|
||||
#define LINK_C2C 3
|
||||
#define LINK_PCI 4
|
||||
// Skipping 5 for PATH_PXB
|
||||
// Skipping 6 for PATH_PXN
|
||||
// Skipping 7 for PATH_PHB
|
||||
#define LINK_SYS 8
|
||||
#define LINK_NET 9
|
||||
extern const char* topoLinkTypeStr[];
|
||||
|
||||
// Local (myself)
|
||||
@@ -68,29 +71,32 @@ extern const char* topoLinkTypeStr[];
|
||||
// Connection through NVLink using an intermediate GPU
|
||||
#define PATH_NVB 2
|
||||
|
||||
// Connection through C2C
|
||||
#define PATH_C2C 3
|
||||
|
||||
// Connection traversing at most a single PCIe bridge
|
||||
#define PATH_PIX 3
|
||||
#define PATH_PIX 4
|
||||
|
||||
// Connection traversing multiple PCIe bridges (without traversing the PCIe Host Bridge)
|
||||
#define PATH_PXB 4
|
||||
#define PATH_PXB 5
|
||||
|
||||
// Connection between a GPU and a NIC using an intermediate GPU. Used to enable rail-local, aggregated network send/recv operations.
|
||||
#define PATH_PXN 5
|
||||
#define PATH_PXN 6
|
||||
|
||||
// Connection traversing PCIe as well as a PCIe Host Bridge (typically the CPU)
|
||||
#define PATH_PHB 6
|
||||
#define PATH_PHB 7
|
||||
|
||||
// Connection traversing PCIe as well as the SMP interconnect between NUMA nodes (e.g., QPI/UPI)
|
||||
#define PATH_SYS 7
|
||||
#define PATH_SYS 8
|
||||
|
||||
// Connection through the network
|
||||
#define PATH_NET 8
|
||||
#define PATH_NET 9
|
||||
|
||||
// New type of path which should precede PATH_PIX
|
||||
#define PATH_PORT PATH_NVL
|
||||
|
||||
// Disconnected
|
||||
#define PATH_DIS 9
|
||||
#define PATH_DIS 10
|
||||
extern const char* topoPathTypeStr[];
|
||||
|
||||
struct ncclTopoNode;
|
||||
@@ -110,9 +116,6 @@ struct ncclTopoLinkList {
|
||||
int type;
|
||||
};
|
||||
|
||||
#define NCCL_TOPO_CPU_INTEL_BDW 1
|
||||
#define NCCL_TOPO_CPU_INTEL_SKL 2
|
||||
|
||||
#define NCCL_TOPO_UNDEF (-1)
|
||||
|
||||
#define NCCL_TOPO_ID_LOCAL_ID_MASK 0x00ffffffffffffff
|
||||
@@ -212,6 +215,7 @@ ncclResult_t ncclTopoLoadSystem(const char* xmlTopoFile, struct ncclTopoSystem*
|
||||
ncclResult_t ncclTopoGetIntermediateRank(struct ncclTopoSystem* system, int rank, int64_t netId, int* intermediateRank);
|
||||
ncclResult_t ncclTopoGetGpuMinPath(struct ncclTopoSystem* system, int type, int* min);
|
||||
ncclResult_t ncclTopoGetGpuMaxPath(struct ncclTopoSystem* system, int type, int* max);
|
||||
ncclResult_t ncclTopoSplitNvLink(struct ncclTopoSystem* system, int* splitNvLink);
|
||||
|
||||
#define NCCL_TOPO_XML_MAX_NODES 8192
|
||||
#define NCCL_GRAPH_XML_MAX_NODES 8192
|
||||
|
||||
@@ -382,6 +382,7 @@ static const double perChMaxTreeBws[][3] = {
|
||||
NCCL_PARAM(PatEnable, "PAT_ENABLE", 2);
|
||||
static int ncclPatEnable(struct ncclComm* comm) {
|
||||
int patEnable = ncclParamPatEnable();
|
||||
if (comm->minCompCap < 60) return 0; // Need SM60 or higher for CUDA atomics
|
||||
if (patEnable != 2) return patEnable;
|
||||
if (comm->nNodes != comm->nRanks) return 0; // PAT only supports 1 GPU per node
|
||||
if (comm->netDeviceType != NCCL_NET_DEVICE_HOST) return 0; // PAT doesn't support net device offload
|
||||
@@ -486,7 +487,7 @@ ncclResult_t ncclTopoTuneModel(struct ncclComm* comm, int minCompCap, int maxCom
|
||||
if (a == NCCL_ALGO_TREE && coll == ncclFuncAllReduce) busBw = std::min(busBw*.92, graphs[a]->nChannels*perChMaxTreeBw);
|
||||
if (a == NCCL_ALGO_TREE && p == NCCL_PROTO_LL) busBw = std::min(busBw*1.0/3.8, llMaxBw);
|
||||
if (a == NCCL_ALGO_TREE && p == NCCL_PROTO_LL128) busBw = std::min(busBw * (nNodes == 1 ? 7.0/9.0 : 120.0/128.0), graphs[a]->nChannels*perChMaxTreeLL128Bw);
|
||||
if (a == NCCL_ALGO_TREE && graphs[a]->pattern == NCCL_TOPO_PATTERN_TREE) busBw *= .85;
|
||||
if (a == NCCL_ALGO_TREE && comm->maxTreePattern == NCCL_TOPO_PATTERN_TREE) busBw *= .85;
|
||||
if (a == NCCL_ALGO_PAT) busBw *= .75;
|
||||
if (a == NCCL_ALGO_COLLNET_DIRECT && p != NCCL_PROTO_SIMPLE) busBw = 0; // Not used
|
||||
if (a == NCCL_ALGO_COLLNET_CHAIN && p != NCCL_PROTO_SIMPLE) busBw = 0; // Not used
|
||||
|
||||
Ссылка в новой задаче
Block a user