先搞懂为什么一个 GPU 会拖垮全局
你有没有遇到过这种情况:多机训练跑得好好的,突然某个 step 从 0.8 秒变成 8 秒,loss 还在降,但训练速度让人崩溃。我最早以为是我们代码里某个算子写崩了,抓瞎改了一整天。后来才发现,问题根本不在代码——是集群里某个 GPU 掉队了,整个集群都在等它。

分布式训练里,每个 step 结束都要做一次梯度同步。以最常用的 AllReduce 为例,NCCL 的 AllReduce 会让所有 GPU 把梯度聚合成一个平均值再分发回去。这个操作有个硬特性:它必须等所有 Rank 都到了才能开始。最快的 GPU 就算 0.5 秒算完了梯度,也要干等着最慢的 GPU 算完。所以单 GPU 慢 20%,集群有效算力就少了 20%——不是损失一小块,是所有人统一变成最慢那个人的速度。
我最早看掉队,第一反应是看 GPU 利用率。结果发现掉队那张卡的利用率很低,我还以为它闲着。后来才想明白:利用率低恰恰可能是它没拿到新的 batch,因为它上一轮的梯度同步还没结束,下一轮数据早就准备好了,只能干等。所以,第一步永远是用 step 时间找出异常 Rank,而不是凭利用率猜。
定位第一步:记录每个 Rank 的 step 时间,筛出异常者
在训练代码里给每个 step 打点,把时间按 Rank 写到 CSV:
import time
start = time.time()
...
# step 结束时
print(f"rank {rank}, step {i}, time {time.time() - start:.4f}s")
用固定 seed 跑几十个 step,算每个 Rank 的平均 step 时间。同型号 GPU 之间的差距一般小于 5%;如果某个 Rank 比其他 Rank 高 15%–20%,它大概率就是掉队者。这一步的价值是先把“哪台机器”圈出来,避免在正常节点上浪费时间。
第二步:用 profiler 区分“算得慢”和“等得慢”
找到疑似节点后,别急着换卡。在它上面用 torch.profiler 跑一个 step,时间线会直接告诉你慢在哪。
from torch.profiler import profile, ProfilerActivity
with profile(activities=[ProfilerActivity.CUDA]) as prof:
train_step()
print(prof.key_averages().table(sort_by='cuda_time_total'))
关键看两类东西:kernel 本身耗时和kernel 之间的 gap。
- 如果掉队卡上某个 GEMM、AllGather 或 NCCL kernel 时间明显更长,问题在这张卡的执行速度。
- 如果所有 kernel 耗时都正常,但 kernel 之间有大段空白,说明卡在集合通信同步——问题大概率不在 GPU 算力,而在网络或其他 Rank 的延迟。
用 NSight Systems 能看到更细的 NCCL 等待时间。导出 timeline 后,搜索 NCCL 相关的区间,就能看到每次 AllReduce 在等谁。
第三步:用硬件指标和网络测试定位根因
如果 profiler 说是计算慢,接着看硬件指标。在掉队节点和正常节点同时跑一段监测:
nvidia-smi dmon -s pucvmt -d 1
重点看 SM 利用率、当前时钟、温度和功耗。常见的硬件掉队原因有三个:
- GPU 温度到 90°C 以上,SM 时钟被拉低——散热或机房温度问题。
- 功耗被
nvidia-smi -pl设了上限,或者电源/机柜供电不足,导致 Boost 上不去。 - NVLink/PCIe 链路降速,比如 x16 掉到 x8,或者速率代从 Gen4 降到 Gen3。
表格可以直接帮你区分三类现象:
| 现象 | 判断 | 优先处理 |
|---|---|---|
| step 时间长 + kernel 耗时高 | 计算瓶颈(算子慢/降频) | 用 profiler 定位 kernel,检查硬件指标 |
| step 时间长 + kernel 之间 gap 大 | 同步/通信瓶颈 | 看 NCCL 等待,跑 nccl-tests,查网络 |
| GPU 利用率低 + 温度高 | 散热/功率限制 | 清理散热,解除功率上限 |
用 nvidia-smi 文档里的查询命令可以一次把这些字段列全:
nvidia-smi --query-gpu=index,utilization.gpu,clocks.sm,temperature.gpu,power.draw,pcie.link.gen.current,pcie.link.width.current --format=csv
集群规模大时,建议用 DCGM 统一采集这些指标,按时间对齐后,掉队节点的异常比逐台看明显得多。
profiler 里的 gap 大,或者 NCCL 等待时间长,就查网络。IB 链路用 ibstat 看 Active 和速率;以太网用 ethtool <网卡> | grep Speed 看协商速率。再用 nccl-tests 在疑似节点和正常节点之间跑 AllReduce 带宽对比。如果同型号节点的带宽掉了一半,问题大概率在网卡、线缆或交换机。
几个常被问到的坑
为什么同一批同型号 GPU 也会掉队?
我最初也以为只有异构集群才会掉队。后来遇到完全同规格的节点反复变慢,查到最后是它和另一个作业共享同一个网络交换机,对端一跑大流量,它的 AllReduce 就变慢。掉队不一定在 GPU 上,也可能在从 GPU 到交换机的任何一段。
掉队一定是硬件故障吗?
不一定。有时候是拓扑不对:NCCL 默认选的网络路径不是最优的,或者 PCIe 交换机上有其他设备的 DMA 在抢带宽。先跑一次纯通信 benchmark 和一次纯计算 benchmark,能帮你把硬件和软件分开。
偶尔掉队正常吗?
低频率的抖动在共享集群里难免。但如果它周期性出现,或者每次出现都固定在同一个 Rank,就值得认真查了——固定位置往往意味着某个链路或某块卡有隐患。
诊断做到这里,你应该能区分三种情况:计算慢、同步等待、外部干扰。真正难的不是找到慢的那张卡,而是判断它为什么慢。我的经验是:不要相信单指标,比如利用率、温度、NCCL 时间单独看都会骗人。只有把 step 时间、kernel 时间、硬件指标和网络 benchmark 叠在同一条时间线上,掉队的原因才会自己浮出来。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/1895.html