多卡训练:参数在哪里,数据怎样流动
从两张卡的梯度同步,推演到 FSDP、Tensor Parallel 与集群拓扑。
建立显存、通信量和关键路径三本账,理解 DP/TP/PP/CP/EP/FSDP、ring collective 与跨机带宽。
读完这一章,你应该能够
- 画出每个rank的参数、激活与梯度位置
- 按byte推导collective成本和并行组
- 用小规模基线排查带宽、overlap与恢复故障
本章目录 · 0 个小节
多买几张卡,不会自动形成一个更大的 GPU
每张卡有自己的显存、计算与通信路径。训练程序中的 rank 是一个通信参与者,常见部署是一进程一卡。多卡训练要回答三个问题:每个 rank 持有什么张量、执行哪些算子、何时必须等别人。模型放得下只是第一关;通信使它能共同更新,调度决定它是否有效率。
课程分析:先学GPU 内的一步,再看 GPU 之间的依赖。数据并行可以让不同卡处理不同样本,但没有减少每份模型的显存;张量并行拆算子,FSDP 拆存储,两者解决的问题不同。
从两张卡的同一次更新开始
设两个 rank 各持有相同参数,分别计算局部 batch 的平均梯度 。若局部有效样本数相等,全局平均梯度为 。AllReduce(sum) 使每个 rank 得到和,再按约定归一化;不是所有库都以同一个地方、同一个规则除 world size。
如果各 rank 的有效 token 数不同,简单平均局部 token 均值通常不等于全局 token 均值。应对分子和有效 token 分母作一致处理。梯度累积还要检查跨 microbatch 的权重。这个统计问题与 NCCL 的网络速度无关,但能让“分布式版”训练出不同结果。
相同初始参数,rank 0 与 rank 1 各取不同数据
各自 forward:保存各自激活
各自 backward:得到局部梯度
Collective:归约梯度或梯度分片
各 rank 更新自己拥有的参数/状态
下一步需要时重新同步或重建完整参数
六种并行,拆的是不同轴
| 路线 | 分给不同 rank 的对象 | 主要通信与权衡 |
|---|---|---|
| DP / DDP | 不同样本;通常各持完整模型 | 梯度 AllReduce,显存重复 |
| FSDP | 参数、梯度、优化器状态分片 | 参数 AllGather、梯度 ReduceScatter,换通信省显存 |
| TP | 同一层权重/中间维度 | 每层频繁归约或汇集,偏好高速互联 |
| PP | 不同层/阶段 | 激活和梯度 send/recv,microbatch 调度与空泡 |
| CP | 同一序列不同区段 | 跨区段交换 attention 所需信息,适合长上下文 |
| EP | 不同 expert | token dispatch/combine,负载与 AlltoAll 开销 |
Megatron Core 官方指南列出这些维度及组合;具体实现中的分片布局和通信并不由缩写唯一决定。官方并行指南 EP 也不一定是额外独立乘数:不同框架可重用 DP 等物理 rank,必须读 process group 和并行配置。
课程拓扑例子,不是任何模型的官方配置:两台机器、每台四卡,取 TP=4、DP=2。rank 0–3 组成机器 A 的 TP group,4–7 组成 B 的 TP group;对应分片的 DP groups 是 [0,4]、[1,5]、[2,6]、[3,7]。TP 通信留在机内,DP 跨机。把 rank 顺序换错,就可能让高频 TP 跨慢链路。
全局 batch 通常与 microbatch、梯度累积和独立数据副本数有关;不能直接乘所有 GPU 数,因为 TP/PP/CP 中多张卡可能共同处理同一批样本。
第一笔账:显存是峰值,不只是平均分片
课程预算假设:BF16 参数 2 byte、BF16 梯度 2、FP32 主参数 4、Adam 两个 FP32 moment 8,共 16 byte/参数。某些实现的梯度或主副本不同,不能把 16 当通用常数。还要加激活、临时 buffer、通信桶与 allocator 余量。
FSDP 的持久状态大致随分片数下降,但某一层计算前需要重建该层参数,prefetch 可能让相邻层同时占用内存。一次调用把整个模型放入一个巨大的 AllGather group,峰值与按 block 分组完全不同。显存不足时先定位 OOM 在 forward、backward、optimizer 还是保存阶段,再决定分片、重计算或减 batch。
真实源码:FSDP2 的生命周期
固定 PyTorch commit a402343453f89b31a2ec8372b304accdc5b78e5f,文件 torch/distributed/fsdp/_fully_shard/_fully_shard.py::fully_shard。它接收 module、DeviceMesh、mixed precision、reshard 等配置,初始化状态和参数组,并在 module 上接入 FSDP 行为。固定源码
源码明确描述:forward 前 AllGather 参数;若 reshard_after_forward=True 则用后释放完整参数;backward 前再次 AllGather;梯度计算后 ReduceScatter。参数分片表示为 DTensor。输入不是“把整个训练循环自动分布式化”,而是 module 与 mesh;数据采样、loss 归一化、优化器和 checkpoint 仍需协同。接口文档
课程分析:这一调用链解释为什么 FSDP 的通信不能简单等同于 DDP 的一次梯度 AllReduce。是否重分片、分组粒度和预取决定通信次数与峰值;本站成本实验明确采用“前向后重分片、反向再聚合”的一种路径,不宣称覆盖全部策略。
第二笔账:Ring AllReduce 传多少 Byte
设 个 rank,每个待归约张量为 byte。教学 ring 分为 ReduceScatter 和 AllGather,每阶段 轮,每轮每 rank 发送 byte,因此:
是每轮启动/延迟项, 是模型假设的有效单向带宽。接收量与发送量同阶;不要把“发送+接收”重复当成单向链路流量。NCCL 实际会根据拓扑和消息大小选算法、协议及通道,未必就是这个单 ring。官方带宽口径
手算:四 rank、每卡 1 GiB 梯度,发送量为 1.5 GiB/卡。若有效带宽是假设的 50 GB/s,忽略延迟得到约 32.2 ms 下界。GiB 是 byte;GB/s 是 byte/s;400 Gb/s 的原始 bit rate 除以八才是 50 GB/s,协议、共享链路和竞争还会降低有效值。
NCCL tests 的 algbw=M/t,AllReduce 的 busbw=algbw·2(p-1)/p。busbw 是便于解释 collective 的归一化指标,不是直接读取每条物理线的计数器,也不是简单与整机所有端口标称带宽相加比较。定义
集合通信与物理网络怎样相遇
AllGather 把各 rank 分片汇集到每人,ReduceScatter 先归约再各留一片,AlltoAll 交换面向不同目标的不同分片。所有成员必须按协议参与;count、dtype 或调用次序不匹配可能挂住。NCCL 语义
课程分析:PCIe 是常见设备总线,NVLink/NVSwitch 组成 GPU 高带宽互联,InfiniBand 或以太网/RoCE 常承担网络连接。现代系统也存在跨节点 NVLink,不能把“跨机必然只有 IB”当规则。瓶颈应由实际路径确定:GPU 到 NIC 是否跨 CPU socket、NIC 是否共享、交换网络是否过订阅、同一节点是否存在慢链路。
在准备好的 GPU 机器上先看 nvidia-smi topo -m。先测单机两卡,再测目标进程映射下的跨机 collective;若单机正常跨机慢,先定位网卡选择、路径和带宽基线,不要立即降低学习率。官方 NCCL 故障指南也区分 GPU、网络、运行时和拓扑诊断。排查依据
第三笔账:Overlap 能隐藏多少通信
反向按层产生梯度,早完成的 bucket 可以先通信,同时计算更早层的反向。理想重叠时部分时间接近两者最大值,但存在依赖的尾部必须暴露。bucket 太小,启动开销多;太大,等待梯度集齐才开始通信。FSDP prefetch 用显存换等待时间,也可能争用计算所需带宽。
课程分析:优化目标是 critical path。trace 中通信 kernel 与计算同时存在,不证明通信已完全隐藏;它们可能争抢 SM 或 HBM,使计算变慢。按 rank 比较 step 时间、可隐藏区间和尾部等待。最慢一个 rank 决定同步步长,数据加载、专家负载和故障网卡都可能制造慢 rank。
可运行预算实验与真实机器检查
python3 -m labs.run --experiment systems --config labs/configs/systems.json --seed 7
python3 -m unittest discover -s tests -p 'test_*.py' -v
课程实现输出显存账本、ring 的逐轮传输事件及 DP/FSDP/TP 通信估计。它是可核查的字节/时序模型,不会启动真实多机集群。改变 GPU 数、每节点卡数、带宽和消息大小,观察为什么增加卡数可能省显存却增加延迟项。字段说明与默认假设见 labs/README.md。
默认保持卡数和每个副本的 token 数时,TP 消耗多张卡协同处理同一批数据,因此 DP 与 TP 的全局 token 数不同。不能只凭输出的 step 时间判定谁更快;先对齐任务量,再比较 token/s、每 token 成本、显存和数值目标。activation 预算是可调假设,显示“能放下”不等于通过了真实 OOM 测试。
已有 CUDA、NCCL 与编译好的官方 nccl-tests,并且当前机器至少有两张可见 GPU 时,可在该上游仓库执行:
./build/all_reduce_perf -b 8M -e 128M -f 2 -g 2
这是上游真实 benchmark 命令形式,不是本站已完成的集群测量。比较多种消息大小的时间和 busbw,不要拿小消息延迟区间预测巨大梯度的带宽。跨机启动还需正确 MPI/集群配置,不能把单机 -g 增大就视为跨机测试。官方测试仓库
保存、恢复与故障定位
checkpoint 至少要保持参数、优化器、scheduler、随机状态、数据游标与 step 的一致性;异步保存还要明确哪个 step 的状态已持久化。PyTorch DCP 提供分布式模型与 optimizer state 的保存/加载接口,但数据队列与训练作业恢复逻辑仍需应用负责。官方 DCP
工程任务:用两节点八卡的假设拓扑,画出 TP/DP groups,手算每层激活与每步梯度消息字节数。分别注入慢网络、慢数据 rank、全模型一次 AllGather、保存阶段显存峰值四种问题,为每个问题写出观测指标与反证实验。最后设计“某rank保存失败”和“恢复到不同world size”的验收条件;没有完成一致性测试,就不能把能加载权重等同于训练可恢复。
把这一章放进实验里
调整变量,观察机制,再运行本地 Python 实验验证。
本章证据与源码
引用代表相应来源支持的结论,不意味着公开了完整训练配方。
源码观察NCCL tests bandwidth accountingNVIDIA · b4d5beebca8a官方事实NCCL Collective OperationsNVIDIA · 2026-09-22 查官方事实NCCL TroubleshootingNVIDIA · 2026-09-22 查源码观察PyTorch FSDP2 fully_shard implementationPyTorch · a402343453f8官方事实PyTorch FSDP2:fully_shardPyTorch · main 文档快照官方事实Megatron Core Parallelism Strategies GuideNVIDIA · 2026-09-22 查官方事实Getting Started with Distributed CheckpointPyTorch · 2026-09-22 查留下你的工程判断
记录一个还没有解释清楚的现象,或下一次实验要控制的变量。笔记仅保存在当前浏览器。
自动保存在本机 · 可在“我的学习笔记”导出