GPU 关键指标汇总:算力、显存、通信
监控
目前我们使用 vGPU 用虚拟GPU 、 dcgm-exporter 和原生prometheus 提供指标。
GPU 显存使用率 (大于 0.9 持续2m)
1 |
|
GPU 利用率
1 |
|
**GPU 温度过高 因散热故障或负载过高导致 ** (大于 85 持续2m)
1 |
|
GPU 功率 为0可能已挂掉 (等于0 持续2m)
1 |
|
**虚拟卡显存使用率 **(大于 0.9 持续2m)
1 |
|
CPU 使用率
1 |
|
内存 使用率
1 |
|
监控中需要包含常见的监控指标,比如 CPU、内存、GPU、网卡、NVLink 等信息。这些指标可以有效辅助定位异常、分析性能。比如:
-
根据 Host 内存、GPU 显存使用量推测 OOM 的风险。
-
根据 GPU Utilization、SM Active、Tensor Active 推测性能:
-
- Util 很低,可以看是否 CPU 相关处理占比过高,没有充分发挥 GPU 算力。
- Util 很高,SM 很低,考虑是否通信占比很高,等待通信完成。
- Util 一直是 100%,而 SM 很低,考虑是不是 NCCL 通信阻塞。(GPU 在执行 NCCL 通信操作时长时间等待资源(如带宽、网络)而无法推进通信,进而导致训练进程挂起、性能下降,甚至死锁。)
- Util 和 SM 很高,Tensor 很低,可以考虑是否模型中非矩阵计算占比过高。
- 如果 Tensor Active 很高,超过 50%,那么大概率任务的吞吐已经很高,比较难太大提升。
-
根据不同 GPU 的指标判断是否存在负载不均。
-
根据 CNP Slow Restart 判断是否有网络拥塞的风险。
- 根据监控指标的趋势识别首先出现异常的 Worker。
SM(Streaming Multiprocessor)– 计算引擎的最小单元
GPU 不是像 CPU 那样一个核心一个核心的,GPU 是由多个 SM 单元(类似多核 CPU 的“核簇”) 组成的。
- 每个 SM 包含:
- 多个 CUDA Core(执行标量运算)FP32、INT32 等普通标量计算
- 多个 Tensor Core(执行张量矩阵运算)只处理 矩阵乘法/加法,高性能,支持 FP16, BF16, TensorFloat32, INT8, FP8 等格式。
- 共享内存、调度器、寄存器等
SM Active 表示 GPU 忙不忙,Tensor Core Active 表示忙的是不是深度学习核心计算。
日志
dmesg 查内核硬件级错误(Xid/OOM),syslog 查系统服务和调度逻辑,kubectl describe 查 K8S 调度状态,是集群诊断的三大利器。
监控指标之外,另一种非常有用的信息是日志,包括但不限于
-
用户日志:用户任务打印的实时日志,比如训练进度信息,关键节点的锚点信息,以及一系列对于追踪任务进度和排查任务有帮助的信息。
-
节点日志:通常可以辅助排查问题,或者查看一些没有在用户日志中体现的信息。常见的方式是使用 dmesg 查看异常信息,比如 GPU 的 Xid Error,Host OOM 导致的进程被 Kill,以及 PCIe Link 降级,网卡抖动/Down 等等。可以重点关注任务异常时间附近的日志信息。除此之外,也可以在 syslog 中查看更细粒度的日志,比如 kubelet 的执行日志,可以查看 Pod 的相关操作记录。
-
操作日志:如果是 K8S 集群,其会包含丰富的 Event 信息,记录了与 Pod 生命周期相关的关键事件。可用于诊断 Pod 的状态变化或异常行为,比如 Pod 的 Start 和 Stop 时间,Pod 是否被驱逐,节点是否异常,Pending 的原因等。
-
插桩日志:为了实现用户无感、更细力度的追踪和监控,通常也会基于 NCCL 或者 eBPF 插桩,以便记录关键的操作或函数调用。比如新版本 NCCL 也专门提供的 NCCL Profiler Plugin ,以便用户更好的从 NCCL 提取性能数据,并将其用于调试和分析。
- GPU OOM 的异常通常不会在 dmesg 信息中,而是在用户侧日志中。
- 我们早期的大规模任务中会基于用户日志是否在持续更新来识别任务是否阻塞(比如 NCCL Hang)。具体来说,当前时间 - 用户日志上次更新时间 > 5min 时发出告警,提示任务存在 Hang 的风险。
1 |
|
syslog 是 Linux 系统的通用日志系统,记录了所有服务、内核、系统调用等事件日志。
系统 | 日志文件路径 |
---|---|
Ubuntu/Debian | /var/log/syslog |
CentOS/RHEL | /var/log/messages |
1 |
|
1 |
|
经验
日志锚点
在日志中添加相关锚点对于定位问题非常有帮助,比如在每个关键逻辑块的起始和终止位置打印相应可识别的日志
比如可以通过 “NCCL INFO Bootstrap” 关键字来判断 NCCL 初始化的开始。通过 “NCCL INFO xxx Init COMPLETE” 关键字来判断 NCCL 初始化完成。
TIPS:基于不同 Worker 中这些日志的时间戳可以大致判断各个 Worker 的执行进度。
可复现性/随机性
随机性是训练中常见的现象,有些是预期中的,有些是预期外的,有时排查问题也需要排除这些随机性。常见的有几类:
- 预处理的随机性:比如 CV 预处理中常见的随机裁剪、旋转等,通常可以固定随机种子来保证确定性。
- 模型算子的随机性:模型底层算子不可避免的会引入一些 非确定性行为(相同输入、模型、代码、环境下,每次运行的结果仍然可能不一样。),可以通过相应的配置尽可能避免这些的问题。
- 二分法简单粗暴,往往是获得最小可复现 Codebase 以便协助排查问题的有效手段
- 对数据/代码分批测试,慢慢收窄范围
- 获得最小可复现代码或数据路径,对定位训练不稳定、随机性问题特别有效
- 随机输入有些时候反而影响排查问题,可以考虑保存真实数据的中间状态作为下一阶段的输入,比如直接保存模型的输入并剥离前后处理以便排查模型问题。
Checkpointing
对于规模比较大或者时间比较长的任务,定时保存 Checkpoint 可以有效避免各种异常导致的无效训练
- 需要考虑保存 Checkpoint 的耗时和存储空间占用。可以通过分布式保存缩短保存时间,或者异步保存等方式减少对训练进度的干扰。
- 如果训练资源规模比较大,那么整个任务因某个 GPU 或节点异常导致失败的概率会非常高,可以适当缩短 Checkpointing 间隔。比如很多公司的大模型预训练会将 Checkpointing 间隔保持在 30min 甚至 5min。
快速识别Worker 异常
- 可以尝试找到监控中首先出现异常的 Worker。通常表现为监控数据首先降为 0,或者没有相应数据。
- 可以使用 PromQL 的 count_over_time 指标来协助快速识别异常 Worker。count_over_time 主要用于计算给定时间区间内某个指标的样本数量,可以帮助分析一段时间内事件的发生次数。
GPU 利用率指标
GPU Utilization
对应 DCGM 的 DCGM_FI_PROF_GR_ENGINE_ACTIVE,表示在一个时间间隔内 Graphics 或 Compute 引擎处于 Active 的时间占比。Active 时间比例越高,意味着 GPU 在该周期内越繁忙。该值比较低表示一定没有充分利用 GPU,比较高通常意味着 GPU 硬件繁忙,但不代表计算效率高(可能等NCCL通信或者memory copy之类,要结合SM 、tensor core 和PCIe/TX/RX 来看)。为什么不用DCGM_FI_DEV_GPU_UTIL
GPU SM Active
对应 DCGM 的 DCGM_FI_PROF_SM_ACTIVE,表示一个时间间隔内,至少一个 Warp 在一个 SM 上处于 Active 的时间占比,该值表示所有 SM 的平均值,对每个 Block 的线程数不敏感。该值比较低表示一定未充分利用 GPU。如下为几种 Case(假设 GPU 包含 N 个 SM):
- Kernel 在整个时间间隔内使用 N 个 Block 运行在所有的 SM 上,对应 100%。
- Kernel 在一个时间间隔内运行了 N/5 个 Block,该值为 20%。
- Kernel 有 N 个 Block,在一个时间间隔内只运行了 1/4 时间,该值为 25%。
GPU SM Occupancy
对应 DCGM 的 DCGM_FI_PROF_SM_OCCUPANCY,表示一个时间间隔内,驻留在 SM 上的 Warp 与该 SM 最大可驻留 Warp 的比例。该值表示一个时间间隔内的所有 SM 的平均值,该值越高也“不一定”代表 GPU 使用率越高。
SM Active / Occupancy 区别
SM Active:代表 GPU SM “有在干活”的时间比例(执行活跃度)
- 判断 GPU 是否被调度;
- 低 → GPU 空转、利用不足;
- 高 → 至少有 warp 在执行(不一定是高效执行)
SM Occupancy:代表 GPU SM “一次能干多少活”的能力(并发能力)
- 衡量的是:一个 SM 被装填得满不满;
- 属于理论并发度/硬件饱和度;
- 并不代表 GPU 正在“干活”;
- 受限于:寄存器数量、shared memory、线程块大小等因素;
用来
- 判断 GPU 是否可以并发更多线程;
- 用于分析 kernel 的并发潜力;
- 通常用于优化 kernel 的 launch 参数(比如 thread/block 设置);
GPU Tensor Active
对应 DCGM 的 DCGM_FI_PROF_PIPE_TENSOR_ACTIVE,表示一个时间间隔内,Tensor Core 处于 Active 的时间占比,该值表示的是平均值,而不是瞬时值。如下所示是几种 Case(假设 GPU 包含 N 个 SM):
- 整个时间间隔内,N/2 个 SM 的 Tensor Core 都以 100% 的利用率运行,该值为 50%。
- 整个时间间隔内,N 个 SM 的 Tensor Core 都以 50% 的利用率运行,该值为 50%。
- 整个时间间隔内,N/2 个 SM 的 Tensor Core 都以 50% 的利用率运行,该值为 25%。
- 整个时间间隔的 80% 时间内,N/2 的 SM 的 Tensor Core 都以 50% 的利用率运行,该值为 20%。
GPU 异常
Xid Error
是 NVIDIA GPU 在运行过程中遇到的一种硬件或驱动层面的错误。Xid Error 通常会出现在 NVIDIA Driver 的日志中,并带有一个特定的错误代码。此错误码可以通过 DCGM 的 DCGM_FI_DEV_XID_ERRORS 获取,表示一段时间内,最后发生的 Xid 错误码。
这些异常通常会在 dmesg 中,可以通过监控 dmesg 日志实现一系列的自动运维机制。
-
31 GPU Memory Page Fault,通常是应用程序的非法地址访问,极小概率是驱动或者硬件问题。
-
在节点侧的日志中体现为 “MMU Fault”、“Fault is of type FAULT_PDE ACCESS_TYPE_VIRT_READ” 等信息;
-
用户侧通常展示为 “CUDA error: an illegal memory access was encountered” 等信息。
-
-
79 表示 GPU has fallen off the bus,意味着 GPU 出现了严重的硬件问题,无法从总线上检测到(掉卡)。
- 在节点侧的日志中经常展示为 “GPU has fallen off the bus”。同时使用 nvidia-smi -L 也可以看到相应的 “Unable to determine the device handle for gpu xxx: Unknown Error”。此问题也可能会伴随着 NVSwitch 的错误(Sxid Error)一起出现,比如出现:“SXid(PCI:xxx): 24007, Fatal, Link 44 sourcetrack timeout error (First)”。
- 用户侧通常会展示为 “RuntimeError: No CUDA GPUs are available” 或 “CUDA error: unknown error”。
- 48/63/94 GPU 出现了不可纠正的 ECC Error,通常是硬件问题,需要终止 GPU 上的相关进程并重置 GPU。也经常会与 63/64 和 94/95 一起出现。
- 在节点侧的日志中经常展示为 “An uncorrectable double bit error” 或 “Row Remapper”;
- 在用户侧通常也会展示相应信息 “CUDA error: uncorrectable ECC error encountered”。
-
109/119/120 GSP RPC Timeout / GSP Error,通常是 GPU 的 GSP(GPU System Processor)组件运行状态异常,也可能会和 Xid Error 109 一起出现。 可以选择关闭 GSP,避免 GSP 出现的一系列问题。使用 nvidia-smi 可以查看是否关闭 GSP。如下图所示,如果有对应版本号则表示开启,如果对应为 N/A 则表示已经关闭
nvidia-smi -a |grep GSP
- 在节点侧的日志中经常展示为 “Timeout after 6s of waiting for RPC response from GPUx GSP!”,与此同时也可能伴随 Xid Error 109;
- 在用户侧的日志中经常展示为 “CUDA error: unspecified launch failure” 等。
当然,并不意味着关闭 GSP 就不会出现 Xid Error 109 的问题,关闭 GSP 后个别情况下还是会有单独出现 Xid Error 109 的情况。在节点侧日志中会有 “errorString CTX SWITCH TIMEOUT” 信息,而在用户侧同样会有 “CUDA error: unspecified launch failure”。
其他异常
PCIe 降速(网卡降速)
PCIe 降速是非常常见但又容易被忽略的问题,其相应的也可能导致网卡降速,影响整体的训练性能。因为只是降低了速度而并不会中断,因此容易被忽略,此时一个正确的性能基线显得尤为重要。
在节点侧日志中通常会展示类似如下信息:“PCIe 16x Link -> 2x Link”,可能还会有相应的速度提示,比如从 252.048 Gb/s 降低到 31.506 Gb/s。
相应的 PCI 总线配置中也可以看到异常网卡的 LnkSta 与正常网卡有明显区别,并且存在 “(downgraded)” 信息。
此时也可能会对应网卡的 CNP Slow Restart 数量增多。CNP Slow Restart 指标可以参考 roce_slow_restart_cnps
你可以先看 dmesg 和 系统日志 是否有提示:
1 |
|
可能会看到类似
1 |
|
或者:
1 |
|
网卡抖动
有些时候网卡也会出现偶尔的抖动,
-
节点侧表现为短暂的 “Link down” 和 “Link up”。
-
与此同时,用户侧的 NCCL 日志用也会出现 “port error” 和 “port active” 信息,如果发生的时间比较短,重试成功后任务会继续执行,也可能会看到 GPU Tensor Active 等指标会有个短暂的下降并快速恢复的过程。
网卡 Down
有些时候网卡会频繁 “Link down” 和 “Link up” 甚至不可用。这种情况往往会导致 NCCL 通信的 Timeout,出现 “Connection closed by localRank”、“Timeout(ms)=1800000”、“Some NCCL operations have failed or timed out” 等信息。
需要说明的是,1800000 timeout 是 NCCL 默认的 30 分钟超时时间。有时为了快速结束异常,可能会缩短 Timeout 间隔,但是太低的间隔也会导致网络抖动直接触发退出。
GPU 降频
GPU 降频也是一个常见的问题,可以通过 GPU SM CLOCK 指标观察,可能由于电源供应不足或者温度过高导致。
其通常会对性能造成极大的影响,比如我们曾遇到过 PSU 问题导致 GPU SM CLOCK 从正常的 2 GHz 降低到 500 MHz,从而导致整个任务的性能降低到原来的 1/4。
此时也可能会出现同一任务中,降频后的 GPU 的 SM Active 指标比较高,而正常 GPU 的 SM Active 比较低。
慢节点
除了 PCIe 降速、GPU 降频会导致训练任务降速之外,慢节点导致降速更难被发现。
比如说,我们在一个训练任务中遇到相同任务的相同配置下,多次启动时训练速度会有一定的差距,MFU 相对差距达到 10% 以上,甚至超过一些优化方案所能带来的收益。最终发现是存在慢节点导致,在排除慢节点后性能非常稳定。
在 MegaScale: Scaling Large Language Model Training to More Than 10,000 GPUs 中也提到过慢节点的问题。当然其也提到有些慢节点不是一直导致任务降速,而是在训练中逐渐降速。
可以使用 Prometheus + Node Exporter 收集 CPU、GPU、PCIe 带宽指标,定位慢节点,但是可能很不显著。也可以结合 “通信时间分布图” (比如 NCCL 中的 send/recv) 来发现潜在的慢节点。
节点 Down
节点 Down 也会导致任务的异常,在 K8S Event 日志中会有 “NodeNotReady” 等信息。随后节点上的任务会被驱逐,也可能会出现 “TaintManagerEviction”。
出现该问题的可能比较多,不过通常比较直观,容易被发现并及时终止任务,相比空转或性能下降要更好。除了在 Event 中有相关信息外,监控机制也会及时发现这类异常并及时上报。
频繁异常节点
相比慢节点而言,故障节点更容易定位,但是频繁故障的节点对于集群整体的有效利用率也是致命的。对于大规模任务而言,频繁故障的节点会明显增加训练任务的中断率,从而导致重启以及未来得及保存 Checkpoint 而出现的计算浪费(故障与上次 Checkpoint 之间的计算)。
Meta 在 [2410.21680] Revisiting Reliability in Large-Scale Machine Learning Research Clusters 中将其称为 Lemon 节点,指那些作业失败率显著高于平均水平的节点。Meta 在集群中通过识别出 Lemon 节点,使得大规模作业(512+ GPU)的失败率降低 10%,从 14% 降低至 4%。
我们在新集群的起始阶段也遇到过类似问题,具体来说,我们发现某些节点的 GPU 频繁出现 Xid Error 而导致任务异常,当将这些节点驱逐后发现任务稳定性明显提升。
性能问题
上述提到的 PCIe 降速(或网卡降速)、GPU 降频都会导致非常明显的训练降速问题,比如可能导致训练任务降速 2-8 倍,如果有明确的基线通常比较容易发现此问题。相对而言,慢节点可能只是稍微影响训练速度,比如 10% 左右,对于大规模训练任务更需要关注,而在小规模任务经常被忽略。
除此之外,也有一些其他容易影响性能的因素,这里简单介绍。
周期性降速
- DataLoader 和 Checkpointing 的问题
- 节点有周期性任务
- CPU、GPU、网络等均未发现明显问题
- 分布式训练中,每个进程(比如每张 GPU 上的一个进程)被称为一个 Rank,某个 Rank 中 Python 的垃圾回收机制会导致一直持有 GIL。如果某个 Rank 执行变慢(例如被 GC 阻塞),那它就会 拖慢整个同步流程,我们称这个慢的 Rank 为 Straggler(拖后腿的节点)
- Megatron-LM 中有主动 GC(Garbage Collect) 在一定的 Step 后所有 Rank 同时主动 GC,这样就可以将所有 Rank 的 GC 放在同一时间,降低对整个任务的影响
调度问题导致降速
按 GPU 的细粒度调度方式也可能会导致性能问题。
我们遇到过一个 8 GPU 任务两次执行速度差一倍的情况,排查后发现其使用了 TP(Tensor Parallelism) + DP(Data Parallelism) 的方式:
- 快的任务:正好调度到 1 台 8 GPU 机器上,可以充分利用 NVLink + NVSwitch 通信,速度很快。
- 慢的任务:被调度到 2 台机器,导致 TP 需要跨机通信,影响整体训练速度。
TP 的通信量比较大,在有条件的情况下都会将 TP 通信限制在一个节点内,充分利用 NVLink + NVSwitch,这也是为什么在大规模训练中往往 TP 的大小不会超过 8。
网络配置导致降速
是否启用 ZRT-RTTCC(具有往返时间拥塞控制的缩放零接触RoCE 技术 )、是否使用流量隔离(DeepSeek 在 A100 Infra 文章和 DeepEP 开源库中都有提到,通过为不同类型流量分配不同的 Service Level,以避免流量之间的干扰)
集群的交付验收阶段通常会有一系列的准入测试,比如使用 ib_write_bw、ib_read_bw、nccl-tests 等工具验证,可以很好的发现和解决类似问题。即使有漏网之鱼,也往往会在交付之后的早期阶段被发现。
任务抢占导致降速
如果采用按 GPU 粒度的调度方式,那么一个节点上的多个 GPU 可能属于不同的任务。此时,如果 GPU 隔离方式不彻底,用户强制修改使用的 GPU 会导致影响被抢占 GPU 对应的任务(比如实际分配了 7 号 GPU,但通过修改配置使用 0 号 GPU)。
例如:我们发现一个多 GPU 的任务性能不符合预期,在查看监控后发现只有一个 GPU 的 SM Active 比较高,其他 GPU 对应的 SM Active 比较低。定位后发现用户强制使用了非分配的 GPU,出现抢占问题,驱逐相应任务后速度恢复正常。
OOM
Shared Memory OOM
训练任务通常需要一定的 Shared Memory 来执行进程间通信,如果 Shared Memory 不足可能会出现问题。
“Bus error: nonexistent physical address” 是其中一种常见的问题。
GPU OOM
GPU OOM 是训练中非常常见的问题,并且由于监控采集存在一定间隔(比如 10s 一个数据点),导致监控中可能无法体现这个问题。
为此,可以尝试从日志中查看,PyTorch 会打印比较详细的 OOM 日志,通常包含 “OutofMemoryError: CUDA out of memory” 信息,并且显示当前进程已经使用了多少显存(“this process has 79.24GiB memory in use”),尝试申请多少显存(“Tried to allocate 3.38 GiB”)等信息
当然,偶尔也会对应 NCCL 的 OOM,对应 “NCCL WARN CUDA failure 2 out of memory” 信息。如果是在保存 Checkpoint 这种明确的位置,可以适当的添加 torch.cuda.empty_cache() 来规避。
用户自身问题导致的 GPU OOM 外,如果隔离不彻底,也会有极小的概率存在资源抢占导致的 OOM。和抢占导致任务降速的原因一样,都是因为某些任务错误使用了非分配的 GPU。
如果任务中某个 GPU 的 GPU_MEM_USED 获取他指标明显高于其他 GPU,并且是任务刚启动就占用比较高,很有可能是有问题的。
CUDNN_STATUS_NOT_INITIALIZED
OOM 并不会输出明确的 out of memory 信息,比如 PyTorch 会采用 lazy Init 的方式初始化 cuDNN。然而,如果 PyTorch 已经使用完所有 GPU 显存,就会导致初始化 cuDNN 时没有足够显存,出现类似 “cuDNN error: CUDNN_STATUS_NOT_INITIALIZED” 的错误。
PyTorch 显存优化
如果使用 PyTorch 训练,当 GPU 显存占用比较高时,可以尝试使用 PYTORCH_CUDA_ALLOC_CONF 环境变量来优化显存的占用,相关文档可以参考 CUDA semantics — PyTorch 2.6 documentation 。
一种比较典型的 Case 是设置 PYTORCH_CUDA_ALLOC_CONF=”expandable_segments:True” 来降低显存碎片化问题。具体来说,对于频繁改变显存分配大小的场景,比如,模型输入 Shape 频繁变化(图像 Shape、序列长度等),此配置允许创建可扩展的 CUDA 内存段,避免因微小分配变化导致的内存碎片问题。当然,此功能可能会影响分配性能。
Host OOM
为了实现按 GPU 细粒度调度,有时会将 Host Memory 按照 GPU 数量平均切分,就可能导致分给每个 GPU 的 Host Memory 不是特别多。此时,
- 对于那些在 Master 中进行全量 Checkpoint 保存或加载的任务可能会出现 OOM。
- 对于加载 Checkpoint 而言,其 Master 进程将整个 Checkpoint 加载到内存,然后切片后传输到相应的 GPU 显存,并释放相应空间;保存 Checkpoint 与其相反。
PyTorch 训练常见问题
PyTorch 初始化端口占用
PyTorch 的分布式训练中,任务初始化阶段 Master 会占用一个端口,以便其他 Worker 与其通信,对应 MASTER_ADDR 和 MASTER_PORT。如果端口已经被占用,则会出现绑定失败的问题。相关环境变量如下所示,可以参考 Distributed communication package - torch.distributed — PyTorch 2.6 documentation
如果未正确使用指定的 MASTER_PORT,则可能出现端口占用的问题,出现 **“The server socket has failed to bind to [:: | ::xxx]”、“Address already in use”** 等信息。 |
在 PyTorch 的分布式训练中,Master 负责绑定端口,其他 Worker 与其建立连接。比如使用 HuggingFace accelerate 启动任务,由于使用方式问题,导致其他 Worker 也去尝试绑定 MASTER_PORT,则会出现 “ConnectionError: Tried to launch distributed communication on port xxx, but another process is utilizing it.” 相关信息。
PyTorch 初始化 Timeout
PyTorch 初始化阶段除了端口被占用外,另一个常见的问题是 “torch.distributed.DistStoreError: Socket Timeout”。此问题通查意味着节点间通信受阻,需逐一排查网络、配置、资源及同步问题。
PyTorch 分布式训练中,起始阶段其他 Worker 与 Master 建立连接的默认超时时间是 300 秒。可以通过 PyTorch 的 dist.init_process_group 来控制
这一问题常见有如下几个原因:
- Pod 启动不同步:如果在启动 Pod 之前还有启动初始化操作,比如巡检或者镜像下载,则可能出现 Pod 启动不同步的问题。
- torchrun 启动不同步:常见原因是 Warmup 阶段不同步,比如所有 Worker 都在调用 torchrun 之前安装依赖包,尤其是需要编译的某些包。则可能由于网络等原因导致完成时间不太统一,启动 torchrun 的时间可能会间隔 5 分钟以上,进而导致上述 socket timeout 的问题。
为了更好的定位此类问题,可以在调用 torchrun 之前打印唯一的、比较明确的日志信息。比如,在调用 torchrun 之前打印了 “RANK: xxx…” 信息,根据这个信息可以推测每个 Worker 调用 torchrun 的时间戳,进而判断启动是否同步,甚至某个 Worker 是否执行到 torchrun。
NCCL 2.20.x 版本 Bug(Message truncated)
我们多次遇到用户使用 PyTorch 训练时出现类似如下的错误,日志中有 “Message truncated” 异常:
这个是 NCCL 2.20.x 版本的 Bug,已经在 2.21+ 版本修复,具体可以参考:[BUG] NCCL2.20.5 meets “Message truncated : received 1024 bytes instead of 256” error while 2.18.5 not · Issue #1273 · NVIDIA/nccl · GitHub
在容器环境下,还可能出现 “Cuda failure ‘invalid device context’” 和 “ncclIUnhandedCudaError: Call to CUDA function failed” 的异常,NCCL 从 2.21 版本开始修复了这个问题,升级 NCCL 版本可以解决。
NCCL Hang & Timeout
训练中 NCCL Hang 住或 Timeout 的问题也是非常常见的问题。NCCL Hang 住的典型特征是 GPU_Util 为 100%,而 GPU_SM_Active 或 GPU_Tensor_Active 指标接近于 0。
NCCL 通信的默认 Timeout 为 30min 中,上述问题通查会在 30min 后异常退出。
NCCL 初始化 Hang 住
NCCL 初始化阶段 Hang 住出现的概率比较低,可能和 nvidia-fabricmanager 组件有关(当然,也可能是其他原因,比如网络环境异常,导致节点无法正确建立 P2P 通信)。Fabric Manager负责配置 NVSwitch 内存结构,以在所有参与的 GPU 之间形成一个统一的内存结构,并监控支持该结构的 NVLink。从较高层次来看,FM 承担以下职责:
- 配置 NVSwitch 端口之间的路由;
- 与 GPU 驱动程序协调,初始化GPU;
- 监控结构中的 NVLink 和 NVSwitch 错误。
NCCL 在 2.17+ 版本开始支持 NVLink Sharp,这个也是在 H100 的 NVSwitch 才支持的。当用户设置 NCCL_ALGO=NVSL 以及 NCCL_NVLS_ENABLE(默认),需要启动对应的 nvidia-fabricmanager。
具体来说,我们发现多机分布式训练时 Pytorch 在初始化节点会 Hang 住,甚至用 NCCL 的 AllReduce 测试也会 Hang,但设置 NCCL_ALGO=Ring 则可以正常执行。最终发现是节点上 nvidia-fabricmanager 异常退出导致的,通过重启 nvidia-fabricmanager 可以解决(有些时候也需要重启机器 NCCL 2.18 / Cuda 12.2 fails on H100 system with transport/nvls.cc:165 NCCL WARN Cuda failure ‘invalid argument’ · Issue #976 · NVIDIA/nccl · GitHub )。
通信操作不 match 导致 NCCL Timeout
PyTorch 训练中出现 NCCL Timeout 很多是因为通信操作不匹配导致的。比如,代码中有逻辑判断,只有一个 Rank[0] 在执行 AllReduce 操作,其他 Rank 都在执行 AllGather 操作,导致通信阻塞并 Timeout。
TIPS:这类问题通常会伴随 “[Rank 0] Watchdog caught collective operation timeout: WorkNCCL(SeqNum=xx, OpType::YYY, Numelln=xxx, NumelOut=xxx, Timeout(ms)=60000)” 日志信息,可以通过所有 Worker 的 OpType 来判断在执行什么通信操作,根据 Numellm 和 NumelOut 判断通信量。
GPU OOM 导致任务通信 Hang
Max 值一直是 100%,而 Min 值一直是 0%。这个问题很可能是个别 Worker 出现 “torch.OutOfMemoryError: CUDA out of memory”,影响了 NCCL 通信,当前 Worker 退出,但是其他 Worker 没有感知到。