DP、ZeRO-1/2/3、FSDP:每卡显存多少、通信多多少?
Megatron distributed optimizer 是哪一级?

英文问法How does DDP overlap communication with backward? · What do ZeRO stages 1, 2 and 3 shard, and how much memory per GPU? · How does torch FSDP work? · How much more communication does ZeRO-3 need than DDP?

考过NVIDIA Research Intern("How does torch FSDP work?",拿到 offer,Glassdoor RVW102358730,年份未核实)· 文心("deepspeed zero1-3")· 牛客人才库合并帖(公司未写明,"FSDP2和FSDP1的区别")· 牛客("DeepSpeed ZeRO stage1和stage 2的通信量区别,论文和代码实现有没有gap")· xAI ML/Infra 第一轮补全 DP 和 FSDP 的 distributed matmul(1p3a 1171574)

先弄清楚:每个概念是什么

DDP(data parallel)
每张卡一份完整模型,各吃不同的数据,backward 后 all-reduce gradient 求平均。gradient 分成 bucket,一个 bucket 算完就异步 all-reduce,和剩下的 backward 同时跑。
Model states:16Ψ bytes
mixed precision Adam 每个参数:bf16 param 2 + bf16 grad 2 + fp32 master、m、v 共 12 bytes(推导见 optimizer)。例:7B 模型 DDP 每卡一整份,112 GB,一张 80 GB 的卡放不下。
reduce-scatter 与 all-gather
all-reduce = reduce-scatter(求和,每卡只拿到结果的 1/N)+ all-gather(把各卡的 1/N 拼回完整的),详见 all-reduce。
ZeRO-1 / 2 / 3
把 model states 按 DP rank 切成 N 份,每张卡只保管、只更新自己那 1/N。stage 1 切 optimizer state,2 再切 gradient,3 再切 parameter。
FSDP
PyTorch 版的 ZeRO-3:模型分成若干 unit,算到某个 unit 前 all-gather 出完整参数,算完马上释放;backward 后 reduce-scatter gradient。每卡显存 ≈ 分片后的模型 + 最大的那个 unit。

看图:ZeRO 每级切掉什么,FSDP 一个 unit 怎么走

每个 GPU 框里三条:param、grad、optimizer state,各分 4 段DDPZeRO-1ZeRO-2ZeRO-3GPU0param 2Ψgrad 2Ψoptim 12ΨGPU1param 2Ψgrad 2Ψoptim 12ΨGPU2param 2Ψgrad 2Ψoptim 12ΨGPU3param 2Ψgrad 2Ψoptim 12Ψ每卡存16Ψ4Ψ + 12Ψ/N2Ψ + 14Ψ/N16Ψ/N7B、N = 8112 GB38.5 GB26.25 GB14 GB每卡通信2Ψ2Ψ2Ψ3Ψ
图 1:每个小框是一张卡,三条从上到下是 param、grad、optimizer state。蓝色整条是存一整份,暖色一段是只存自己那 1/N。图上只画 4 张卡,下面的 GB 是 7B、N = 8 的数。
forward(unit 1 → 最后一个)平时:只存 1/N 参数完整参数:算 forwardall-gather 参数又只剩 1/N 参数算完马上 freebackward(倒着走回 unit 1)只存 1/N 参数完整参数:算 backward再 all-gather 一次参数只留 1/N gradientreduce-scatter gradient所有 unit 加起来:all-gather Ψ + all-gather Ψ + reduce-scatter Ψ = 3Ψ每卡显存 ≈ 分片后的模型 + 最大的那个 unit
图 2:FSDP 里一个 unit(比如一层)在一张卡上的经历。蓝色框是临时凑出的完整参数,用完就释放,同一时刻只有正在算的 unit 是完整的。

30 秒回答

In DDP every GPU holds a full replica, and gradients are all-reduced bucket by bucket during backward, overlapping communication with compute. Mixed-precision Adam needs 16 bytes per parameter, replicated on all N ranks. ZeRO shards these model states across the data-parallel ranks: stage 1 shards optimizer states, 4Ψ + 12Ψ/N bytes per GPU; stage 2 adds gradients, 2Ψ + 14Ψ/N; stage 3 adds parameters, 16Ψ/N. Stage 2 communicates 2Ψ, a reduce-scatter plus an all-gather, same as DDP, and the paper says stage 1 adds no communication either; stage 3 gathers parameters again in backward, 3Ψ or 1.5×. FSDP is PyTorch's ZeRO-3; Megatron's distributed optimizer is ZeRO-1-style optimizer-state sharding.

