1. 多GPU计算环境搭建

多GPU并行计算的第一步不是写kernel,而是让一台机器上的多块GPU真正“可见、可用、可验证”。很多初学者在运行多卡程序时遇到“只识别到一块卡”“device count为0”“两块卡算力不一致导致通信失败”等问题,根源往往不在代码,而在工具链、驱动、运行时或环境变量。本章围绕CUDA工具链安装、多GPU硬件识别、驱动与运行时配置、设备属性查询、环境验证与常见问题排查展开,目标是搭建一个可复现、可诊断的多GPU开发环境。

一、CUDA工具链的组成与安装

CUDA工具链并非单一软件,而是一组协同工作的组件。理解它们的层次关系,是排查问题的前提。最底层是NVIDIA显卡驱动,它负责操作系统与GPU之间的通信;驱动之上是CUDA运行时(cudart),应用程序通过它调用GPU;再往上是CUDA驱动API(cuda driver API),提供更底层的控制;最上层是nvcc编译器、cuBLAS/cuDNN等库以及nsight调试工具。

在Linux上安装CUDA Toolkit,推荐使用官方runfile或包管理器。以Ubuntu为例,使用包管理器安装的典型流程如下:

# 查看系统与显卡
uname -m
lspci | grep -i nvidia

# 添加NVIDIA包仓库并安装
sudo apt update
sudo apt install -y cuda-toolkit-12-4

# 配置环境变量
export PATH=/usr/local/cuda-12.4/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda-12.4/lib64:$LD_LIBRARY_PATH

# 验证编译器
nvcc --version

这里有一个关键点:驱动版本与CUDA Toolkit版本必须匹配。CUDA 12.4要求驱动版本不低于525.60.13(Linux)。如果驱动过旧,即使安装了新Toolkit,运行时也会报“CUDA driver version is insufficient for CUDA runtime version”。因此安装前应先执行 nvidia-smi 查看驱动版本,再决定安装哪个CUDA版本。

建议优先安装驱动,再安装CUDA Toolkit。若使用容器化部署,可只安装驱动,在容器内挂载CUDA Toolkit,避免宿主机环境被污染。

二、多GPU硬件识别

硬件识别是确认“机器上到底有几块GPU、它们是什么型号”的过程。最直接的工具是 nvidia-smi,它会列出所有NVIDIA GPU的编号、型号、显存、温度、功耗和当前占用进程。执行后应重点关注:GPU数量是否与物理插卡一致、每块卡的型号是否相同、显存容量是否一致。

如果 nvidia-smi 只显示一块卡,而机器上确实插了多块,常见原因包括:BIOS中PCIe插槽被禁用、显卡未插紧、供电不足、或者驱动未正确加载。此时可用 lspci | grep -i nvidia 确认操作系统是否在PCI层面看到了所有卡。如果lspci能看到但nvidia-smi看不到,问题通常在驱动或内核模块。

另一个重要工具是 nvidia-smi topo -m,它展示GPU之间的拓扑连接关系,包括是否通过NVLink互联、是否跨PCIe Switch、是否跨NUMA节点。多GPU通信性能高度依赖这个拓扑:同一PCIe Switch下的卡通信延迟低,跨NUMA节点的卡通信延迟高。后续做多卡任务调度时,应优先把通信密集的任务分配到拓扑距离近的GPU上。

三、驱动与运行时配置

驱动加载后,CUDA运行时通过设备文件与GPU通信。Linux下常见设备节点为 /dev/nvidia0、/dev/nvidia1 等,以及控制节点 /dev/nvidiactl。如果这些节点缺失,运行时将无法枚举设备。可通过以下命令检查:

ls -l /dev/nvidia*
# 正常应看到 nvidia0 nvidia1 nvidiactl nvidia-uvm 等

# 若缺失,可尝试重新加载内核模块
sudo modprobe nvidia
sudo modprobe nvidia_uvm

环境变量也会显著影响多GPU行为。CUDA_VISIBLE_DEVICES 用于限制程序可见的GPU集合,例如 CUDA_VISIBLE_DEVICES=0,1 表示只使用前两块卡。这个变量在任务调度中非常有用:调度器可以通过它把不同任务绑定到不同GPU上,避免相互干扰。注意,设置后程序内部的设备编号会重新从0开始映射,例如原本的GPU 2在设置后可能变成逻辑设备0。

