训练梯度同步与集合通信:从参数服务器到 Ring AllReduce
更新时间:2026-09-25。上一篇 NVLink 多 GPU 互联回答了「卡间链路有多快」,这一篇回答链路之上「卡与卡之间按什么规矩交换数据」。数据并行训练里,每张卡都能独立算出自己那份梯度,但参数更新要求所有卡的梯度一致——于是每个训练步都要做一次全局归约。这个动作叫集合通信(collective communication),它的数据量和带宽利用方式,直接决定多卡训练能不能随卡数线性加速。HPC 侧的同构做法可对照多 GPU 域分解,另一种并行切法见数据、张量与流水线并行。
本文要回答的问题
- 数据并行训练里,为什么每张卡算完反向传播后不能直接更新参数?梯度同步发生在哪一步?
- 朴素参数服务器(parameter server)让所有卡把梯度汇聚到一张卡再广播,瓶颈具体卡在哪?
- Ring AllReduce 的 scatter-reduce 与 all-gather 两个阶段各做什么?为什么需要 N-1 步?
- 每张卡实际收发多少数据?2(N-1)/N 这个通信量公式怎么来的,为什么说带宽随卡数扩展?
- tree(树形)归约和 ring 相比各自的取舍是什么?NCCL 又如何按拓扑选算法、做通信计算重叠?
一、数据并行:N 份梯度必须先变成一份
数据并行(data parallel)是最简单的并行策略:模型在每张卡上有一份完整副本,一个全局批次被切成 N 份,卡 i 只吃第 i 份小批(mini-batch)。前向、反向传播全程各卡独立、毫无往来,直到反向传播结束——每张卡都算出了一套形状与模型参数完全相同的梯度张量,但因为吃的数据不同,N 套梯度的数值各不相同。
参数更新的数学规则是按全局批次的平均梯度走一步 SGD:w ← w − η ·(1/N)Σ g_i。若各卡拿着自己的 g_i 直接更新,N 张卡的模型立刻分道扬镳,下一批前向就跑在 N 个不同模型上,训练彻底失义。所以更新前必须插入一道全局操作:把 N 套梯度求和(再除以 N),并让每张卡都拿到同一份结果。这个「所有输入归约、结果分发给所有节点」的原语就是 all-reduce。
一个 70 亿参数的模型,FP16 下梯度张量约 14GB;1750 亿参数的模型则超过 350GB。每个训练步,这笔数据都要在卡间完整流动一次。模型有多大、每步通信量就有多大,这正是集合通信成为大模型训练第一开销的原因。
值得强调的是同步的位置:它卡在「反向结束、优化器更新之前」这个严格的依赖点上。梯度没归约完,任何一张卡都不能更新;更新没完成,下一批前向也不能开始。也就是说集合通信处在训练迭代的关键路径(critical path)上,没有任何「先干别的、稍后补」的余地——除非从算法内部把它和尚未完成的那部分反向计算重叠起来(见第五节)。这也是为什么同一个模型,换一批带宽不同的卡,训练时长可能相差数倍,而推理性能却几乎不受影响。
二、朴素参数服务器:汇聚再广播,瓶颈在中心
最直白的 all-reduce 实现是参数服务器(parameter server,PS):指定一张卡(或一组 CPU 节点)当中心,其余 N−1 张卡把梯度全部发给它;中心收齐后求和、取平均,再把结果广播回所有卡。
逻辑无懈可击,数据量却灾难。设模型大小为 M。汇聚阶段,PS 要从 N−1 张卡接收总计 (N−1)·M 的数据;广播阶段再发出 (N−1)·M。PS 一侧的总进出流量约为 2(N−1)·M,随卡数线性膨胀。更致命的是这些流量全压在 PS 的链路上:N 张卡同时往一个口灌数据,链路是共享的,其他卡发得再快也只能排队;广播时同样如此。环上传输可以 N 条链路同时跑满,星形拓扑的任何瞬间却只有「中心 ↔ 某张卡」在实质传输,中心带宽变成整个集群的天花板。
给个算例:8 卡、M=14GB、链路有效带宽 400GB/s。理想情况下若所有链路并行,每卡收发约 24GB 数据应在 0.06 秒左右完成;PS 模式下中心要吞吐 2×7×14≈196GB,单口 400GB/s 也要近 0.5 秒,卡越多差距越大。这就是 2017 年百度提出 Ring AllReduce 后,PS 在稠密数据并行训练里迅速被淘汰的原因。
三、Ring AllReduce:环上两阶段,各走 N−1 步
Ring AllReduce 的物理前提是把 N 张卡连成一个单向环:每卡只和左邻、右邻有链路。算法分两个阶段。
第一阶段 scatter-reduce(分散-归约)。先把梯度张量均匀切成 N 个分片,记为 chunk 0 … chunk N−1。随后环上跑 N−1 步:每一步,每张卡把自己手上的一个分片发给右邻、同时从左邻收一个分片,收到后把同名分片与本地分片相加。随着分片沿环流动,第 k 步结束后,会有一张卡拿到某个分片的全部 N 份之和;N−1 步跑完,每张卡都恰好持有一个「已经归约完全」的分片(各卡持有的分片不同)。
第二阶段 all-gather(全收集)。再跑 N−1 步,规则相同但不再做加法:每张卡把自己持有的归约好的分片沿环转发,N−1 步后所有分片绕环一周,每张卡集齐全部 N 个分片,于是每卡都有了完整的归约结果。
用 4 卡、每卡梯度切成 4 片走一遍,能看清分片如何「边走边长大」。记卡 i 的第 j 片为 g_i[j]。scatter-reduce 第 1 步:GPU0 把 g0[0] 发给 GPU1,GPU1 把 g1[1] 发给 GPU2,GPU2 发 g2[2] 给 GPU3,GPU3 发 g3[3] 给 GPU0;收到即累加,于是 GPU1 上 chunk0 变成 g0[0]+g1[0],GPU2 上 chunk1 变成 g1[1]+g2[1]。第 2 步继续转发这些两片之和:GPU1 把它发给 GPU2,chunk0 累加成三份之和。第 3 步后,GPU2 拿到 g0[0]+g1[0]+g2[0]+g3[0],四张卡各自持有一个不同分片的四份之和。此时每张卡只有 1/4 的最终答案,但全环合起来已经是完整答案。
all-gather 阶段不再有算术,只是搬运这四个成品分片。再走 3 步,chunk0 之和从 GPU2 出发沿环经过 GPU3、GPU0、GPU1 绕满一圈,其余三个分片同理反向错开流动,结束时每张卡显存里都集齐四个分片,拼起来就是完整的平均梯度(求和后统一除以 4)。全程共 2(N−1)=6 步,没有中心,每一步环上 N 条链路全部在传不同的分片——任意时刻检查网络,四条链路没有一条空闲,也没有两个分片抢同一条链路,这正是调度设计的精妙处。
四、通信量公式:每卡只收发 2(N−1)/N·M
Ring 的通信量可以精确算出。每个阶段共 N−1 步,每步每卡发出和接收各一个分片,分片大小为 M/N。所以每阶段每卡通信量为 (N−1)·M/N,两阶段合计:
V_ring = 2·(N−1)/N·M
这是整个算法最重要的式子。代入数字:N=8 时 V=1.75M,即 14GB 模型每卡收发约 24.5GB;N=64 时 V≈1.97M≈27.6GB;N 趋于无穷时 V 趋近 2M。单卡通信量上有界(2M),与卡数几乎无关——这与 PS 模式中心卡承担 2(N−1)M 形成鲜明对比。
关键在于链路并行度。环上任何时刻有 N 条链路同时传输,每条每步搬 M/N,一个阶段的墙钟时间约为 (N−1)·(M/N)/B ≈ M/B(B 为单链路带宽),同样与 N 无关。也就是说,增加卡数时新增的梯度归约工作被新增的链路并行消化掉了:聚合带宽随卡数线性增长。这正是「带宽最优」(bandwidth-optimal)算法的含义。
代价是延迟:共 2(N−1) 个传输步,每步都有启动延迟 α,延迟项约为 2(N−1)α。卡数极多、消息很小时,α 会盖过带宽项,所以实际实现会把多个小张量合并(fuse)、或在节点内切换层级算法。
用经典的 α-β 通信模型把两项写在一起,单次 ring all-reduce 的墙钟时间约为:
T = 2(N−1)·α + 2(N−1)/N·M/β
其中 α 是每步固定启动延迟,β 是单链路有效带宽。大消息时第二项主导,ring 接近最优;小消息时第一项主导,2(N−1) 个串行启动点反而成了累赘。这解释了工程上两个相反方向的优化:小张量先 fuse 成大块再传(把 α 摊薄一次),大张量则放心交给 ring(把 β 吃满)。N=8、M=14GB 代入:α 取约 10 微秒,延迟项仅约 0.14 毫秒,而带宽项 24.5GB/400GB/s≈61 毫秒,带宽项占 99% 以上;若是 1MB 的小张量,带宽项只剩微秒级,延迟项立刻反客为主。
五、带宽天花板与扩展效率:典型数字
多卡训练的加速效率可用一个简单模型估算。设单卡单步计算时间为 T_c(数据并行下每卡批大小不变时,T_c 基本固定),通信时间为 T_m。由于通信无法完全并行于计算时:
效率 E ≈ T_c /(T_c + T_m), 加速比 S ≈ N·E
以 70 亿参数模型、8 卡 A100、NVLink 有效带宽约 400GB/s 为例:V≈24.5GB,T_m≈0.06 秒;若单步计算 T_c≈0.3 秒,则 E≈83%,8 卡实测加速比常在 7 倍上下。换到 PCIe 4.0(有效约 20GB/s),T_m≈1.2 秒,效率跌到约 20%,8 卡不如 2 卡——同一份训练任务,差距全在带宽。
集群放大后同理。典型生产数字:机内 8 卡 NVLink 数据并行扩展效率约 85%-90%;跨 IB NDR(约 50GB/s)拉到 64-128 卡时,若全局批相应增大、T_c 变长,效率还能维持在 70%-85%;但若保持每卡批大小不变、硬拉卡数,T_m/T_c 随规模攀升,千卡级线性度(linear scaling)可能跌破 50%。这就是为什么带宽被称为多卡训练 scaling 的天花板:算力可以靠加卡线性购买,梯度交换时间却只能靠链路带宽和算法压缩。
再给一个大模型算例。1750 亿参数模型 FP16 梯度约 350GB,单卡根本放不下完整副本,实际以张量并行 8 卡为底座、再在其上做数据并行;每个数据并行组单步 ring 通信量约 1.97×350GB≈690GB,机内 NVLink 400GB/s 有效带宽下纯通信约 1.7 秒,必须靠重叠压进计算时间里。工业界公布的典型效率:8 卡机内数据并行 MFU(模型浮点利用率)可达 50% 上下;64-128 卡跨 IB 维持 40%-50%;千卡规模若网络为 200Gb/s 级旧带宽且批大小保守,MFU 常掉到 25%-35%。带宽每代翻倍(HDR→NDR→GDR,200→400→800Gb/s),同等规模的扩展效率就抬升一个台阶。
主要缓解手段是通信计算重叠。反向传播从最后一层往前逐层产出梯度,框架(PyTorch DDP)把梯度桶(bucket)化:一旦某桶就绪就立刻异步发起 ring all-reduce,此时前层梯度还在计算,通信与计算在时间轴上重叠。假设反向传播耗时 T_b、梯度按 10 个桶均匀就绪,第一桶在 0.1T_b 时即可开传,若单桶 ring 时间不超过 0.1T_b,则九桶全部藏进反向窗口,理想情况下暴露出来的通信只剩最后一桶的时间。桶太大则启动晚、藏不住多少,桶太小则重新落入 α 主导区且挤占通道,桶大小因此是 DDP 最值得调的参数之一。此外增大每卡批大小、梯度累积(累积多步再同步一次)把 T_c 放大 k 倍而 T_m 只发生一次,都直接摊薄 T_m/T_c;代价是全局批变大可能影响收敛,需配合学习率缩放。
六、Ring 之外:tree 与三种方案对比
树形(tree)归约是另一条经典路线:卡两两配对求和,结果再两两配对,log₂N 层后在根卡得到归约值,再沿树反向广播下去。
| 维度 | 参数服务器 PS | Ring AllReduce | Tree 归约 |
|---|---|---|---|
| 单卡通信量 | 中心 2(N−1)M,其余 2M | 每卡均匀 2(N−1)/N·M ≤ 2M | 根附近卡负载大,叶子约 2M |
| 步数/延迟项 | 2 轮(逻辑上),但中心串行 | 2(N−1) 步,延迟随 N 线性 | 2·log₂N 步,延迟最低 |
| 链路并行度 | 低,中心口是瓶颈 | 高,N 条链路同时跑满 | 中,逐层展开,低层并行受限 |
| 带宽利用 | 差,随卡数恶化 | 带宽最优,随卡数扩展 | 次优,归约快、分发受树宽限制 |
| 适用卡数 | 小规模、稀疏参数场景 | 中大规模式稠密 all-reduce 主力 | 延迟敏感的小规模/节点内低层 |
公平地说,PS 并非一无是处:在推荐模型、大规模稀疏嵌入场景,每张卡只触达极小一部分参数,梯度天然稀疏,按参数分片(sharding)的 PS 让每台服务器只存一部分参数,通信量按稀疏度等比例缩小,此时中心不再是瓶颈。tree 也有自己的拥趸:交换芯片支持网内归约(如 InfiniBand SHARP)时,归约在交换机硬件里流式完成,树形的低层并行受限问题被硬件消解,延迟和带宽两头占优。工程实现通常是分层混合:节点内走 NVLink 上的 ring 或双环(double ring,双向两个环再减半延迟),节点间在 IB 上再建一层 ring 或 tree,这正是 NCCL 的层级 all-reduce。NCCL 还会根据消息大小切换算法:小消息用延迟低的 tree/sharp,大消息用带宽最优的 ring。
还要注意 ring 的两个阶段可以单独使用。scatter-reduce 单独拿出来就是 reduce-scatter,all-gather 本身也是独立原语。ZeRO、FSDP 这类分片优化器状态的训练框架,核心通信就是 reduce-scatter 加 all-gather:每张卡只保存 1/N 的优化器状态与参数分片,反向后用 reduce-scatter 各拿一片梯度的归约结果、各自更新,下次前向前再 all-gather 拼出完整参数。通信总量与 ring all-reduce 相同,却把显存占用也切到了 1/N——集合通信原语既能保证一致性,也能反过来支撑显存切分。
七、NCCL 与拓扑感知:让算法贴合物理拓扑
NCCL(NVIDIA Collective Communications Library)是训练侧事实标准的集合通信库,提供 all-reduce、all-gather、reduce-scatter、broadcast、all-to-all 等原语,被 PyTorch DDP、Megatron、DeepSpeed 直接调用。它最大的价值是拓扑感知:初始化时探测 GPU 之间是 NVLink 直连、经 NVSwitch、同 PCIe switch 还是跨 socket、跨 IB,并据此选链路、建环。
具体策略上,NCCL 优先用 NVLink 组成节点内带宽最优环,IB 网卡组成跨机环,并尽量让跨机流量只在节点归约后发生(hierarchical all-reduce);在 NVSwitch/IB SHARP 网络上还能把归约下沉到交换芯片(in-network reduction),进一步缩短树的步数。用户可见的调优旋钮包括 NCCL_IB_*(指定网卡与通道)、NCCL_NSOCKS_PERTHREAD、NCCL_MIN_NCHANNELS 等,但绝大多数场景默认探测即最优。
拓扑感知也解释了并行组绑定原则:需要高频 all-reduce 的数据并行组应尽量取物理同构的卡集合;张量并行的逐层通信必须锁在 NVLink 全互联域内,参见数据、张量与流水线并行;HPC 中 halo 交换与全局归约的层级安排可对照多 GPU 域分解。判据始终一致:让通信发生在带宽金字塔最合适的那一层。
实战中还有几个高频坑。其一,容器内 NCCL 误选了普通以太网卡做跨机回退(fallback),带宽从 50GB/s 掉到 1-2GB/s,表现为加机器反而减速,可用 NCCL_DEBUG=INFO 打印实际选用的链路来定位。其二,NCCL_P2P_DISABLE 等环境变量被旧配置遗留,P2P 被关闭后机内通信退回经主机内存中转,带宽腰斩。其三,GPU 时钟降频、NUMA 绑核错误也会让 ring 的实测带宽只有理论值的一半。定位手段统一:开 INFO 日志看拓扑识别是否正确,跑 nccl-tests 量 all-reduce 总线带宽(busbw),8 卡 A100 机内 busbw 应在 300GB/s 量级,明显偏低即配置或硬件有问题,而不是算法本身。
这一篇在知识体系里的位置
这一篇处在「物理互联 → 通信算法 → 并行策略」链条的中间。链路本身的带宽层级(PCIe/NVLink/InfiniBand)见 NVLink 多 GPU 互联;集合通信原语在 HPC 域分解上的同构应用见 多 GPU 域分解;理解 all-reduce 之后再看数据、张量、流水线三种并行如何分工,见 数据、张量与流水线并行。卡内为什么需要这么多梯度吞吐,可回看 GPU 架构深入:SIMT、warp 调度。
总结
数据并行每步必须把 N 份不同梯度归约成一份再分发给所有卡;朴素参数服务器让中心卡吞吐 2(N−1)M 数据、瓶颈随卡数恶化,Ring AllReduce 用 scatter-reduce 与 all-gather 各 N−1 步让 N 条链路全程并行,单卡通信量压到 2(N−1)/N·M、聚合带宽随卡数线性扩展;tree 以 log₂N 步赢在延迟、ring 以带宽最优赢在大规模,NCCL 按物理拓扑分层混用并配合梯度桶化做计算通信重叠——带宽利用效率就是多卡训练扩展效率的天花板。