本文是AI Infra 面试题系列的 Level 2,覆盖 Q13-Q26。重点是梯度同步、bucket overlap、ZeRO/FSDP、checkpoint 与数值诊断。
上一篇:GPU 与性能基础 · 系列总索引 · 下一篇:TP、SP、CP、PP 与 EP
3. Level 2:DDP、FSDP 与训练运行时
Q13. DDP 从 forward 到参数更新经历了什么?
高分回答要点
- 初始化时各 rank 拥有完整模型,参数应保持一致;每 rank 处理不同数据分片。
- autograd 生成梯度后,DDP hook 在 bucket ready 时启动 AllReduce;归约后每个 rank 获得等价的平均梯度。
- bucket 允许后层梯度生成时就通信,与更早层 backward 计算重叠;optimizer 在所有所需梯度就绪后本地更新。
- unused parameter、动态图、梯度 view、bucket rebuild 和不同 rank 控制流会影响正确性或性能。
追问:为什么只在 step 末尾手工对整个 gradient buffer 做一次 AllReduce,通常 overlap 更差?
Q14. 强扩展和弱扩展怎样定义?
高分回答要点
- 强扩展固定全局问题规模,增加 GPU;理想 step time 约除以
N。效率可写为throughput_N/(N * throughput_1)。 - 弱扩展固定每 GPU 工作量,全局规模随 GPU 数增加;理想每 GPU 吞吐不变,step time 近似不变。
- 对训练必须明确固定的是 samples、tokens 还是 sequence;global batch 改变还会影响优化超参数和收敛,系统 scaling 与 time-to-quality 不是一回事。
- 小模型/小 microbatch 下固定通信和 launch 成本占比高,强扩展很快失效。
实验锚点:约 110M 模型的 8 卡 DDP 强扩展仅 2.82x、效率 35.3%,适合解释为什么单机 GPU 数增加不保证线性速度。
Q15. microbatch 为什么影响 DDP 扩展效率?
高分回答要点
- 对固定模型,每个 optimizer step 的梯度 payload 基本与参数量相关,而不是与 microbatch 成正比;计算量则随 token 数增长。
- 因而增大 microbatch 提高 compute/communication ratio,也通常产生更大的 GEMM、减少 launch 占比。
- 但 activation 显存随 microbatch 增长,最终受到 OOM、数据分布和全局 batch/收敛约束。
- 梯度累积可以增加每次同步前的本地计算;使用
no_sync避免每个 microstep 都 AllReduce。
实验锚点:本项目 microbatch 1→8 时,每 token 梯度 payload 约从 838.8 KiB 降到 104.8 KiB,8 卡弱扩展效率从约 31.9% 提升到 51.2%。
Q16. DDP bucket size 应该怎样调?
高分回答要点
- 小 bucket 更早 ready、潜在 overlap 更好,但 collective 数量多、latency 与 launch overhead 高。
- 大 bucket 带宽效率高,却要等更多梯度生成,尾部 exposed communication 可能变长。
- 最优值取决于 layer backward 时间、网络 latency/bandwidth、参数分布和是否有大 embedding;不能给通用常数。
- 用 timeline 看每个 bucket ready、NCCL 起止、compute stream 空洞和最后一个 collective,而不是只看总 NCCL kernel 时长。
追问:如果总 AllReduce 时间不变,但 step time 下降,发生了什么?如果总时间下降而 step 不变,又可能是什么?
Q17. 如何证明计算和通信发生了 overlap?
高分回答要点
- 在 Nsight Systems/PyTorch Profiler 时间线上同时观察 compute stream 与 NCCL stream,检查真实时间区间重叠。
- 比较 step wall time 与 critical path;不能把所有 kernel duration 直接相加,因为不同 stream 可能并发。
- 做因果对照:禁用异步/改变 bucket、插入同步或改变 microbatch,观察 exposed communication 是否变化。
- 还要检查 GPU 间 skew:一个 rank 较慢会让其他 rank 的 collective kernel 表现为等待。
实验锚点:本项目 m1 与 m8 均看到约 13 个 FP32 Ring AllReduce kernel,累计约 104-106 ms;仅凭这个累计数不能断言它们全部暴露在 step 关键路径上。
Q18. 为什么 profile 中 NCCL kernel 很长,不一定说明网络带宽差?
高分回答要点
- GPU collective kernel 生命周期可能包含等待 peer、等待数据 ready、同步和传输,不等于纯链路搬运时间。
- 某个 rank 的前序 compute、CPU launch 或数据加载慢,会让其他 rank 在 collective 内等待,形成 straggler 放大。
- 多 stream 并发时累计 kernel duration 可大于 wall time;工具时钟、采样和 trace 范围也要核对。
- 应结合 nccl-tests、每 rank timeline、消息大小、算法/协议、拓扑和系统 counters 定位。
Q19. 梯度累积与增大全局 batch 有什么系统和算法区别?
高分回答要点
- 系统上,累积
K个 microstep、只同步一次,可摊薄通信和 optimizer 成本,但总 forward/backward 计算不减少。 - 显存主要由单个 microbatch activation 决定,梯度 buffer 需要保留;可用它突破单步 microbatch 显存限制。
- 算法上 global batch 变大可能需要学习率、warmup、数据顺序调整,并可能改变泛化和达到目标 loss 的 token 数。
- BatchNorm、dropout RNG、loss normalization、gradient clipping 和 mixed-precision overflow 都可能让“数学等价”失效。
Q20. ZeRO-1/2/3 与 FSDP 的核心关系是什么?
高分回答要点
- ZeRO-1 分片 optimizer state,ZeRO-2 再分片 gradient,ZeRO-3 再分片 parameter。
- PyTorch FSDP
FULL_SHARD在概念上接近 ZeRO-3:计算前 AllGather 所需参数,backward 后 ReduceScatter 梯度,并让参数/梯度/optimizer state 分片驻留。 SHARD_GRAD_OP更接近只分片 gradient 与 optimizer state 的折中,通常保留更多参数驻留以减少重新聚合。- 真正实现差异还包括 flatten/per-parameter 表示、prefetch、通信调度、offload、checkpoint 和混合精度,不能只按 ZeRO stage 贴标签。
Q21. SHARD_GRAD_OP 为什么可能比 FULL_SHARD 更快却更占显存?
高分回答要点
- 它减少参数反复 reshard/AllGather 的频率或驻留转换,在 backward 前保留 unsharded 参数,通信和同步更少。
- 代价是完整参数保持更久,峰值显存高于
FULL_SHARD。 FULL_SHARD容量更强,但参数 AllGather、ReduceScatter、prefetch buffer 和 allocator 峰值都可能进入关键路径。- 选择应由“模型能否放下、目标 batch、网络带宽和吞吐”共同决定,不是 stage 越高越好。
实验锚点:本机 SHARD_GRAD_OP 约 10,009 tok/s、1.55 GiB,FULL_SHARD 约 7,704 tok/s、1.06 GiB,正好展示容量/吞吐 Pareto frontier。
Q22. 如何公平比较 DDP、FSDP 和通信压缩?
高分回答要点
- 固定模型、初始化、数据、global tokens/step、优化器语义、计算精度、warmup 和测量窗口。
- 把参数/activation 精度与 collective dtype 分开;FP16 通信不是纯粹“换并行策略”。
- 同时报吞吐、峰值显存、loss/梯度误差和通信字节;容量配置应再比较可达到的最大 batch 或模型规模。
- 校验每 rank 参数更新一致性和 loss 轨迹,避免因错误少算通信获得虚假加速。
实验锚点:报告把 DDP FP32 通信、DDP FP16 通信和两种 FSDP 配置分列,面试时应主动解释这一公平性边界。
Q23. FSDP wrap policy 为什么会影响性能和峰值显存?
高分回答要点
- wrap unit 决定参数 AllGather/ReduceScatter 的粒度,以及可否与相邻层计算 prefetch/overlap。
- 粒度过大,临时完整参数峰值大且通信启动晚;粒度过小,collective 和元数据/launch 开销过多。
- shared parameter、embedding、root module 未 wrap、模块执行顺序与动态控制流会破坏简单估算。
- 应画出两三个 layer 的 forward/backward 时间线,检查当前、prefetch 和待释放的参数同时驻留数量。
Q24. FSDP1 与 FSDP2/fully_shard 有哪些值得面试讨论的变化?
高分回答要点
- FSDP2 使用基于 DTensor 的 per-parameter sharding 表示,而不是主要依赖 flat parameter;更容易与 tensor parallel 等组合并检查 placement。
- 参数在计算前 unshard、计算后 reshard,仍需考虑 AllGather、ReduceScatter、prefetch 和 mixed precision,基本物理成本没有消失。
- API 与 state dict/checkpoint 行为不同,迁移不能只替换类名;要验证 optimizer state、shared weights、compile 和 checkpoint。
- 本项目只实测 FSDP1,因此对 FSDP2 应给官方机制和计划实验,不能宣称有性能结论。
Q25. distributed checkpoint 需要解决哪些问题?
高分回答要点
- 保存模型、optimizer、scheduler、RNG、data loader/样本游标、loss scaler 和训练元数据,保证恢复后的语义连续。
- 每 rank 分片并行写入,使用 manifest/完成标记实现原子提交;故障时不能把半写 checkpoint 当可恢复版本。
- 支持不同 DP/TP/PP 配置恢复需要全局 tensor 元数据和 reshard,而不是把 rank 文件机械重命名。
- 控制 checkpoint pause、异步 staging、存储带宽、保留策略和恢复时间目标;定期做真实 restore 演练。
追问:一个 rank 写失败、其余 rank 成功时怎么办?怎样避免重启后重复或跳过训练样本?
Q26. 训练中出现 loss spike/NaN,你如何定位是数值问题还是分布式错误?
高分回答要点
- 先确定首次异常 step/rank/layer,保存输入、loss scale、梯度 norm、激活统计和 optimizer state;不要只在最终 NaN 后排查。
- 单卡 FP32 或更稳定精度复现,关闭 fused kernel、compile、通信压缩和并行维度,做逐步二分。
- 检查不同 rank 的参数 checksum、梯度归约、数据重复/坏样本、overflow skip 是否一致,以及 collective 顺序。
- 对照最近代码、驱动、kernel、拓扑和数据变更;建立小规模 deterministic replay,但承认某些异步算法无法 bitwise 重现。
评分上限提示:只回答“调低学习率/做 gradient clipping”最多 1 分,因为它没有定位根因。