一句话说,AllReduce(全归约)是一种集合通信(collective communication)操作:每个参与者手里都有一份同样形状的数据,先按元素做同一种运算(最常见是求和),再把结果发回给所有人。在分布式训练里,它干的事就是——把每张 GPU 算出来的梯度(gradient)加到一起、求个平均,再让每张卡都拿到这份"总梯度",这样所有卡更新出来的模型参数才会完全一致。
打个比方
十个会计各算一部分账。最笨的办法是每人把自己的表复印九份寄出去,再收九份回来自己加——通信量随人数平方增长,人一多网络就炸。聪明的办法是围成一个环:把每个人的表切成十段,先转一圈把各段汇总成完整的一段总和(这步叫 ReduceScatter),再转一圈把各自的完整分片发出去(这步叫 AllGather)。转两圈,人人手里都有完整的合计数,而每个人实际搬运的数据量只跟表的大小有关、几乎和人数无关。
这就是 Ring AllReduce(环形全归约)的核心,也是它成为分布式训练基石的原因。而 NCCL(NVIDIA Collective Communications Library)就是把这套原语在真实硬件上跑得最快的库:它探测机器里的连接方式(卡间高速互连、PCIe、机器间网络),挑选算法和协议,切数据、做流水线、让通信尽量和计算重叠。所以 AllReduce 是"要做什么",NCCL 是"怎么做到最快"。同类角色还有 MPI、Gloo 等。
别搞混的几个亲戚
| 操作 | 开始时谁有数据 | 结束后谁有结果 |
|---|---|---|
| Broadcast | 一个进程 | 所有人 |
| Reduce | 所有人 | 一个进程 |
| AllGather | 每人一份 | 所有人拿到所有人的 |
| ReduceScatter | 所有人 | 每人只拿一份分片 |
| AllReduce | 所有人 | 所有人拿到完整结果 |
AllReduce 约等于 ReduceScatter + AllGather,环形算法就是这么拆的。
为什么通信常常比计算还慢
计算发生在本地,通信要跨设备,走卡间互连和走机器间网络差着量级。其次,数据并行(data parallelism)训练里每个 step 都要把全部梯度 AllReduce 一次,梯度规模与参数量同阶,搬的数据和模型一样大。第三,集合通信自带同步屏障:所有卡都得等最慢的那张,一张慢卡或一次网络抖动会被放大成整队停顿。第四,通信通常在反向传播之后串行排队,算得再快也压不住这段等待。
对从业者意味着什么
看到"加卡不加速",第一反应就是去看通信时间占比,用 profiling 工具看 AllReduce 到底花了多久。常见缓解手段:梯度分桶、让反向传播边算边通信、用梯度累积降低通信频率、把通信最密集的并行组排在同一台机器内、选择合适的并行策略。NCCL 还提供若干可调参数,具体以官方文档为准。
对普通人来说,这就是"堆算力不等于线性变快"的根本原因之一:大模型训练的成本里,有相当一块花在把数字搬来搬去,而不是花在算上。