此外,CUDA_DEVICE_ORDER 决定设备枚举顺序。默认是 FASTEST_FIRST,按性能排序;设为 PCI_BUS_ID 则按PCI总线顺序排列。在多卡型号不一致的机器上,建议统一设为 PCI_BUS_ID,使设备编号与物理插槽对应,便于运维和排查。

四、设备属性查询与环境验证

确认硬件可见后,还需要在代码层面查询设备属性,判断每块卡是否满足计算需求。CUDA运行时提供 cudaGetDeviceCount、cudaGetDeviceProperties 等接口。下面是一段典型的多GPU属性查询示例:

#include <cstdio>
#include <cuda_runtime.h>

int main() {
    int count = 0;
    cudaGetDeviceCount(&count);
    printf("GPU count = %d\n", count);

    for (int i = 0; i < count; ++i) {
        cudaDeviceProp prop;
        cudaGetDeviceProperties(&prop, i);
        printf("GPU %d: %s, SM %d.%d, SMs=%d, mem=%.1f GB, "
               "clock=%.2f GHz, bus=%d\n",
               i, prop.name, prop.major, prop.minor,
               prop.multiProcessorCount,
               prop.totalGlobalMem / 1073741824.0,
               prop.clockRate / 1e6,
               prop.pciBusID);
    }
    return 0;
}

编译并运行:

nvcc -o query_gpu query_gpu.cu
./query_gpu

假设输出如下(示例数据,非实测):

GPU count = 2
GPU 0: NVIDIA A100-SXM4-40GB, SM 8.0, SMs=108, mem=39.6 GB, clock=1.41 GHz, bus=3
GPU 1: NVIDIA A100-SXM4-40GB, SM 8.0, SMs=108, mem=39.6 GB, clock=1.41 GHz, bus=4

判断环境是否合格,应关注几个关键属性:major/minor 表示计算能力,决定可用的指令集和特性;multiProcessorCount 决定并行度上限;totalGlobalMem 决定单卡可处理的数据规模;pciBusID 用于与拓扑信息对应。如果两块卡的算力或显存差异过大,做数据并行时会出现“木桶效应”,整体性能受最慢卡限制。

多GPU环境验证链路:从硬件到运行时 物理GPU GPU0 / GPU1 PCIe / NVLink 内核驱动 nvidia.ko /dev/nvidia* CUDA运行时 cudart cudaGetDeviceCount 应用程序 多GPU任务 kernel / 通信 验证工具与检查点 lspci | grep nvidia PCI层是否看到所有卡 确认物理插卡 nvidia-smi 驱动版本 / GPU数量 显存 / 温度 / 进程 nvidia-smi topo -m NVLink / PCIe / NUMA 卡间通信距离 cudaGetDeviceProperties 算力 / SM数 / 显存 pciBusID 常见问题与判断依据 只识别到1块卡 lspci可见 → 驱动/模块问题 lspci不可见 → BIOS/插槽/供电 检查 modprobe nvidia 检查 /dev/nvidia* 驱动与运行时版本不匹配 nvidia-smi 查看驱动版本 nvcc --version 查看Toolkit 对照官方版本矩阵 必要时升级驱动 多卡性能不一致 对比算力 major.minor 对比 SM 数与显存 检查 topo -m 拓扑距离 任务按拓扑就近分配

五、常见问题排查思路

环境搭建中最常见的四类问题,可以按“从下到上”的顺序排查。第一类是硬件层:lspci 看不到卡,说明问题在BIOS、PCIe插槽或供电,与CUDA无关。第二类是驱动层:lspci 能看到但 nvidia-smi 报错,通常是内核模块未加载或驱动版本与内核不匹配,可尝试 dmesg | grep -i nvidia 查看内核日志。第三类是运行时层:nvidia-smi 正常但程序报“no CUDA-capable device”,往往是 CUDA_VISIBLE_DEVICES 设置错误或设备节点权限不足。第四类是版本层:程序能跑但报版本不匹配,需要对照NVIDIA官方版本矩阵调整驱动或Toolkit。

不要在同一台机器上混装多个CUDA Toolkit版本而不管理环境变量。多个版本共存时,PATH 和 LD_LIBRARY_PATH 必须指向同一个版本,否则会出现编译用12.4、运行用11.8的隐蔽错误。

完成以上检查后,建议写一个最小验证程序:枚举设备数量、打印每块卡属性、在每块卡上各分配一小块显存并做一次简单kernel调用。如果所有卡都能正确执行且结果一致,说明多GPU环境已经就绪,可以进入后续的多卡任务划分与调度环节。