NCCL多卡通信性能测试:nccl-tests环境搭建与调优实战
简介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 支持并设置 MPI_HOME灵活适配不同环境。测试支持多进程、多线程及每线程多 CUDA 设备同时检查操作性能与结果正确性并附有性能文档辅助解读。目前已有 4611 人学习下载适合作为分布式通信调优与验证的实用参考。1. 从一次多卡训练跑不满带宽说起nccl-tests 到底测什么刚接手一台 8 卡机器的时候我天真地以为只要nvidia-smi能看到 8 张卡torchrun能跑起来多卡通信就没问题。结果一次模拟项目里的 all-reduce 压测直接打脸单卡算力正常一到多卡集合通信带宽只有理论值的三分之一训练脚本卡在同步点上干等。排查了半天才发现问题不在模型代码而在 NCCL 的通信路径和拓扑识别上。这时候能救命的工具就是nccl-tests——NVIDIA 官方那套专门用来压 NCCL 集合通信性能的测试集配合CUDA环境一起装能直接告诉你 all-reduce、all-gather、broadcast 这些操作在你这台机器上到底跑出多少 GB/s、bus bandwidth 是多少、跟理论峰值差多远。这套环境几乎每个做多卡训练、分布式推理的人都会碰到但装不好就是玄学MPI 版本对不上、CUDA 路径没指对、编译出来的 binary 跑起来报libnccl.so not found、多机测试时 hostfile 写错一个字段直接 hang 住。这篇就按我实际部署的顺序把 nccl-tests 从环境确认、依赖安装、编译、单机验证到多机排查整条链路拆开写新手照着能跑通熟手可以对照看版本取舍和参数坑。2. 装之前先把环境对齐CUDA、NCCL、MPI 三件套的版本账nccl-tests 本身代码量不大编译也快真正花时间的是把它依赖的三层环境理顺CUDA 提供 GPU 编程基础NCCL 提供集合通信库MPI 负责多进程启动和跨机通信。这三者版本错一个后面编译或运行就会以各种奇怪方式翻车。我一般会在动手前先把这三样确认清楚写进一个环境清单后面出问题直接对照。2.1 确认 CUDA 版本和驱动匹配先看驱动和 CUDA 运行时。nvidia-smi右上角显示的 CUDA Version 是驱动支持的最高运行时版本不是你实际装的 toolkit 版本这两个经常被搞混。# 查看驱动版本和支持的最高 CUDA 运行时 nvidia-smi # 查看实际安装的 CUDA toolkit 版本 nvcc --version # 查看 CUDA 安装路径后面编译要用 ls -d /usr/local/cuda*逻辑说明nvidia-smi里的 CUDA Version 是驱动能兼容的上限nvcc --version才是你编译时真正用的 toolkit。两者大版本尽量一致比如驱动支持 12.4toolkit 装 12.1 或 12.4 都行但别出现 toolkit 12.6 配老驱动的情况。/usr/local/cuda通常是个软链接指向具体版本目录编译 nccl-tests 时CUDA_HOME要指向它。参数取舍上CUDA 11.x 和 12.x 对 NCCL 的支持差异主要在 NCCL 版本要求NCCL 2.18 以后对 CUDA 12 支持更完整如果你用的是较新的卡比如算力 8.9、9.0 那批建议直接上 CUDA 12.x NCCL 2.19 以上省得遇到算子不支持。老卡跑 CUDA 11.8 也够用别盲目追新。2.2 确认 NCCL 版本和安装方式NCCL 有两种来源一种是 CUDA toolkit 自带的较老一种是单独装的推荐。单独装可以用 apt、可以用 tar 包也可以从源码编。我一般优先用官方 apt 源装版本清晰、升级方便。# 查看当前 NCCL 版本如果已装 dpkg -l | grep nccl # 或者看库文件 ls -l /usr/lib/x86_64-linux-gnu/libnccl* # 用 apt 安装指定版本 NCCL以 2.19 为例具体小版本按官方源 sudo apt install libnccl22.19.* libnccl-dev2.19.*逻辑说明libnccl2是运行时库libnccl-dev是开发头文件和静态库编译 nccl-tests 两个都要。版本号用2.19.*这种通配能匹配到该系列最新小版本。装完libnccl.so会在/usr/lib/x86_64-linux-gnu/下头文件在/usr/include/。常见做法是 NCCL 版本不低于 2.11因为 nccl-tests 里一些新测试项比如alltoall的某些模式需要较新 API。如果你机器上已经有 PyTorch 自带的 NCCL注意别和系统 NCCL 冲突编译时用NCCL_HOME显式指定路径最稳。2.3 MPI 选型OpenMPI 还是 MPICH多机测试必须靠 MPI 拉起多进程。OpenMPI 和 MPICH 都能用我踩过的坑是某些集群默认装了 MPICH但 nccl-tests 的 Makefile 对 OpenMPI 的mpicc包装器兼容更好尤其是跨机--hostfile参数。所以除非集群强制要求我一般选 OpenMPI。# 查看 MPI 版本和实现 mpirun --version which mpicc # Ubuntu 下装 OpenMPI sudo apt install openmpi-bin openmpi-common libopenmpi-dev逻辑说明mpirun --version输出里会写明是 Open MPI 还是 MPICH。mpicc是 MPI 的 C 编译器包装器nccl-tests 编译时用它来链接 MPI 库。装libopenmpi-dev是为了拿到头文件。版本上 OpenMPI 4.x 是主流4.1 以上对多机 RDMA 支持更稳。如果你集群用的是 InfiniBand还要确认libibverbs和rdma-core装了否则 MPI 会退化成 TCP带宽直接砍半。2.4 环境清单对照表动手前把下面这张表填一遍后面出问题能快速定位是哪层不对。组件查看命令期望状态常见问题GPU 驱动nvidia-smi正常显示所有卡卡掉线、驱动版本过低CUDA Toolkitnvcc --version与驱动大版本匹配多版本共存路径混乱NCCLdpkg -l | grep nccl2.11 以上单独安装与 PyTorch 自带冲突MPImpirun --versionOpenMPI 4.xMPICH 兼容性问题RDMAibv_devinfo有活动端口未装 rdma-core提示这张表建议存成文本文件放机器上每次换机器先跑一遍比事后猜快得多。3. 编译 nccl-tests从拉源码到生成可执行文件环境对齐后编译本身不复杂但 Makefile 里的变量和链接顺序有几个容易写错的地方。这一章按实际编译流程走把每个参数为什么这么设讲清楚。3.1 获取源码和目录结构nccl-tests 源码从官方仓库拉注意别拉错分支。主分支对应最新 NCCL如果你系统 NCCL 是 2.19就拉对应 tag 或主分支都行API 兼容性一般没问题。# 拉取源码 git clone https://github.com/NVIDIA/nccl-tests.git cd nccl-tests # 看下目录结构主要关注 src 和 Makefile ls -l逻辑说明源码里src/放各个测试项的 C 文件比如all_reduce.cu、all_gather.cuMakefile是编译入口。根目录还有verifiable/子目录放的是带结果校验的版本调试时很有用。3.2 设置编译环境变量编译前要告诉 Makefile 三件事CUDA 在哪、NCCL 在哪、MPI 用哪个。这三个用环境变量传最灵活。# CUDA 路径指向软链接即可 export CUDA_HOME/usr/local/cuda # NCCL 路径apt 装的通常在系统路径显式指定更稳 export NCCL_HOME/usr # MPI 编译器OpenMPI 装完就是 mpicc export MPI_HOME/usr/lib/x86_64-linux-gnu/openmpi # 把 NCCL 库路径加进链接搜索 export LD_LIBRARY_PATH$NCCL_HOME/lib/x86_64-linux-gnu:$LD_LIBRARY_PATH逻辑说明CUDA_HOME让 Makefile 找到nvcc和 CUDA 头文件NCCL_HOME指向 NCCL 安装前缀apt 装的一般是/usr头文件在/usr/include库在/usr/lib/x86_64-linux-gnuMPI_HOME帮 Makefile 定位mpicc。LD_LIBRARY_PATH是运行时找libnccl.so用的编译时不一定需要但跑测试时必须对。参数取舍如果你 NCCL 是从源码编到/opt/nccl这种自定义路径NCCL_HOME就指过去同时LD_LIBRARY_PATH也要加。别偷懒只设CUDA_HOMENCCL 找不到会报nccl.h: No such file。3.3 执行编译并指定目标架构直接make会编译所有测试项但默认可能不带特定 GPU 架构优化。我一般显式指定算力避免编译出一堆用不上的架构也避免运行时报no kernel image。# 查看 GPU 算力比如 8.0、8.6、9.0 nvidia-smi --query-gpucompute_cap --formatcsv # 编译NVCC_GENCODE 指定目标架构 make MPI1 \ MPI_HOME$MPI_HOME \ CUDA_HOME$CUDA_HOME \ NCCL_HOME$NCCL_HOME \ NVCC_GENCODE-gencodearchcompute_80,codesm_80 \ -j$(nproc)逻辑说明MPI1表示编译 MPI 版本支持多机不设只编单机版。NVCC_GENCODE里compute_80是虚拟架构sm_80是实际架构按你卡填。-j$(nproc)并行编译加速。编译产物在build/目录下每个测试项一个可执行文件比如build/all_reduce_perf。常见做法是如果机器上有多种卡NVCC_GENCODE可以写多个-gencode但会增大 binary。单机型建议只编当前架构。3.4 编译产物和验证编译完先别急着跑多机单机跑一下确认 binary 能起来。# 看编译出了哪些可执行文件 ls build/ # 单机 2 卡快速跑一下 all_reduce数据量小一点 ./build/all_reduce_perf -b 8 -e 128M -f 2 -g 2逻辑说明-b 8起始 8 字节-e 128M到 128MB-f 2每次翻倍-g 2用 2 张卡。这个命令能跑通说明 CUDA、NCCL、MPI 三层都链接正常。输出里会打印每个数据量下的 time、algbw、busbwbusbw 是判断性能的核心指标。如果报libnccl.so.2: cannot open shared object file就是LD_LIBRARY_PATH没设对报mpirun: command not found就是 MPI 没装好或 PATH 没包含。这两个是编译后最常见的翻车点。4. 单机多卡配置参数怎么设、结果怎么看编译通过只是第一步真正要拿到有意义的性能数据得把测试参数和卡数配好。这一章讲单机场景下的参数含义和结果解读多机放到下一章。4.1 核心参数逐个拆all_reduce_perf这类测试项的参数基本一致常用的就那几个但每个都影响结果解读。# 典型单机 8 卡测试命令 ./build/all_reduce_perf \ -b 8 \ # 起始数据量 8 字节 -e 8G \ # 结束数据量 8GB -f 2 \ # 每次翻倍 -g 8 \ # 使用 8 张 GPU -c 1 \ # 校验结果正确性 -n 100 \ # 每个数据量迭代 100 次 -w 20 # 预热 20 次逻辑说明-b和-e决定扫描范围一般从几字节扫到几 GB能看出小包延迟和大包带宽两个维度。-f 2是倍增因子2 表示每次翻倍也可以设 4 减少测试点。-g是 GPU 数单机 8 卡就写 8。-c 1开启结果校验会对比 GPU 间计算结果是否一致调试阶段建议开压测阶段可以关掉省时间。-n和-w是迭代和预热次数预热是为了让 GPU 频率和通信路径进入稳定状态不预热的数据会偏低。参数取舍-n设太小比如 10结果波动大设太大比如 1000测试时间长。我一般正式压测用 100快速验证用 20。-w至少 5最好 20尤其是新机器第一次跑。4.2 结果字段解读跑完输出是一张表字段不多但每个都有用。字段含义关注点size数据量看扫描范围是否覆盖小包和大包count元素个数与 size 对应type数据类型float/int8 等影响带宽redop归约操作sum/prod 等time耗时单位 us越小越好algbw算法带宽数据量/时间反映应用层busbw总线带宽考虑通信模式后的实际带宽#wrong错误数开了 -c 才有必须为 0逻辑说明algbw是简单除法busbw才是判断硬件性能的关键。以 all-reduce 为例busbw algbw × 2 × (n-1)/nn 是卡数。8 卡时 busbw 约等于 algbw × 1.75。看结果主要看大包比如 1GB 以上的 busbw 是否接近硬件理论带宽比如 NVLink 机器 8 卡 all-reduce busbw 能到 200GB/s 以上算正常PCIe 机器会低很多。如果#wrong不为 0说明通信结果错了通常是 NCCL 版本和驱动不匹配或者拓扑识别有问题先别管性能把正确性解决。4.3 拓扑和 NCCL 调试变量单机多卡性能上不去八成是拓扑识别问题。NCCL 有一套调试环境变量能看到它实际走了哪条路径。# 开启 NCCL 调试信息看拓扑和通道 export NCCL_DEBUGINFO export NCCL_DEBUG_SUBSYSINIT,GRAPH # 再跑一次小规模测试 ./build/all_reduce_perf -b 8 -e 128M -f 2 -g 8逻辑说明NCCL_DEBUGINFO会打印 NCCL 初始化时的拓扑信息包括识别到几张卡、卡之间是 NVLink 还是 PCIe、用了几个 channel。NCCL_DEBUG_SUBSYS可以只开关心的子系统避免输出太多。看输出里Ring还是Tree算法被选中以及NVLS是否启用。常见做法是如果发现卡间走的是 PCIe 而不是 NVLink检查nvidia-smi topo -m输出的拓扑矩阵看是不是硬件连接问题或者被NCCL_P2P_DISABLE之类的变量禁用了。我一般会把这些调试变量写进一个env.sh测试前 source 一下省得每次敲。5. 多机部署避坑hostfile、网络和那些让人 hang 住的坑单机跑通后多机才是真正的考验。多机涉及 SSH 免密、hostfile 格式、网络接口选择、防火墙任何一环不对就是 hang 住或者报连接错误。这一章按我踩过的顺序列几条每条按现象、原因、解决写。5.1 现象mpirun 卡住不输出也不报错原因最常见的是 SSH 免密没配好或者 hostfile 里的主机名解析不了。mpirun 在等 SSH 连接但连接建立不起来就干等。解决先手动ssh 目标主机确认免密能通再检查 hostfile 里写的是 IP 还是主机名主机名要在/etc/hosts里能解析。hostfile 格式是每行一个主机名后面跟slotsN指定进程数。# hostfile 示例两台机器各 8 卡 cat hostfile # node1 slots8 # node2 slots8 # 手动测 SSH ssh node1 hostname ssh node2 hostname逻辑说明slots8表示这台机器起 8 个进程对应 8 张卡。如果只写主机名不写 slotsmpirun 默认按 CPU 核数起进程会起一堆用不上的。SSH 测试必须每台都通包括从自己到自己。5.2 现象报No route to host或连接超时原因多机通信没走对网络接口。机器上可能有多张网卡管理网、计算网MPI 默认选了管理网而管理网不通或者带宽低。解决显式指定网络接口。OpenMPI 用--mca btl_tcp_if_include指定网卡名或者用--mca oob_tcp_if_include。# 查看网卡名 ip addr show # 指定用 ib0 或 eth1 这类计算网卡 mpirun --hostfile hostfile \ --mca btl_tcp_if_include ib0 \ --mca oob_tcp_if_include ib0 \ -np 16 \ ./build/all_reduce_perf -b 8 -e 8G -f 2 -g 1逻辑说明btl_tcp_if_include控制 MPI 点对点通信走哪张网卡oob_tcp_if_include控制带外管理通信。有 InfiniBand 就用ib0没有就用计算网对应的eth。-np 16是总进程数两台 8 卡就是 16-g 1表示每个进程用 1 张卡。参数取舍如果集群有 RDMA优先让 MPI 走 RDMA 而不是 TCP需要装openmpi的openib组件并用--mca btl_openib_allow_ib 1。走 TCP 的话带宽受网卡限制千兆网跑多机 all-reduce 基本没意义。5.3 现象多机跑起来但 busbw 远低于单机原因跨机通信走了慢速网络或者 NCCL 没启用 RDMA。NCCL 跨机默认会尝试用 IB但如果libibverbs没装好或者权限不对会静默退化成 TCP。解决开NCCL_DEBUGINFO看跨机通道用的是NET/IB还是NET/Socket。如果是 Socket检查ibv_devinfo是否有活动端口以及 NCCL 是否有权限访问 RDMA 设备。# 检查 RDMA 设备 ibv_devinfo # 强制 NCCL 用 IB并打印网络信息 export NCCL_DEBUGINFO export NCCL_IB_DISABLE0 export NCCL_NET_GDR_LEVEL2 mpirun --hostfile hostfile -np 16 ./build/all_reduce_perf -b 8 -e 8G -f 2 -g 1逻辑说明NCCL_IB_DISABLE0确保不禁用 IBNCCL_NET_GDR_LEVEL2启用 GPU Direct RDMA让数据直接从 GPU 显存走网卡省一次内存拷贝。看输出里NET/IB出现才算走对。5.4 现象结果校验#wrong不为 0原因多机场景下数据不一致可能是某台机器 NCCL 版本不同或者 GPU 架构不同导致 kernel 行为差异。解决先确认所有机器 NCCL 版本一致dpkg -l | grep nccl逐台对。再确认 CUDA 版本一致。如果机器混用不同架构的卡编译时NVCC_GENCODE要包含所有架构。# 逐台检查版本 for h in node1 node2; do echo $h ssh $h dpkg -l | grep nccl; nvcc --version | tail -1 done逻辑说明多机环境最忌讳版本不一致NCCL 小版本差异都可能导致集合通信结果不同。这个循环能一次性把两台机器的版本打出来对比。发现不一致就统一升级或降级到同一版本。5.5 现象跑一会儿报unhandled system error或进程被杀原因通常是显存不够或者超时。测试数据量设太大比如-e 8G在显存小的卡上或者 NCCL 超时时间太短大包测试来不及完成。解决调小结束数据量或者加大 NCCL 超时。显存不够就降-e超时就设NCCL_TIMEOUT。# 显存小的卡结束数据量降到 1G ./build/all_reduce_perf -b 8 -e 1G -f 2 -g 8 # 加大超时单位秒 export NCCL_TIMEOUT1800逻辑说明-e 1G表示最大测到 1GB 数据量8 卡 all-reduce 时每卡要存一份显存占用约 1GB 加上通信 buffer。NCCL_TIMEOUT默认值较小大集群或慢网络下容易超时调到 1800 秒能覆盖大部分场景。注意多机测试前一定先单机跑通单机不过就上多机问题会混在一起排查成本翻倍。6. 装完怎么验证跑通一套可复现的检查流程环境装完、编译过了、单机多机都跑起来了最后一步是确认这套环境真的稳定可用而不是碰巧跑通一次。我一般会走一套固定检查流程从版本一致性到性能基线每步都有明确的通过标准。这套流程跑完后面再出问题就能快速判断是环境退化还是新引入的变量。6.1 版本一致性检查多机环境第一步永远是版本对齐。写个脚本一次性收集所有机器的关键版本输出成表格对比。#!/bin/bash # check_env.sh在所有机器上跑一遍收集版本 HOSTSnode1 node2 for h in $HOSTS; do echo $h ssh $h echo -n driver: ; nvidia-smi --query-gpudriver_version --formatcsv,noheader | head -1 echo -n cuda: ; nvcc --version | grep release | awk {print \$6} echo -n nccl: ; dpkg -l | grep libnccl2 | awk {print \$3} echo -n mpi: ; mpirun --version | head -1 echo -n gpu_count: ; nvidia-smi -L | wc -l done逻辑说明这个脚本把驱动、CUDA、NCCL、MPI、GPU 数量五个关键信息逐台打印。通过标准是所有机器的 CUDA 和 NCCL 版本完全一致驱动大版本一致GPU 数量符合预期。任何一项不一致先解决再往下走。6.2 单机性能基线版本对齐后先在单机跑一个标准测试记录 busbw 作为基线。以后每次环境变动都重跑对比。# 单机 8 卡 all-reduce 基线测试 ./build/all_reduce_perf -b 8 -e 8G -f 2 -g 8 -n 100 -w 20 -c 1 # 把输出存下来方便对比 ./build/all_reduce_perf -b 8 -e 8G -f 2 -g 8 -n 100 -w 20 -c 1 baseline_single.txt 21逻辑说明-c 1开启校验基线测试必须保证正确性。输出存文件是为了后续 diff。通过标准#wrong全为 0大包1GB 以上busbw 达到硬件理论值的 70% 以上。NVLink 机器 8 卡 all-reduce busbw 一般能到 200GB/s 以上PCIe 机器看具体代数PCIe 4.0 x16 大概 20-30GB/s。6.3 多机性能基线单机基线正常后跑多机基线。多机重点看跨机带宽和扩展效率。# 多机 16 卡 all-reduce每进程 1 卡 mpirun --hostfile hostfile \ --mca btl_tcp_if_include ib0 \ --mca oob_tcp_if_include ib0 \ -np 16 \ ./build/all_reduce_perf -b 8 -e 8G -f 2 -g 1 -n 100 -w 20 -c 1 \ baseline_multi.txt 21逻辑说明-np 16对应两台 8 卡机器-g 1每进程一张卡。通过标准#wrong为 0跨机大包 busbw 达到网络理论带宽的 60% 以上。InfiniBand HDR 单口 200Gb/s理论 25GB/s实际能到 20GB/s 以上算正常。如果远低于这个值回去看第 5 章的 RDMA 排查。6.4 常见验证失败对照失败现象优先检查快速命令编译报 nccl.h 找不到NCCL_HOME 和头文件路径ls $NCCL_HOME/include/nccl.h运行报 libnccl.so 找不到LD_LIBRARY_PATHldd build/all_reduce_perf | grep nccl单机 busbw 低拓扑和 P2Pnvidia-smi topo -m多机 hangSSH 和 hostfilessh node2 hostname多机 busbw 低RDMA 是否启用NCCL_DEBUGINFO看 NET/IB#wrong 不为 0版本一致性跑 6.1 的检查脚本逻辑说明这张表把前面几章的排查点汇总出问题先按现象查对应行。ldd能直接看出 binary 链接的libnccl.so路径对不对比猜快。6.5 我自己的固定习惯从那以后我每次换机器或者升级环境都强制走一遍这套流程先跑版本检查脚本再跑单机基线最后跑多机基线三份输出存成带日期的文件。有一次升级 NCCL 小版本后单机正常多机 busbw 掉了 30%对比基线文件才发现是跨机通道从 IB 退成了 Socket回滚版本就好了。要是没存基线这种退化根本发现不了。这套流程花不了十分钟但能省掉后面几小时的玄学排查。希望帮到你。本文还有配套的精品资源点击获取