2. NVSHMEM 开发环境搭建:CUDA Toolkit 与 NVSHMEM 库安装、环境变量配置、编译器选择与编译选项
好,咱们直接进入正题。NVSHMEM 这东西,说白了就是给 GPU 集群用的通信库。你想想看,单卡算力再强,遇到大模型、大规模模拟,也得靠多卡协作。NVSHMEM 就是帮你搞定多卡之间数据交换的利器。
但工具再好,环境搭不对,后面全是坑。我见过太多人花了一周调代码,最后发现是 CUDA 版本和 NVSHMEM 不兼容。嗯,咱们今天就把这事一次性理清楚。
2.1 CUDA Toolkit 安装:版本选择与注意事项
NVSHMEM 对 CUDA 版本有明确要求。我个人习惯是直接上最新稳定版,但如果你在维护老项目,得先查一下兼容性矩阵。
| NVSHMEM 版本 | 最低 CUDA 版本 | 推荐 CUDA 版本 |
|---|---|---|
| 2.x | 10.2 | 11.0+ |
| 3.x | 11.4 | 11.8+ |
| 4.x | 12.0 | 12.2+ |
安装 CUDA Toolkit 其实不复杂。去 NVIDIA 官网下载 runfile 或者 deb 包都行。我建议用 runfile,因为可以自定义安装路径,方便多版本共存。
# 下载 CUDA 12.2 runfile
wget https://developer.download.nvidia.com/compute/cuda/12.2.0/local_installers/cuda_12.2.0_535.54.03_linux.run
# 执行安装,注意不要安装驱动(如果已有)
sudo sh cuda_12.2.0_535.54.03_linux.run --toolkit --silent --override
2.2 NVSHMEM 库安装:源码编译 vs 预编译包
NVSHMEM 有两种安装方式:直接用预编译包,或者从源码编译。我一般推荐新手用预编译包,省事。但如果你需要定制化功能,比如开启 GDRCopy 或者调整某些底层参数,那就得走源码编译了。
方式一:预编译包安装
# 下载 NVSHMEM 预编译包
wget https://developer.download.nvidia.com/compute/nvshmem/4.0.0/local_installers/nvshmem_4.0.0-1_amd64.deb
# 安装
sudo dpkg -i nvshmem_4.0.0-1_amd64.deb
装完之后,库文件默认在 /opt/nvshmem/ 下。你可以检查一下:
ls /opt/nvshmem/lib/
# 应该能看到 libnvshmem.so 和 libnvshmem_host.so
方式二:源码编译安装
源码编译稍微麻烦点,但灵活性高。我在项目中遇到过需要开启 NVSHMEM_USE_GDRCOPY 的场景,预编译包不支持,只能自己编译。
# 克隆源码
git clone https://github.com/NVIDIA/nvshmem.git
cd nvshmem
# 配置编译选项
mkdir build && cd build
cmake .. \
-DCMAKE_INSTALL_PREFIX=/opt/nvshmem \
-DNVSHMEM_USE_GDRCOPY=ON \
-DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda-12.2
# 编译并安装
make -j$(nproc)
sudo make install
-j$(nproc) 能充分利用多核,速度翻倍。我第一次编译时没加,等了快半小时...后来才发现这个参数。
2.3 环境变量配置:让系统找到 NVSHMEM
装完了不代表就能用。你得告诉系统 NVSHMEM 在哪。我习惯把这些配置写到 ~/.bashrc 或者 ~/.bash_profile 里,一劳永逸。
# CUDA 环境变量
export CUDA_HOME=/usr/local/cuda-12.2
export PATH=$CUDA_HOME/bin:$PATH
export LD_LIBRARY_PATH=$CUDA_HOME/lib64:$LD_LIBRARY_PATH
# NVSHMEM 环境变量
export NVSHMEM_HOME=/opt/nvshmem
export LD_LIBRARY_PATH=$NVSHMEM_HOME/lib:$LD_LIBRARY_PATH
export CPATH=$NVSHMEM_HOME/include:$CPATH
配置完记得 source ~/.bashrc 让它生效。然后验证一下:
# 检查 CUDA 是否可用
nvcc --version
# 检查 NVSHMEM 库是否被找到
ldconfig -p | grep nvshmem
如果输出里能看到 libnvshmem.so,那就说明环境变量配对了。
LD_LIBRARY_PATH 写成了 LD_LIBRARY_PATH(少了个 R)...这种低级错误,真的会让人崩溃。
2.4 编译器选择:nvcc 还是 gcc/g++?
NVSHMEM 程序涉及两种代码:GPU 端代码和 CPU 端代码。
- GPU 端代码(.cu 文件):必须用
nvcc编译 - CPU 端代码(.cpp 文件):可以用
gcc/g++或者nvcc
我个人习惯是统一用 nvcc 编译,省得来回切换编译器。但如果你有大型 C++ 项目,用 CMake 管理的话,可以分开指定编译器。
2.5 编译选项:NVSHMEM 程序的编译参数
编译 NVSHMEM 程序时,需要链接 NVSHMEM 库。基本的编译命令如下:
# 单 GPU 程序
nvcc -o my_program my_program.cu \
-I$NVSHMEM_HOME/include \
-L$NVSHMEM_HOME/lib \
-lnvshmem
# 多 GPU 程序(需要 MPI)
nvcc -o my_mpi_program my_mpi_program.cu \
-I$NVSHMEM_HOME/include \
-L$NVSHMEM_HOME/lib \
-lnvshmem \
-I$MPI_HOME/include \
-L$MPI_HOME/lib \
-lmpi
常用的编译选项:
| 选项 | 说明 | 示例 |
|---|---|---|
-arch |
指定 GPU 架构 | -arch=sm_80(A100) |
-lnvshmem |
链接 NVSHMEM 库 | 必须 |
-lnvshmem_host |
链接 NVSHMEM 主机端库 | 某些场景需要 |
-g |
生成调试信息 | 调试时必加 |
-O2 |
优化等级 | 发布时使用 |
-arch 参数,结果程序在 V100 上跑得好好的,换到 A100 上直接报错。原因是默认架构是 sm_70,A100 需要 sm_80。所以,编译时一定要明确指定目标 GPU 架构。
2.6 验证环境:跑一个 Hello World
环境搭好了,咱们跑个简单的程序验证一下。下面是一个 NVSHMEM 的 Hello World:
#include <nvshmem.h>
#include <stdio.h>
int main(void) {
// 初始化 NVSHMEM
nvshmem_init();
int my_pe = nvshmem_my_pe();
int n_pes = nvshmem_n_pes();
printf("Hello from PE %d of %d\n", my_pe, n_pes);
// 最终化 NVSHMEM
nvshmem_finalize();
return 0;
}
编译并运行:
# 编译
nvcc -o hello_nvshmem hello_nvshmem.cu \
-I$NVSHMEM_HOME/include \
-L$NVSHMEM_HOME/lib \
-lnvshmem
# 运行(单进程)
./hello_nvshmem
# 运行(多进程,需要 MPI)
mpirun -np 4 ./hello_nvshmem
如果输出能看到每个 PE 的编号,那就说明环境搭建成功了。
这张图把整个流程串起来了。从 CUDA Toolkit 开始,到 NVSHMEM 库安装,再到环境变量配置,最后是编译运行。每一步都有坑,但按图索骥,基本不会迷路。