compute 和 communication 怎么 overlap?
什么时候 overlap 不起作用?

英文问法How do you overlap communication with computation in distributed training? · How does DDP overlap the gradient all-reduce with backward? · What does CUDA_DEVICE_MAX_CONNECTIONS do?

考过面经:国内:快手("DP、TP-SP的计算通信重叠原理";"CUDA_DEVICE_MAX_CONNECTIONS能干什么,和launch bound什么关系",两篇都问到)

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

Overlap 与 exposed communication
通信和计算同时跑,step time 只多出没被盖住的那部分通信(exposed,trace 上怎么认见 训练慢排查)。像洗衣机转着的时候去做饭。
CUDA stream
GPU 上的一条任务队列,同一条里按顺序执行,不同条之间可以并发。NCCL collective 放在单独的 comm stream,靠 event 表达依赖。
Gradient bucketing(DDP)
把 gradient 按 model.parameters() 的逆序(近似 backward 的顺序)装进桶(默认 25 MiB),一桶装满就异步 all-reduce。桶太小 → 一堆低效的小 collective;只用一个大桶 → 得等 backward 全算完。
Prefetch(FSDP / ZeRO-3)
参数平时分片存放,用之前要 all-gather。提前发下一层的 all-gather,算当前层时下一层参数已经在路上。
Critical path 上的通信
下一步计算直接要用通信结果(TP 的 all-gather 之后紧接 GEMM),旁边没有独立计算可做,只能把 GEMM 切成块和通信流水起来。

看图:两条 stream 上的 overlap

不 overlap:100 + 200 + 70 = 370 ms计算 stream通信 streamforwardbackward空等all-reduce370 msDDP 25 MiB 一桶:all-reduce 藏进 backward,≈ 301 ms计算 stream通信 streamforwardbackward≈ 301 ms最后一桶 0.9 ms一条竖线 = 一桶的 all-reduce,桶满就发,合计 70 ms0100200300370 ms
图 1:按比例画的时间线,上面是计算 stream,下面是通信 stream。DDP 分桶后 70 ms 的通信散在 backward 期间,只有最后一桶露在外面。
backward 只有 50 ms:通信比能盖住它的计算还长计算 stream通信 streambackward 50 ms空等all-reduce 70 ms至少露出 20 ms→ gradient accumulation(no_sync())少同步几次
图 2:backward 只有 50 ms 时(放大了比例),就算第一桶一开始就发出去,也至少有 20 ms 露在外面。

30 秒回答

Run collectives on a separate CUDA stream while independent compute runs on the default stream, so only the unhidden part adds to step time. DDP buckets gradients in roughly reverse layer order; when a bucket fills during backward, its all-reduce launches asynchronously while earlier layers keep computing. FSDP and ZeRO-3 prefetch the next layer's parameter all-gather. Tensor-parallel communication is on the critical path, so the GEMM is split into chunks and pipelined with the all-gather or reduce-scatter. Overlap fails when communication outlasts the compute available to hide it.

关键公式

\[ T_{\text{step}} \approx T_{\text{compute}} + T_{\text{exposed}} \] \[ T_{\text{exposed}} \approx \max(0,\ T_{\text{comm}} - T_{\text{hide}}) + T_{\text{last}} \]

\(T_{\text{hide}}\):能和通信并行的独立计算;\(T_{\text{last}}\):最后一桶(或第一次 all-gather)总是盖不住。

并行通信和什么 overlap开关
DP(DDP)gradient all-reduce剩下的 backwardbucket;Megatron --overlap-grad-reduce
ZeRO / FSDP参数 all-gather、gradient reduce-scatter相邻层的 forward / backwardFSDP prefetch;Megatron --overlap-param-gather
TP + SPactivation all-gather / reduce-scatter同一 GEMM 的其他 chunk--tp-comm-overlap(Transformer Engine)
PPp2p send / recv1F1B 稳态里另一个 micro-batchoverlap_p2p_comm(需 virtual pipeline)

手算例子

1B 参数、bf16 gradient 2 GB,DP = 8 跨机、每卡 50 GB/s:ring all-reduce 70 ms(见 all-reduce 页)。设 forward 100 ms、backward 200 ms:

什么时候 overlap 不起作用

必答点

追问

CUDA_DEVICE_MAX_CONNECTIONS 是干什么的?Megatron 为什么要设成 1?

它控制 host 到 GPU 的硬件 work queue 数(默认 8)。设成 1 后所有 stream 的 kernel 按 launch 顺序进同一个队列,Megatron 在 backward 里先 launch dX 的 async all-reduce、再 launch dW 的 GEMM,通信就先被调度。TP 或 CP > 1 且 Blackwell 之前的 GPU 上 Megatron 要求设 1,FSDP 要求不设 1。

FSDP 的 backward prefetch 具体做什么?

backward 里本来是"当前层 reduce-scatter → 下一层 all-gather",同一条 NCCL stream 会让两次通信连着暴露;prefetch 把下一层的 all-gather 提到当前 reduce-scatter 之前发出。

和 launch bound 有什么关系?

kernel 很小、CPU launch 跟不上时,GPU 本来就有空隙,comm kernel 也会发晚,解法见 CUDA Graph。

来源Li et al. 2020(PyTorch DDP) §3.2.2–3.2.3(pp.4–5)· Zhao et al. 2023(PyTorch FSDP) §3.3(pp.6–7)、§3.4.2(p.7)· PyTorch DDP API(bucket_cap_mb)· NeMo Communication Overlap(DP、TP、PP)· Megatron-LM README(overlap flags)、megatron/training/arguments.py、megatron/core/tensor_parallel/layers.py(CUDA_DEVICE_MAX_CONNECTIONS)· CUDA Programming Guide §5.2.3.2 · NCCL env(NCCL_MIN_NCHANNELS)· PyTorch Performance Tuning Guide(no_sync、DDP 顺序)