集合通信
数据并行:all-reduce或reduce-scatter和all-gather
张量并行:all-reduce
流水并行:点对点p2p
序列并行:all-gather和reduce-scatter
专家并行:all-to-all
集合通信-MPI标准
进程间通信也是消息传递
最基本的消息传递包括send receive 等等
MPI系统的通信方式都是p2p
可以阻塞可以非阻塞
而open MPI中就有多个集合通信算法 比如all-reduce all-to-all
集合通信 算子
1.MPI_Bcast 是一种广播操作,它的作用是将一个进程(通常是根进程)的数据发送给通信组中的所有其他进程。
int MPI_Bcast(void *buffer, int count, MPI_Datatype datatype, int root, MPI_Comm comm)
buffer:需要广播的数据(根进程是发送缓冲区,其他进程是接收缓冲区)。
count:数据的数量。
datatype:数据类型(如 MPI_INT、MPI_FLOAT 等)。
root:根进程的编号(发送数据的进程)。
comm:通信域(通常是 MPI_COMM_WORLD)。
//Bcast Example
int data;
if (rank == 0) {
data = 100; // 根进程初始化数据
}
MPI_Bcast(&data, 1, MPI_INT, 0, MPI_COMM_WORLD);
// 现在所有进程的 data 都是 100
2.MPI_Scatter 是一种散射操作,它的作用是将根进程的数据分成若干份,分别发送给通信组中的每个进程。
int MPI_Scatter(void *sendbuf, int sendcount, MPI_Datatype sendtype,
void *recvbuf, int recvcount, MPI_Datatype recvtype,
int root, MPI_Comm comm)
sendbuf:根进程的发送缓冲区(包含所有数据)。
sendcount:发送给每个进程的数据数量。
sendtype:发送数据类型。
recvbuf:接收缓冲区(每个进程接收到的数据)。
recvcount:每个进程接收的数据数量。
recvtype:接收数据类型。
root:根进程的编号。
comm:通信域。
//Scatter Example
int send_data[4] = {10, 20, 30, 40}; // 根进程的数据
int recv_data; // 每个进程接收的数据
MPI_Scatter(send_data, 1, MPI_INT, &recv_data, 1, MPI_INT, 0, MPI_COMM_WORLD);
// 进程 0 收到 10,进程 1 收到 20,进程 2 收到 30,进程 3 收到 40int send_data[4] = {10, 20, 30, 40}; // 根进程的数据
int recv_data; // 每个进程接收的数据
MPI_Scatter(send_data, 1, MPI_INT, &recv_data, 1, MPI_INT, 0, MPI_COMM_WORLD);
// 进程 0 收到 10,进程 1 收到 20,进程 2 收到 30,进程 3 收到 40
3.all-gather和all-reduce
AllGather:适合需要收集所有进程的原始数据,并将完整数据分发给每个进程的场景。
AllReduce:适合需要对所有进程的数据进行全局计算(如求和、求最大值等),并将结果分发给每个进程的场景。
4.RingAllreduce
基于环,每个参与的GPU或者进程只和他的两个邻居通信。
优点:减少通信延迟和网络拥塞
5.all-to-all与all-gather
上述都是基于open MPI的内容,那么NCCL的出现解决什么问题?
那就是因为MPI针对CPU设计 现在也有多GPU通信需求。
GPU 优化:NCCL 专为 GPU 设计,性能优于 MPI。
集合通信:NCCL 提供了高效的集合通信操作,特别适合深度学习。
框架集成:NCCL 被广泛集成到主流深度学习框架中,使用方便。
扩展性:NCCL 在多 GPU 和多节点场景下表现出色。
什么是NCCL?
专为NVIDIA GPU设计的通信库
优化了通过NVLink进行的GPU间通信
绕过传统CPU中转,而是GPU之间直接通信
CUDA的基层,CUDA程序中无缝使用NCCL
自适应拓扑:不管单节点多GPU还是多节点多GPU,自动检测
NCCL解决什么问题?
解决的是GPU间通信,它也支持广播、规约、全收集。
GPU间通信的方式
1.GPU共享内存
数据从GPU拷贝到CPU的主机内存,再从CPU的主机内存拷贝到另一块GPU。
高延迟 、额外性能开销/
2.GPU 直接p2p
同一个节点的GPU直接通信,而不通过CPU的主机内存,减少延迟。依赖于PCIe架构
3.NVLink
不通过PCIe,而是通过NVLink
NCCL架构总览
魔改的MPI架构,进程叫做rank
每个rank都有一个rank ID
rank的集合构成了一个“communicator”
也就是一些可以互相通信的进程。
一个rank可以分属于多个communicators
而且在不同的地方可能有不同的rank ID
NCCL架构-communicator
首先就是初始化吧:
ncclCommInitRank():初始化指定rank的communicator。
ncclCommInitAll():同时初始化所有rank的communicator。
initTransportsRank():检测可用GPU设备以及拓扑结构,计算最佳通信路径,建立连接。
ncclTopoFillGpu():建立包含GPU的XML树结构,可以设置环境变量NCCL_TOPO_DUMP_FILE来书输出XML文件,并通过该XML文件来查看机器的拓扑结构。
查看单机内拓扑结构
nvdia-smi topo -m
GPU拓扑结构-单机
NVLink + NVSwitch
也是可以通过
nvdia-smi topo -m
看看拓扑
现在GPU还是以PCIe连接到CPU为中心的互联体系中
PCIe Gen 5 x16 双向带宽才 128GB/s,落后于 NVLink
GPU拓扑结构-机间
InfiniBand/RoCE + Spine-Leaf
拓扑优化
NCCL 2.12 中引入的PXN拓扑优化 ,称为 PCI × NVLink,可以加速一倍的机间的数据传输效率。
具体简述:多机多卡的时候,可以数据包线移动到
NCCL算法Ring-Tree
Ring算法:
基本算法,环形、只能和相邻两个邻居进行数据交换。
特点:简洁;对等
缺点:如果是Ring的all-reduce,延迟比较高
Tree算法:
因为二叉树大约一半节点都是叶子节点
那么通过将叶子节点变换为非叶子节点,得到两棵二叉树
每个节点在其中好一棵二叉树是叶子节点
另一棵二叉树是非叶子节点
特点:延迟耕地,高效利用带宽和脱坡结构
通信时间预估公式
解释一下上述的内容:
latency:初始延迟:启动通信的开销和数据传输的初试时间。
受NVLink和PCIe的延迟影响
nBytes:需要传输的数据总量, 通常是模型参数的总大小
algo_bw:算法带宽,特定的拓扑下能达到的最大数据传输速率。
NCCL自动化选择机制
1.拓扑探测:NCCL 在初始化时探测硬件拓扑(如 GPU、NVLink、PCIe、InfiniBand 的连接关系)。使用 ncclTopoGetSystem() 获取系统的物理拓扑信息。
2.算法评估:对于每种通信算法(如 Ring AllReduce、Tree AllReduce),NCCL 计算其在不同拓扑下的预估通信时间。使用公式 time = latency + nBytes / algo_bw 计算每种算法的性能。
3.算法选择:时间最短算法作为最有算法。在 NVLink 全连接拓扑中,Ring AllReduce 通常是最优选择;而在跨节点通信中,Tree AllReduce 可能更优。
如何对不同算法做性能测试?
git clone git@github.com:NVIDIA/nccl-tests.git
NCCL_ALGO=Tree ./build/all_reduce_perf -b 1M -e 2048M -f 2 -g 8
NCCL_ALGO=Ring ./build/all_reduce_perf -b 1M -e 2048M -f 2 -g 8
上面可以用Tree和Ring算法得到 Avg bus bandwidth
而在机内,Ring算法带宽比Tree算法高一些。
CollNet算法
NCCL 2.6 引入的新算法
建立在SHArP(Scalable Hierarchical Aggregation and Reduction Protocol)基础之上的,
专为与InfiniBand(IB)网络配合使用而设计。
SHArP,也被称为NCCL Plugin或NCCL-RDMA-SHARP插件,是提升通信性能的关键工具。
核心优势在于其将计算任务卸载到网络交换机的能力。这种技术允许交换机直接参与到数据的聚合(Reduce)操作中,从而减少了GPU之间的直接通信需求。