简介nccl-tests 是一套面向 GPU 分布式通信开发与性能调优人员的 NCCL 测试工具集基于 CUDA 环境构建用于验证多卡、多进程乃至多节点场景下集合通信操作的正确性与性能表现。包内共 15 个文件以 7 个 .cu 源文件为核心覆盖 all_reduce、all_gather、reduce_scatter、broadcast、alltoall 等典型集合通信算子测试另含 2 个头文件、2 个 Makefile、2 个 Markdown 文档及说明文本压缩包约 27KB结构紧凑、便于直接编译。构建时可通过 make 指定 CUDA_HOME 与 NCCL_HOME若需多节点 MPI 支持设置 MPI1 与 MPI_HOME 即可完成带 MPI 的编译。测试支持多进程、多线程及每线程多 CUDA 设备运行进程数由 MPI 控制。目前已有 4611 人学习下载适合需要评估 NCCL 通信带宽、排查集合通信瓶颈或验证多卡并行正确性的开发者参考使用。1. 从一次多卡训练掉速说起nccl-tests 到底测什么上个月帮一个做分布式训练的团队看问题8 卡 A100 的机器跑 ResNet-50 的 DDP 训练单卡吞吐正常一上多卡就掉到理论值的六成。日志里没有任何报错loss 曲线也正常就是慢。这种问题最难受——没有 crash没有 OOM你连从哪查都不知道。后来我让他们先别动训练代码跑一遍 nccl-tests五分钟就定位到是某两张卡之间的 P2P 带宽只有正常值的三分之一根因是主板 PCIe 拓扑和 BIOS 里一个 ACS 选项没关。这就是 nccl-tests 的价值它不测你的模型不测你的数据管道只测 NCCL 这个集合通信库在你这台机器上到底能跑出多少带宽和延迟。NCCL 是 NVIDIA 多卡通信的底层PyTorch 的 DDP、DeepSpeed、Megatron 全都建在它上面。训练掉速、多卡扩展比不上去、NCCL timeout第一件事就该跑 nccl-tests 把硬件和拓扑的底摸清楚。它适合所有碰多卡的人——不管你是刚拿到机器要验收还是训练跑着跑着怀疑通信有问题。下面按「编译 → 单机测 → 多机测 → 排错」这条线走一遍参数怎么设、结果怎么看、坑在哪都落到具体命令上。2. 编译与拓扑确认先把 NCCL 和 CUDA 的版本对齐2.1 为什么编译这一步最容易翻车nccl-tests 本身是一组 MPI 程序它调用 NCCL 的 API 做 all-reduce、all-gather 这些集合操作再用 CUDA event 计时。所以它同时依赖三样东西CUDA Toolkit、NCCL 库、MPI 实现。这三者的版本必须和你的训练环境一致否则测出来的数字没有参考价值——你测的是另一套运行时。最常见的翻车是 NCCL 版本不匹配。系统里可能装了两个 NCCL一个是 CUDA 自带的一个是 conda 环境里的还有一个是 PyTorch 自带的。编译 nccl-tests 时链接到哪个运行时加载哪个经常不是同一个。我一般会先确认训练用的 PyTorch 到底加载了哪个 NCCL# 看 PyTorch 实际加载的 NCCL 版本和路径 python -c import torch; print(torch.cuda.nccl.version()); import torch._C; print(torch._C._nccl_version()) # 看动态链接库实际解析到哪个 libnccl ldd $(python -c import torch; print(torch.__file__) | xargs dirname)/lib/libtorch_cuda.so | grep nccl第一行打印 NCCL 的版本号一个整数比如 21903 代表 2.19.3第二行用 ldd 看 PyTorch 的 CUDA 库到底链到哪个 libnccl.so。如果这两个指向的路径和你编译 nccl-tests 时用的不是同一个测出来的带宽就不能代表训练时的真实情况。这一步花两分钟能省掉后面几小时的困惑。2.2 编译命令与关键参数确认版本后开始编译。nccl-tests 的 Makefile 支持通过环境变量指定 CUDA 和 NCCL 路径git clone https://github.com/NVIDIA/nccl-tests.git cd nccl-tests # CUDA_HOME 指向你的 CUDA ToolkitNCCL_HOME 指向 NCCL 安装目录 make MPI1 \ MPI_HOME/usr/local/mpi \ CUDA_HOME/usr/local/cuda-12.1 \ NCCL_HOME/usr/local/nccl-2.19 \ NVCC_GENCODE-gencodearchcompute_80,codesm_80 \ -j$(nproc)MPI1打开 MPI 支持多机测试必须开。NVCC_GENCODE指定目标架构A100 是 sm_80H100 是 sm_90写错了编译能过但跑起来会 JIT 重编译第一次运行特别慢容易误判成性能问题。NCCL_HOME一定要指向你训练环境实际用的那个 NCCL不要图省事用系统默认的。编译完在build/目录下会生成一堆可执行文件常用的几个all_reduce_perf、all_gather_perf、broadcast_perf、reduce_scatter_perf、sendrecv_perf。名字就是它测的集合操作_perf后缀表示这是性能测试版本。2.3 跑之前先确认拓扑在测带宽之前先搞清楚你的卡是怎么连的。NCCL 会自动探测拓扑并选择最优路径NVLink、PCIe、还是跨 NUMA 走 QPI/UPI但你需要知道它选了什么才能判断结果是否合理# 看 GPU 拓扑矩阵NV# 表示 NVLinkPIX/PXB 表示 PCIe 不同层级 nvidia-smi topo -m # 看 NCCL 自己探测到的拓扑和它选的算法 NCCL_DEBUGINFO ./build/all_reduce_perf -b 8 -e 128M -f 2 -g 8 21 | head -50nvidia-smi topo -m输出的矩阵里对角线是 X两个 GPU 之间如果是NV18表示 18 条 NVLink 通道PIX表示同一 PCIe switch 下SYS表示跨 NUMA 节点。如果 8 卡机器上出现大量SYS说明卡分属两个 CPU socket跨 socket 通信会走 UPI带宽直接砍半。NCCL_DEBUGINFO会打印 NCCL 选了哪条路径、用了 ring 还是 tree 算法这些信息在结果异常时是判断依据。3. 单机测试把 all_reduce 的带宽和延迟跑明白3.1 核心命令与参数逐项拆解单机测试是最常用的场景一条命令就能跑出从 8 字节到 128MB 消息大小的完整带宽曲线# 8 卡单机 all_reduce消息从 8B 到 128MB每次翻倍跑 20 次取平均 ./build/all_reduce_perf -b 8 -e 128M -f 2 -g 8 -n 20 -w 5 -c 1参数含义逐个说清楚这些参数直接决定结果可不可信-b 8起始消息大小 8 字节测的是小消息延迟。-e 128M结束消息大小 128MB测的是大消息带宽。-f 2步进因子每次消息大小乘 2所以会测 8B、16B、32B……一直到 128MB。-g 8用 8 张 GPU。-n 20每个消息大小迭代 20 次。-w 5前 5 次作为 warmup 不计入结果。这个很关键第一次跑会触发 JIT 编译和显存分配不 warmup 的话小消息延迟会虚高。-c 1开启结果校验每次 all_reduce 后检查数据正确性。调试阶段一定开着性能测试时可以关掉减少干扰。输出是一张表每行一个消息大小列包括time耗时、algbw算法带宽、busbw总线带宽。看结果主要看 busbw不是 algbw。algbw 是消息大小除以时间busbw 是 algbw 乘以一个和算法相关的系数ring all-reduce 是 2*(n-1)/n它反映的是实际物理链路上的流量。8 卡 A100 NVLink 全互联busbw 峰值应该在 200 GB/s 以上如果是 PCIe Gen4 的机器峰值大概在 25-30 GB/s。低于这个数就说明有问题。3.2 结果怎么读三个典型异常拿到结果不要只看峰值要看曲线的形状。三种异常最常见小消息延迟高。8B 消息的 time 如果在 20 微秒以上说明 kernel launch 开销或同步开销过大。常见原因是没 warmup、GPU 处于低功耗状态nvidia-smi -q -d PERFORMANCE看 clocks throttle 原因或者 NCCL 选了不合适的算法。可以试NCCL_ALGORing强制指定算法对比。大消息带宽上不去。128MB 的 busbw 如果只有理论值的一半先查拓扑。用nvidia-smi topo -m确认是不是所有卡都在 NVLink 域内。如果是 PCIe 机器检查NCCL_P2P_DISABLE和NCCL_SHM_DISABLE这两个环境变量有没有被意外设成 1——有些训练框架为了稳定性会关掉 P2P代价就是带宽暴跌。曲线中间有断崖。某个消息大小之后带宽突然掉一截通常是 NCCL 在这个尺寸切换了算法或协议从 LL 切到 LL128 再切到 Simple。这是正常行为但如果断崖位置和你的训练消息大小重合就值得关注。可以用NCCL_DEBUGINFO看切换点再用NCCL_PROTO强制指定协议做对比。3.3 多操作对比别只测 all_reduceDDP 训练里 all_reduce 是主力但张量并行TP用 all_gather 和 reduce_scatter流水线并行用 sendrecv。只测 all_reduce 可能漏掉问题# 依次测四种常用操作消息范围一致便于对比 for op in all_reduce all_gather reduce_scatter broadcast; do echo $op ./build/${op}_perf -b 8 -e 128M -f 2 -g 8 -n 20 -w 5 -c 1 done如果 all_reduce 正常但 all_gather 异常问题可能在 NCCL 的某个特定 kernel 或你的拓扑对某种通信模式不友好。把四个结果放一起对比能快速缩小范围。我一般会把输出重定向到文件用脚本提取每个操作的峰值 busbw 做横向对比比肉眼看表格快得多。4. 多机测试MPI 启动、网卡绑定与跨节点带宽4.1 MPI 启动命令与 hostfile多机测试的复杂度主要来自 MPI 配置和网络。先准备一个 hostfile每行一个节点 IP 和槽位数# hostfile 内容示例每台机器 8 个槽位 cat hostfile EOF 192.168.1.10 slots8 192.168.1.11 slots8 EOF # 用 mpirun 启动2 节点 16 卡 mpirun -np 16 \ --hostfile hostfile \ --allow-run-as-root \ -x NCCL_DEBUGWARN \ -x NCCL_IB_HCAmlx5_0,mlx5_1 \ -x NCCL_SOCKET_IFNAMEbond0 \ ./build/all_reduce_perf -b 8 -e 8G -f 2 -g 1 -n 20 -w 5 -c 1注意-g 1多机场景下每个 MPI rank 管一张卡总卡数由-np决定。-x把环境变量透传给远程进程这是多机调试的关键——你在本地 export 的变量不会自动传到远程节点。NCCL_IB_HCA指定用哪几张 RDMA 网卡NCCL_SOCKET_IFNAME指定 TCP 通信走哪个网口。这两个设错了NCCL 会 fallback 到 TCP socket带宽直接掉一个数量级。4.2 网络路径确认IB 还是 RoCE多机带宽上不去九成是网络路径问题。先确认 NCCL 到底走了 RDMA 还是 TCP# 在 mpirun 里加 NCCL_DEBUGINFO抓取网络初始化日志 mpirun -np 16 --hostfile hostfile -x NCCL_DEBUGINFO \ ./build/all_reduce_perf -b 8 -e 1G -f 2 -g 1 -n 10 -w 3 -c 1 21 | \ grep -E NET/IB|NET/Socket|Using network如果日志里出现NET/IB说明走了 RDMA出现NET/Socket就是 TCP。RoCE 环境下还要确认 GID index 选对了NCCL_IB_GID_INDEX选错会导致 RDMA 建链失败然后静默 fallback 到 TCP。跨节点 all_reduce 的 busbw 在 100Gbps IB 上应该在 10-12 GB/s200Gbps 上 22-25 GB/s400Gbps 上 45-50 GB/s。低于这个范围先查网络别怀疑 GPU。4.3 跨节点测试的常见配置组合多机测试的环境变量组合比较多我整理了一张常用对照表按场景查场景关键环境变量说明InfiniBandNCCL_IB_HCAmlx5_0指定 IB 网卡多张用逗号分隔RoCE v2NCCL_IB_GID_INDEX3GID index 因驱动而异需实测多网卡聚合NCCL_IB_HCAmlx5_0,mlx5_1让 NCCL 同时用多张卡指定 TCP 网口NCCL_SOCKET_IFNAMEbond0避免走错网口导致降速关闭 P2PNCCL_P2P_DISABLE1仅调试用会大幅降带宽强制 RingNCCL_ALGORing对比 Tree 算法差异这张表里的值不是万能公式RoCE 的 GID index 每台机器可能不同用show_gids命令查实际值。多网卡场景下NCCL_IB_HCA写错一个字母NCCL 不会报错只会少用一张网卡带宽减半但你不看日志根本发现不了。5. 避坑与排查那些让结果失真的细节5.1 坑一warmup 不够导致小消息延迟虚高现象8B 消息的 time 显示 50 微秒以上但大消息带宽正常。原因第一次执行 CUDA kernel 有 JIT 编译和 context 初始化开销-w设太小比如 0 或 1时这些开销被计入平均。解决-w至少设 5小消息场景设 10。另外确认 GPU 不在低功耗状态跑之前先用nvidia-smi -q -d PERFORMANCE看 clocks throttle 原因如果是SW Power Cap或HW Slowdown先解决散热和功耗墙。5.2 坑二NCCL 版本和训练环境不一致现象nccl-tests 跑出来带宽很好但训练还是慢。原因编译 nccl-tests 用的 NCCL 和 PyTorch 运行时加载的 NCCL 不是同一个版本两者在算法选择和协议支持上有差异。解决用 2.1 节的ldd方法确认 PyTorch 实际加载的 libnccl 路径编译时NCCL_HOME指向同一个目录。如果 PyTorch 是 pip 装的它自带的 NCCL 在torch/lib/下直接指向那里。5.3 坑三MPI 环境变量没透传现象多机测试时本地设了NCCL_IB_HCA但日志显示走了 TCP。原因mpirun默认不把本地环境变量传到远程节点必须用-x显式指定。解决所有 NCCL 相关的环境变量都加-x。或者用--envlist批量指定。调试时加NCCL_DEBUGINFO确认远程节点实际生效的配置。5.4 坑四GPU 被其他进程占用现象结果波动大同一命令跑两次带宽差 30%。原因机器上有其他训练任务或监控进程占着 GPU显存和 SM 被分走。解决跑之前nvidia-smi确认没有其他进程必要时nvidia-smi --gpu-reset重置。容器环境里注意CUDA_VISIBLE_DEVICES的设置别让 nccl-tests 看到不该看的卡。5.5 坑五消息大小范围设窄了现象只测了 1MB 到 128MB结论是带宽正常但训练还是慢。原因训练中梯度 all_reduce 的消息大小可能只有几十 KB小模型或几 MB大模型如果只测大消息就漏掉了小消息区间的性能问题。解决-b从 8 字节开始-e至少覆盖到模型单次通信的最大消息。不确定的话直接-b 8 -e 1G多花几分钟但覆盖全。6. 进阶用脚本把 nccl-tests 变成日常巡检工具单次跑 nccl-tests 只能看当下状态但硬件性能会漂移——网卡固件升级、驱动更新、BIOS 改配置、甚至机房温度变化都可能影响带宽。我现在的习惯是把 nccl-tests 包成一个巡检脚本每次上机先跑一遍和基线对比。#!/bin/bash # nccl_health_check.sh - 多卡通信健康巡检 # 用法: ./nccl_health_check.sh baseline_file set -e NCCL_TESTS_DIR/opt/nccl-tests/build BASELINE${1:-/opt/nccl-tests/baseline.txt} RESULT/tmp/nccl_result_$(date %Y%m%d_%H%M%S).txt # 固定参数保证每次可比 COMMON_ARGS-b 8 -e 256M -f 2 -g 8 -n 20 -w 10 -c 1 echo all_reduce | tee $RESULT $NCCL_TESTS_DIR/all_reduce_perf $COMMON_ARGS 21 | tee -a $RESULT echo all_gather | tee -a $RESULT $NCCL_TESTS_DIR/all_gather_perf $COMMON_ARGS 21 | tee -a $RESULT # 提取峰值 busbw 和基线对比 extract_peak() { grep -E ^\s[0-9] $1 | awk {print $NF} | sort -rn | head -1 } CURRENT_PEAK$(extract_peak $RESULT) BASELINE_PEAK$(extract_peak $BASELINE 2/dev/null || echo 0) echo 当前峰值 busbw: $CURRENT_PEAK GB/s echo 基线峰值 busbw: $BASELINE_PEAK GB/s # 低于基线 90% 就告警 if [ $(echo $CURRENT_PEAK $BASELINE_PEAK * 0.9 | bc) -eq 1 ]; then echo 警告: 带宽低于基线 90%请检查拓扑和网络 exit 1 fi echo 通信健康检查通过这个脚本的关键点是参数固定。-b 8 -e 256M -f 2 -g 8 -n 20 -w 10这套参数每次都一样结果才有可比性。extract_peak函数从输出里抓最大的 busbw 值和基线文件对比低于 90% 就退出码非零方便接进监控系统。基线文件就是第一次跑出来的结果存下来当参考。用了一段时间后我发现最有价值的不是单次结果而是趋势。有一次巡检发现 all_reduce 峰值从 240 GB/s 慢慢掉到 210 GB/s查了半天是某张卡的 NVLink 降速了nvidia-smi -q看 NVLink 状态发现速率从 25 GT/s 掉到 20 GT/s重启后恢复。这种渐进式劣化不靠定期巡检根本发现不了。从那以后我每次拿到新机器或者改过 BIOS、驱动、固件之后都强制走一遍这个巡检脚本把基线存下来。后面训练出问题先跑巡检对比基线五分钟就能排除或确认通信层的问题不用再靠猜。希望这套流程能帮到你少走一些我踩过的弯路。本文还有配套的精品资源点击获取