训练慢、GPU utilization 低怎么查?
训练 hang 住怎么查?
先弄清楚:每个概念是什么
- GPU utilization(nvidia-smi)
- 采样周期里有任意 kernel 在跑的时间比例,不是 SM 用满了多少。一个很小的 kernel 一直跑也算 100%,hang 住时 NCCL kernel 在等对端也可能显示很高。
- Profiler trace 与 GPU gap
- 把 CPU 和 GPU 每个 kernel 画在时间线上。GPU 上的空白就是浪费,空白前 CPU 在干什么,瓶颈就在哪。
- Implicit sync
- CPU 必须等 GPU 算完才能往下走:
.item()、print(cuda_tensor)、.cpu()、依赖 GPU 结果的if。CPU 没法提前排队,GPU 就出现空白。 - Straggler
- 某一张卡比别人慢(数据更长、降频、网卡有问题),所有卡在下一次 collective 都得等它。
- Collective hang
- collective 要求所有 rank 都调用、顺序和 shape 一致。一个 rank 没进来,其余的就一直等。
看图:排查流程和两个例子
30 秒回答
For slow training I first quantify step time, throughput and MFU; nvidia-smi utilization only tells whether any kernel is running. Then I profile a few steps with torch.profiler or Nsight Systems and classify the GPU-timeline gaps: idle at step start means the data loader; many tiny gaps mean launch overhead; gaps after .item() or .cpu() are implicit syncs; long NCCL kernels with nothing beside them are exposed communication. For a hang, I dump every rank's stack with py-spy, turn on NCCL_DEBUG=INFO and the flight recorder, and find the rank that is not in the same collective as the others.
训练慢:步骤
- 量化:step time、tokens/s、MFU;nvidia-smi / DCGM 看 util 和显存
- profile 几步:
torch.profiler(schedule跳过 warmup)或 Nsight Systems,看 timeline - 按下表归类;再做控制变量:单卡 → 单机 → 多机看 scaling,换 synthetic data 排除 DataLoader
| trace 上看到 | 原因 | 怎么修 |
|---|---|---|
| 每步开头 GPU 空等 | DataLoader | num_workers、pin_memory、预处理离线 |
| kernel 之间密集小空隙 | launch-bound | CUDA Graphs、kernel fusion、更大 batch |
.item() / .cpu() 后的空隙 | implicit sync | 别每步同步,攒几步再取 |
| NCCL kernel 很长、旁边没计算 | comm exposed | nccl-tests 测 busbw;开 overlap;TP 留在机内 |
| 某 rank 总是最后到 collective | straggler | 比各 rank 的 step time,换节点、balance 数据 |
训练 hang:步骤
- init 就卡住:
NCCL_DEBUG=INFO NCCL_DEBUG_SUBSYS=INIT,BOOTSTRAP,NET看 bootstrap 和网卡选择。常见是 NCCL 选错网卡(用NCCL_SOCKET_IFNAME指定)、MASTER_ADDR / 端口不通、防火墙 - 训练中卡住:每个 rank
py-spy dump --pid PID(加--native看 C++ 栈) - flight recorder:
TORCH_NCCL_TRACE_BUFFER_SIZE=2000+TORCH_NCCL_DUMP_ON_TIMEOUT=true,超时时每个 rank dump 最近的 collective,用fr_trace找缺席的 rank - 复现时开
TORCH_DISTRIBUTED_DEBUG=DETAIL:检查每次 collective 的类型和 shape 是否一致 - 设
init_process_group(timeout=...)(NCCL 默认 10 分钟),让 hang 变成报错退出
常见原因:rank 相关的 if 让 collective 顺序或次数不一致。PP send / recv 不配对。某 rank OOM 退出。网络或 PCIe ACS 有问题。
手算例子
- 慢:step 400 ms = 计算 240 + 等数据 120 + 露出的 all-reduce 40。nvidia-smi 显示 70%,因为 NCCL kernel 也算在跑,真正计算只占 60%。
num_workers0 → 8、pin_memory=True后等数据消失 → 280 ms,吞吐 ×1.43。 - hang:8 卡在 step 1200 卡住。py-spy:rank 1–7 停在
loss.backward()的 DDP all-reduce,rank 0 停在save_checkpoint()里的all_gather,因为它写在if rank == 0:里 → collective 不匹配。修法:所有 rank 都调用,或者挪出if。
必答点
- util 只表示"有 kernel 在跑",要看 step time、MFU 和 trace
- profile → 找 GPU 空白 → 分类:data、launch、sync、comm、straggler、kernel 本身
- 控制变量:单卡 / 单机 / 多机,synthetic data,nccl-tests
- hang:py-spy 每个 rank 的栈 + NCCL_DEBUG + flight recorder → 找没进同一 collective 的 rank
- 设 timeout,把无限等待变成可诊断的报错
追问
GPU util 周期性忽高忽低(Tower 题)?
把低谷和代码对齐:每 N 步的 checkpoint / eval,DataLoader 的 buffer 取空再填,或 comm 和计算交替暴露。trace 里看低谷时 CPU 在干什么。
怎么找 straggler?
各 rank 记录 step time,TORCH_NCCL_ENABLE_TIMING=1 记录每个 collective 的耗时。慢的 rank 计算时间长,其他 rank 在 collective 里等得久。
timeout 之后会发生什么?
NCCL backend 的 watchdog 会 abort 这个 collective 并让进程崩溃,因为失败的异步 NCCL 操作可能让后续 CUDA 计算用到坏数据。TORCH_NCCL_ASYNC_ERROR_HANDLING 控制怎么 abort。
来源nvidia-smi docs(Utilization)· torch.profiler(schedule、trace handler)· PyTorch Performance Tuning Guide(num_workers、pin_memory、CUDA Graphs、avoid CPU-GPU sync)· NCCL Logging(Diagnosing Initialization Hangs)、NCCL env(NCCL_DEBUG、NCCL_DEBUG_SUBSYS、NCCL_SOCKET_IFNAME)· nccl-tests · py-spy README(dump、--native)· torch.distributed(init_process_group timeout、TORCH_DISTRIBUTED_DEBUG)、PyTorch NCCL env vars · Flight Recorder tutorial