分布式集合通信原语详解
引言
在分布式系统和并行计算中,多个节点(进程、线程、GPU)协同完成任务时,必然涉及"一对多""多对一""多对多"的数据交换。这些操作被抽象为集合通信原语(Collective Communication Primitives),是 MPI、NCCL、Gloo、Horovod 等通信库的基石,也是分布式机器学习与科学计算的核心。
理解每种原语的原理、使用场景、通信量,能帮助我们在系统设计时:
- 选择合适的算法实现(Ring / Tree / Butterfly / Rabenseifner 等);
- 估算网络瓶颈与扩展性;
- 排查分布式训练中的性能问题。
本文系统梳理经典原语:Broadcast、Scatter、Gather、All-Gather、Reduce、All-Reduce、Reduce-Scatter、All-to-All、Scan 和 Barrier。
符号约定
为方便后文比较,先统一符号:
| 符号 | 含义 |
|---|---|
p |
参与节点数(ranks) |
n |
每个节点持有的数据量(字节,本文取"输入时每节点数据量"为基准) |
α |
消息启动延迟(latency) |
β |
单位数据传输时间(bandwidth inverse) |
通信成本常用 Hockney 模型 :发送大小为 m 的消息耗时 ≈ α + m·β。
下文"通信量"同时给出:
- 每节点发送字节数(决定最慢节点的瓶颈)
- 全网总流量(决定网络压力)
- α-β 时间估算(粗略但能反映扩展性)
一、Broadcast(广播)
原理
根节点(root)将同一份长度为 n 的数据发送给其余 p-1 个节点。结束后,每个节点都持有这份数据。
常见算法:
朴素(朴素线性)
root → node1 → node2 → ... → node(p-1)
共 p-1 次串行发送,root 串行瓶颈严重
递归倍增 / Binomial Tree
root
/ \
n1 n2
/ \ / \
n3 n4 n5 n6
每一层并发广播,共 ⌈log₂ p⌉ 步
每个节点最多接收并转发 n 一次
Binomial Tree 是 MPI/NCCL 中最常见的实现。
使用场景
- 模型参数、超参数分发到所有 worker;
- 初始化阶段把同一份配置复制到每个 rank;
- 一致性状态机中的全局状态分发。
通信量
| 指标 | 朴素 | Binomial Tree |
|---|---|---|
| 每节点发送 | root: (p-1)·n,其他: 0 |
至多 n |
| 全网总流量 | (p-1)·n |
(p-1)·n |
| α-β 时间 | (p-1)·α + (p-1)·n·β |
⌈log₂ p⌉·(α + n·β) |
关键点 :Tree 算法的并行度高,总流量相同但延迟随 log p 增长,适合小消息;朴素串行实现适合极大消息但节点很少的场景。
二、Scatter(分散)
原理
根节点持有长度为 p·n 的数据,逻辑切成 p 份;通信结束后,节点 i 收到第 i 份(长度为 n)。
与 Broadcast 的区别:Broadcast 每节点得到完整数据;Scatter 每节点得到不同片段。
root
[a0|a1|a2|a3] ← p·n 总长
↓ ↓ ↓ ↓
n0 n1 n2 n3 ← 每节点拿到自己那份
使用场景
- 大矩阵按行分片到不同 worker(矩阵乘法的前置步骤);
- 数据并行中的样本划分;
- 任务分发:每 worker 领到不同的子任务。
通信量
| 指标 | 值 |
|---|---|
| 每节点 | root 发送 (p-1)·n;其他节点接收 n |
| 全网总流量 | (p-1)·n |
| α-β 时间 | ⌈log₂ p⌉·(α + n·β)(树形实现) |
三、Gather(收集)
原理
每个节点持有长度 n 的数据,根节点最终得到按 rank 顺序拼接的 p·n 数据。
是 Scatter 的逆操作。
使用场景
- 收集所有 worker 的局部结果到 master 做汇总;
- 把分散计算的指标收回到中心节点;
- 与 Scatter 配合做矩阵分块计算。
通信量
| 指标 | 值 |
|---|---|
| 每节点 | 非 root 发送 n;root 接收 (p-1)·n |
| 全网总流量 | (p-1)·n |
| α-β 时间 | ⌈log₂ p⌉·(α + n·β) |
四、All-Gather(全收集)
原理
每个节点持有长度 n 的数据;通信结束后,每个节点都拥有完整拼接的 p·n 数据。
常见算法:
Ring All-Gather(p=4 示意)
step 0: [a][b][c][d] ← 初始状态
step 1: [a|b][b|c][c|d][d|a] ← 每节点收下一跳数据
step 2: [a|b|c][b|c|d][c|d|a][d|a|b]
step 3: [a|b|c|d][b|c|d|a][c|d|a|b][d|a|b|c]
共 p-1=3 步
Bruck All-Gather(对数步)
step 1: 每节点发给 (i+1) mod p
step 2: 每节点发给 (i+2) mod p ← 携带已收数据
...
step k: 每节点发给 (i+2^(k-1)) mod p
共 ⌈log₂ p⌉ 步,但消息大小指数增长
使用场景
- 分布式训练中先 All-Gather 收集所有 worker 的局部张量,再做 Reduce;
- 各节点需要看到全局信息(如全局指标汇总);
- 矩阵分块计算后做拼合(gemm 之后需要把分片拼回)。
通信量
| 指标 | Ring | Bruck |
|---|---|---|
| 每节点 | (p-1)·n 发送 + (p-1)·n 接收 |
每步消息倍增,总和近似 (log p)·n·p |
| 全网总流量 | p·(p-1)·n |
p·(log p)·n·(p+1)/2 |
| α-β 时间 | (p-1)·α + (p-1)·n·β |
log p·α + (log p)·n·β·(p+1)/2 |
Ring 适合大消息,Bruck 适合小消息(步数少)。
五、Reduce(归约)
原理
每个节点持有长度 n 的数据,根节点应用结合性算子 (SUM、MAX、MIN、PROD、LOR、LAND、BXOR 等),最终得到长度 n 的归约结果。
算子必须满足结合律(最好还满足交换律),以便分布式执行。
使用场景
- 各节点计算局部梯度/损失,在 root 汇总为全局值;
- 全局统计量(所有节点的最大值、总和、均值);
- 把分布式计算结果汇总到一台机器落盘。
通信量
| 指标 | 值 |
|---|---|
| 每节点 | 非 root 发送 n;root 接收并计算 (p-1)·n |
| 全网总流量 | (p-1)·n |
| α-β 时间 | ⌈log₂ p⌉·(α + n·β)(树形 + 算子在树上合并) |
六、All-Reduce(全归约)⭐
原理
每个节点持有长度 n 的数据;通信结束后,每个节点都得到归约结果 (长度仍为 n)。
这是分布式深度学习训练里使用频率最高的原语------梯度同步本质上就是一次 All-Reduce。
常见算法:
朴素实现
Reduce 到 root + Broadcast,root 是瓶颈。
Ring All-Reduce ⭐
分两步:
第 1 步:Reduce-Scatter
把数据切成 p 段,每段沿环累加 p-1 步
每节点最终持有归约后的 1/p 片段
第 2 步:All-Gather
每节点把它持有的 1/p 片段广播给其他节点
经过 p-1 步后每节点收集全
Ring All-Reduce(p=4 示意,"S"代表 Reduce-Scatter,"G"代表 All-Gather)
S1 S2 S3 G1 G2 G3
─┼────┼────┼────┼────┼────┼────► 时间
a b c d
↓ ↓ ↓ ↓
每个 chunk 经 3 次累加,每个节点最终持有 1 个 chunk 的归约结果
然后 All-Gather 把 1 个 chunk 广播给其他节点
核心优势 :每节点总通信量 2·(p-1)·n/p ≈ 2n,与节点数 p 几乎无关!
Tree All-Reduce
递归倍增树,每步 n 数据并行累加。
Double Binary Tree (Rabenseifner)
两棵二叉树组合,构造一个 log p 步算法,同时通信量低。
使用场景
- 分布式深度学习训练的梯度同步(PyTorch DDP、Horovod、DeepSpeed 的核心);
- 全局参数服务器中的聚合步骤;
- 所有节点需要一致的全局聚合值(如全局平均 reward、loss);
- 规约后做下一步决策(All-Reduce 出的 logits、概率)。
通信量
| 算法 | 每节点 | 全网总流量 | α-β 时间 |
|---|---|---|---|
| 朴素(Reduce+Bcast) | ~p·n |
~2p·n |
O(p·α + p·n·β) |
| Ring | 2·(p-1)·n/p ≈ 2n |
2·(p-1)·n |
2·(p-1)·α + 2n·β |
| Tree | ~2n·log p |
~2p·n·log p |
O(log p·(α + n·β)) |
| Double Binary Tree | ~2n |
~2p·n |
O(log p·α + n·β) |
关键洞察 :Ring All-Reduce 的每节点通信量几乎与 p 无关------这正是分布式训练能扩展到上千 GPU 的核心原因。NCCL 默认对大消息使用 Ring,对小消息使用 Tree。
七、Reduce-Scatter(归约-分散)
原理
每个节点持有长度 p·n 的数据(切分为 p 段);按段做归约后,节点 i 持有第 i 段(长度 n)。
可以理解为:Reduce 输出被 Scatter 到不同节点。
输入(每节点 p·n)
node0: [a0|a1|a2|a3]
node1: [b0|b1|b2|b3]
node2: [c0|c1|c2|c3]
node3: [d0|d1|d2|d3]
输出(每节点 n)
node0: a0⊕b0⊕c0⊕d0
node1: a1⊕b1⊕c1⊕d1
node2: a2⊕b2⊕c2⊕d2
node3: a3⊕b3⊕c3⊕d3
使用场景
- Ring All-Reduce 的第一步;
- 把归约结果直接分布到不同 worker 处理(不需要后续 All-Gather);
- 分布式矩阵运算中的部分结果归约。
通信量
| 指标 | Ring |
|---|---|
| 每节点 | (p-1)·n/p 发送 + (p-1)·n/p 接收 |
| 全网总流量 | (p-1)·n |
| α-β 时间 | (p-1)·α + (p-1)·n·β/p |
八、All-to-All(全局转置)
原理
每个节点持有 p 段数据(总长 p·n),第 i 段要发给节点 i;通信结束后,每个节点收到来自所有节点的各 1 段(拼成长度 p·n)。
本质上是 p × p 的转置操作。
All-to-All(p=4 示意)
输入:
node0: [a0→n0 | a1→n1 | a2→n2 | a3→n3]
node1: [b0→n0 | b1→n1 | b2→n2 | b3→n3]
node2: [c0→n0 | c1→n1 | c2→n2 | c3→n3]
node3: [d0→n0 | d1→n1 | d2→n2 | d3→n3]
输出:
node0: [a0→n0 | b0→n0 | c0→n0 | d0→n0]
node1: [a1→n1 | b1→n1 | c1→n1 | d1→n1]
...
使用场景
- 矩阵转置 :分布式
A^T; - MoE(混合专家)路由:把 token 分发给对应的专家 GPU;
- 推荐系统:跨分片的 embedding 查询结果聚合;
- 数据重分布:把数据按新的 rank 顺序重排。
通信量
| 指标 | Ring/Spread | Bruck |
|---|---|---|
| 每节点 | 发送 p·n(p 次 n 大小消息) |
每步数据倍增 |
| 全网总流量 | p²·n |
约 p²·n/2 |
| α-β 时间 | p·α + p·n·β(顺序发送) 或 log p·α + p·n·β(优化) |
log p·(α + n·β) |
All-to-All 是通信量最重的原语,p 增大时扩展性较差------这也是 MoE 推理中专家数量受限的重要原因。
九、Scan / Prefix Sum(前缀扫描)
原理
每个节点持有长度 n 的元素序列(或长度 n 的单元素),应用结合性算子后,节点 i 得到前 i+1(或前 i)个节点数据的归约结果。
Inclusive Scan(p=4,SUM)
输入: a, b, c, d
输出: a, a+b, a+b+c, a+b+c+d
Exclusive Scan
输出: 0, a, a+b, a+b+c
常见算法:
朴素:链式串行
node0 → node1 → node2 → node3
O(p) 步
Blelloch Scan(双调)
Up-sweep(归约树)+ Down-sweep(前缀分发)
O(log p) 步
使用场景
- 分布式前缀和:在多节点上做累计分布、排序、负载均衡;
- 稀疏矩阵 CSC 格式构建中的索引累加;
- 流式/流水线并行中的位置索引分配;
- 编译器中的数据流分析。
通信量
| 指标 | Blelloch |
|---|---|
| 每节点 | n·log p 量级 |
| α-β 时间 | log p·(α + n·β) |
十、Barrier(同步屏障)
原理
无数据移动。所有节点必须到达同一同步点后才能继续。常作为其他集合通信的前置隐式同步。
实现方式:
集中式:所有节点发心跳到某个协调者,协调者收到全部后通知大家
对等式:每对节点互相握手确认
dissemination barrier:log p 步对数算法
使用场景
- 阶段边界:确保所有节点完成当前阶段才进入下一阶段;
- 分布式训练中区分 iteration/epoch;
- 调试同步问题(人为插入 barrier 排查 race condition)。
通信量
- 数据移动:0
- α-β 时间:集中式
O(log p·α),对等式O(p·α)
十一、对比总结
| 原语 | 输入 / 节点 | 输出 / 节点 | 每节点通信量 | 主要场景 |
|---|---|---|---|---|
| Broadcast | root: n |
每节点: n |
Tree: n;朴素: (p-1)·n |
参数分发 |
| Scatter | root: p·n |
每节点: n |
(p-1)·n(root) |
数据切分 |
| Gather | 每节点: n |
root: p·n |
(p-1)·n(非 root 发) |
结果汇总 |
| All-Gather | 每节点: n |
每节点: p·n |
Ring: (p-1)·n |
全局可见 |
| Reduce | 每节点: n |
root: n |
(p-1)·n |
局部聚 |
| All-Reduce | 每节点: n |
每节点: n |
Ring: ~2n |
梯度同步 |
| Reduce-Scatter | 每节点: p·n |
每节点: n |
Ring: (p-1)·n/p |
All-Reduce 子步 |
| All-to-All | 每节点: p·n |
每节点: p·n |
p·n |
转置 / MoE |
| Scan | 每节点: n |
每节点: prefix | n·log p |
前缀和 |
| Barrier | --- | --- | 0 | 同步 |
十二、实现算法一览
| 算法 | 适用原语 | 每节点通信量 | α-β 时间 |
|---|---|---|---|
| Naive / Linear | 几乎所有 | O(p·n) |
O(p·α + p·n·β) |
| Ring | All-Gather, All-Reduce | O(n)(不随 p 增长) |
O(p·α + n·β) |
| Binomial Tree | Broadcast, Reduce | O(n·log p) |
O(log p·(α + n·β)) |
| Bruck | All-Gather | O(n·log p) |
O(log p·(α + n·β)) |
| Double Binary Tree (Rabenseifner) | All-Reduce | O(n) |
O(log p·α + n·β) |
| Blelloch | Scan | O(n·log p) |
O(log p·(α + n·β)) |
| Butterfly | All-Reduce | O(n·log p) |
O(log p·(α + n·β)) |
算法选择经验法则
- 大消息 + 多节点 → Ring:通信量恒定,带宽主导;
- 小消息 + 多节点 → Tree:步数少,延迟主导;
- 梯度同步几乎总是 Ring :通信量与
p无关,是分布式训练能扩展的根本; - 消息极小(如同步信号) → Bruck / Tree :避免 Ring 的
O(p)延迟; - GPU 集合通信 → 拓扑感知:NCCL 会根据 NVLink / PCIe / IB 自动选 Ring 或 Tree。
十三、工程实践要点
1. 让通信和计算流水线化
把集合通信与前向/反向计算 overlap 是性能关键:
- PyTorch DDP 的 gradient bucketing:梯度按 bucket 触发 All-Reduce,反向传播时提前触发,减少尾部等待;
- ZeRO / FSDP 的 预取 + 通信窗口;
- 手动实现
comm_stream与计算 stream 分离(CUDA)。
2. 选择合适的消息大小
- NCCL / MPI 的集合通信对小消息和大消息会切换算法;
- 把多个小张量合并成一次大集合通信(concat 后再 All-Reduce),避免反复启动;
- 但消息过大时会受网络拥塞影响,需要权衡。
3. 拓扑感知与 placement
- GPU 之间的 NVLink、PCIe 拓扑决定集合通信带宽;
NCCL_IB_HCA、NCCL_SOCKET_IFNAME等环境变量可调网络设备;- 节点内 NVLink + 节点间 IB 是常见异构拓扑,NCCL 会自动分层调度。
4. 异步与 group 语义
- NCCL 的
ncclGroupStart/End可以把多次集合通信合并为一个提交,避免频繁同步; - PyTorch DDP 用
process_group隔离不同并行策略(数据并行、专家并行); - MPI 的
MPI_Win+RMA在某些场景下比集合通信更高效。
5. 容错与重试
- 大规模训练中节点故障不可避免,需要 All-Reduce 容错实现(如 NCCL 的 fault-tolerant 模式、Elastic All-Reduce);
- 集合通信 + Checkpoint 是工业级系统的标配。
十四、常见框架对照
| 框架 | 适用 | 特点 |
|---|---|---|
| MPI (OpenMPI / MPICH) | HPC、跨语言 | 最完整、跨语言事实标准 |
| NCCL (NVIDIA) | NVIDIA GPU + NVLink/IB | 针对 GPU 优化,是 PyTorch DDP 的默认后端之一 |
| Gloo (Meta) | CPU/GPU 通用 | 简单可移植,PyTorch DDP 备选后端 |
| OneCCL (Intel) | Intel CPU/GPU | Intel 生态深度优化 |
| Horovod (Uber) | 深度学习 | 基于 MPI/NCCL,提供更易用 API |
| ACCL / HCCL | 自研 | 阿里 / 华为自研集合通信库,针对自研网卡优化 |
十五、小结
- Broadcast / Scatter / Gather:基础分发与收集;
- All-Gather / Reduce-Scatter:数据重分布的两种基本动作;
- All-Reduce ⭐:分布式训练的灵魂,几乎所有参数同步都靠它;
- All-to-All:通信量最重,但 MoE / 转置场景不可替代;
- Scan:并行算法里的关键工具;
- Barrier:纯同步原语,是其他集合通信的"伴生"。
理解这些原语的原理(看流程图)、通信量(看公式)、使用场景(看业务),就能在设计分布式系统时做出合理选择,在排查性能问题时迅速定位瓶颈。