推导 / 关键公式

每卡 model states(bytes)

没切的部分每卡一整份,切了的部分 \(\div N\)。以 ZeRO-1 为例(ZeRO-2、3 见下表):

\[ \text{ZeRO-1: } \underbrace{2\Psi + 2\Psi}_{\text{param, grad}} + \underbrace{\frac{12\Psi}{N}}_{\text{master, m, v}} \]

每卡通信量(元素数,ring 下再乘 \((N-1)/N\))

DDP:all-reduce \(= \Psi + \Psi\)。ZeRO-2:gradient reduce-scatter \(\Psi\) + 更新后 param all-gather \(\Psi\),还是 \(2\Psi\)。论文说 ZeRO-1 同样不增加通信,但没有单独推导。ZeRO-3:forward、backward 各 all-gather 一次 param,再 reduce-scatter gradient:

\[ \Psi + \Psi + \Psi = 3\Psi = 1.5 \times \text{DDP} \]

手算例子:7B、N = 8

做法每卡 bytes7B、N = 8通信
DDP\(16\Psi\)112 GB\(2\Psi\)
ZeRO-1\(4\Psi + 12\Psi/N\)28 + 10.5 = 38.5 GB\(2\Psi\)
ZeRO-2\(2\Psi + 14\Psi/N\)14 + 12.25 = 26.25 GB\(2\Psi\)
ZeRO-3 / FSDP\(16\Psi/N\)14 GB\(3\Psi\)
Megatron distributed optimizer\(6\Psi + 12\Psi/N\)42 + 10.5 = 52.5 GB\(2\Psi\)

Megatron 这行是 bf16 param + fp32 grad(gradient 用 fp32 累加,不切时 18Ψ = 126 GB):param 2 + grad 4 = 6 bytes 每卡一整份,只切 fp32 main param、m、v 这 12 bytes。表里不含 activation。

必答点

追问

ZeRO-1 和 ZeRO-2 的通信量有区别吗?

论文只推导了 ZeRO-2。实现里如果 ZeRO-1 的 gradient 仍用 all-reduce,再 all-gather param,就是 \(3\Psi\),换成 reduce-scatter 才是 \(2\Psi\)(按论文 §5.1 推算,牛客题问的 gap 指的就是这里)。memory 上 ZeRO-2 不归自己的 gradient 用完就释放,ZeRO-1 要留一整份。

FSDP 怎么把通信藏起来?

all-gather 放在单独的 CUDA stream 上,backward 默认 prefetch 下一个 unit 的 all-gather。forward 的 prefetch 默认关,CPU-bound 时才开,细节见 overlap。

FSDP2 和 FSDP1 有什么区别?

FSDP1 把一组参数 flatten、拼接成一个 FlatParameter 再切,FSDP2 用 DTensor 把每个参数各自按 dim-0 切,表示更简单、容易和 TP 组合,sharded state dict 不需要 all-gather,也不用像 limit_all_gathers 那样阻塞 CPU。

跨机时全切太慢怎么办?

用 hybrid sharding,机内切、机间复制,all-gather 和 reduce-scatter 只走 NVLink,见 Megatron-FSDP v2。

手写 FSDP 的一层 linear 怎么写?(xAI 题)

每卡存 \(W\) 的 1/N 行。forward 前 all-gather 出完整 \(W\),算本地 batch 的 \(XW\) 后释放。backward 再 all-gather 一次算 \(dX\),本地算完整形状的 \(dW\),reduce-scatter 成各自那 1/N 行。

来源Rajbhandari et al. 2019(ZeRO)§3.1(pp.7–8)、§5、Table 1(pp.10–11)、§7(pp.13–14)· Zhao et al. 2023(PyTorch FSDP)§1(p.2)、§3、§3.2(pp.3–6)、§3.3(pp.6–7)· Megatron Core: Distributed Optimizer · PyTorch DDP notes · PyTorch fully_shard(FSDP2)