范靥荒蜗static ncclResult_t connectRings(struct ncclComm* comm, int* ringRecv, int* ringSend, int* ringPrev, int* ringNext) {
......
for (int c=0; c
int* recv = ringRecv+c*comm->nNodes;
int* send = ringSend+c*comm->nNodes;
int* prev = ringPrev+c*comm->nRanks;
int* next = ringNext+c*comm->nRanks;
for (int n=0; n
int recvRank = recv[n];
int prevSendRank = send[(n-1+nNodes)%nNodes];
prev[recvRank] = prevSendRank;
int sendRank = send[n];
int nextRecvRank = recv[(n+1)%nNodes];
next[sendRank] = nextRecvRank;
}
}
......
}
按照当前实验环境,两个 node,每个 node 分别两块 GPU,上述 ring 连接逻辑对应日志如下:
rank 0
3为前驱,1为后继
Ring 00 : 3 -> 0 -> 1
rank 1
0为前驱,2为后继
Ring 00 : 0 -> 1 -> 2
rank 2
1为前驱,3为后继
Ring 00 : 1 -> 2 -> 3
rank 3
2为前驱,0为后继
2 -> 3 -> 0
NCCL 初始化的目的,可以引用如下示意图来概括:
初始化总结
基于现有测试环境,以上内容从源码层面分析了 NCCL 的初始化关键流程。包括:
网络插件初始化。主要涉及 InfiniBand,或者基于 TCP/IP 的 Socket 网络插件的选择。
NCCL 控制环的建立。主要涉及 Unique Id 的产生和用途,以及所有 rank 组成控制面环形拓扑的相关流程。
PCIe 设备发现。记录从 GPU/NIC 出发,直至归属 CPU 这条物理 PCIe 链路上有哪些 PCIe 设备,并生成从 GPU/NIC 至 CPU 链路的 PCIe XML 链路拓扑。
PCIe 设备间建图。PCIe 设备之间的物理链路关系是事实存在的,但是 NCCL 需要将这些链路关系建立到自身的数据结构中,目的是后续 channel 的计算服务。
PCIe 设备间最优可达路径计算。基于 PCIe 建图,NCCL 拥有 PCIe 设备间的连关系。在此基础上,NCCL 使用 BFS 算法,计算任意 PCIe 设备之间的最优可达路径,即跳数最少,带宽最大的可达路径。
设备内部基于 Ring,Tree 通信算法的 channel 计算。在路径带宽允许的情况下,NCCL 会尽最大可能计算出同一种通信算法的更多通信通道,从而尽可能榨干硬件带宽资源。
多机间 Ring,Tree 通道的连接。在多级多卡之间,协商出最终可通信的逻辑算法拓扑。
也分析了 NCCL tree 由 Double Binary Tree 到 Split Tree,再到 Balanced Tree 的演进和优化思想
截至本篇文章发布的时间点,可能是读者们能搜到的有关 NCCL 初始化介绍最全面和详细的文章了。