集合通信科普:AllReduce 如何吃掉你的训练时间
训练扩卡之后,性能却不是线性增长——八卡的效率可能只有七成,六十四卡可能只剩一半。损耗的大头往往不在计算,而在集合通信:每一步训练结束后,所有卡都要把梯度"对齐"。这个动作叫 AllReduce,它是分布式训练中最基础的集体操作,也是最容易被低估的时间黑洞。
AllReduce 到底在做什么
数据并行是最常见的训练方式:每张卡持有完整模型副本,各处理一小批数据,算出的梯度彼此不同。参数更新前必须让所有卡上的梯度变成同一个平均值——这个"各自有个数,最后大家拿到同样的和"的操作就是 AllReduce。
工程实现上它通常拆成两步:ReduceScatter(各卡各拿总和的一部分)加 AllGather(再把各自的那份广播给所有人)。关键在于算法选择:环形(Ring)算法让每张卡只与相邻两卡通信,数据像接力一样绕环一周。可以证明,环形的总传输量对每张卡约为 2 乘以 N 减 1 再除以 N 乘以数据量——数据量是梯度总大小,N 是卡数。当 N 较大时,这近似等于"两倍梯度大小",与卡数基本无关,因此被称为带宽最优算法。树形算法时延特性更好,适合小消息;实际系统中则常按网络拓扑做分层:节点内走 NVLink 高带宽环,节点间走 InfiniBand,两层各用各的最优算法。
一笔具体的时延账
以 7B 模型混合精度训练为例,梯度约 14GB。八卡节点内 AllReduce,每卡收发总量约 2 乘 8 分之 7 乘 14GB,约 24.5GB;H100 节点内 NVLink 有效带宽按数百 GB/s 计,耗时约几十毫秒,相对于每秒多次的迭代尚可接受。但同一笔账放到跨机场景就难看了:若节点间只有 400Gbps 一条链路(约合 50GB/s),同步 14GB 梯度的耗时将以数百毫秒计,一步训练被通信拖成两倍长。
这正是大规模训练的共性困境:计算随卡数线性扩,而网络带宽扩不过卡数。节点内外带宽差出一个数量级,通信分层与流量调度因此成为大规模集群设计的核心功课。
压缩通信的三板斧
| 手段 | 原理 | 典型收益 |
|---|---|---|
| 梯度分桶与计算重叠 | 反向传播中先算完的层先通信,与后续计算并行 | 掩盖大部分节点内通信 |
| 状态分片(ZeRO 类) | 把优化器状态与梯度切分到各卡,减少冗余同步 | 通信量与显存同时下降 |
| 精度与压缩 | 低精度梯度、稀疏化或低秩压缩后再同步 | 通信量成倍下降,需评估收敛 |
三板斧之外还有架构层面的选择:增大每卡 batch 提高计算通信比、流水线并行减少同步频率、以及 MoE 场景改用 all-to-all(那是另一个故事)。它们的共同目标是同一个——让每字节数据通信期间,算力单元别闲着。
实操建议
拿到集群先别急着堆模型:跑一遍基准的 AllReduce 带宽测试,确认节点内与节点间的实测有效带宽与规格的差距;训练中用 profiling 工具看通信时间占比,超过一成就说明优化空间在通信侧而不是算力侧。买卡时也别只比 FLOPS——节点内互连带宽与网络规格,在规模化训练里的重要性不亚于算力本身。