编译命令
nvcc 是 NVIDIA 提供的 CUDA C/C++ 的编译器。编译一个简单的 vectorAdd.cu 源代码的命令是:
bash
nvcc vectorAdd.cu -o vectorAdd
这将使用默认参数编译 vectorAdd.cu,并输出一个叫 vectorAdd 的可执行文件。
一些常用的 nvcc 编译参数包括:
-arch=sm_XX
指定 GPU 计算能力-std=c++11
指定 C++ 标准-g
添加调试信息-lineinfo
添加行信息-O2
高级优化-w
抑制 warning-c
只编译不链接链接参数:--lcuda
-lcudart
链接 CUDA runtime 库-lcublas
-lcudnn
链接 CUDA 库-L/path
指定库搜索路径一个常见的完整编译命令可能像:
bash
nvcc -arch=sm_61 -std=c++11 -O2 -lineinfo vectorAdd.cu -o vectorAdd -lcudart -L/cuda/lib64
指定了 GPU 计算能力、C++11 标准、优化等选项,并链接了 CUDA runtime 库。
太复杂了,整个简单的
nvcc 可以直接编译 CUDA 源代码生成可执行文件,同时具有编译和链接的功能,所以不需要额外添加 -c 参数。直接使用:
nvcc matrixMultiply.cu -o exe
nvcc 会进行以下步骤:
1. 用相应的 CUDA 编译器编译 .cu 源文件,生成目标代码
2. 链接目标代码和默认包含的 CUDA 运行时库,生成最终可执行文件所以一个简单的 nvcc 命令就可以完成编译链接,生成可执行文件。
而如果需要分步编译,则需要使用 -c 参数生成独立的目标文件:
nvcc -c matrixMultiply.cu -o matrixMultiply.o
然后再链接目标文件:
nvcc matrixMultiply.o -o exe -lcudart -lcublas
总结一下:
- nvcc 文件 -o exe: 编译+链接,直接生成可执行文件
- nvcc -c 文件 -o obj: 仅编译,生成目标文件
- nvcc obj -o exe lib: 链接目标文件生成可执行文件
nvcc 链接 CUDA 库
1. 必须链接cudart运行时库:
bash
nvcc demo.cu -o demo -lcudart
2. 使用cublas库做矩阵运算:
bash
nvcc demo.cu -o demo -lcublas -lcudart
注意链接顺序,先放CUDA库。
3. 指定自定义的库搜索路径:
bash
nvcc demo.cu -o demo -L/path/to/cuda/lib -lcublas -lcudart
4. 链接指定版本的库:
bash
nvcc demo.cu -o demo -L/cuda-10.1/lib64 -lcublas -lcudart
5. 动态链接库:
bash
nvcc demo.cu -o demo
然后运行时:
bash
LD_LIBRARY_PATH=/cuda/lib ./demo
6. 使用cuDNN库(需要LICENSE):
bash
nvcc demo.cu -o demo -lcudnn -lcublas -lcudart
两种源文件:
对于CUDA程序,我们通常有:
- .cu文件:包含CUDA C/C++代码,包括主程序代码和CUDA kernels。需要nvcc编译。
- .cpp/.c文件:只包含主程序代码,不含CUDA kernels。可以用gcc/g++编译。
编译CUDA程序的一般步骤是:
- 用nvcc编译.cu文件,生成.o文件
- 用g++编译.cpp文件,也生成.o文件
- 再用g++/nvcc链接所有.o文件生成最终可执行文件
nvcc -c demo.cu -o demo.o
g++ -c main.cpp -o main.o
nvcc demo.o main.o -o demo -lcudart
即分别编译.cu和.cpp文件,然后链接生成可执行文件。
使用shell脚本编译
bash
#!/bin/bash
# 1. #! - Shebang的开始标识,告诉系统这是一个可执行脚本。
# 2. /bin/bash - 指定脚本需要使用的解释器路径,在这里是bash shell。
# 3. 将脚本作为参数传递给指定的解释器来执行。
# 也就是说,#!/bin/bash 这一行告诉系统当运行这个脚本时,使用 /bin/bash 这个bash解释器来执行。
# Shebang非常重要,它可以使脚本具备可执行性,并调用正确的解释器。
# 除了`#!/bin/bash`指定bash外,还可以根据需要指定其他解释器,例如:
# - #!/bin/sh - 运行sh shell
# - #!/usr/bin/python - 指定python解释器
# - #!/usr/bin/perl - 指定perl解释器
# 指定编译器
nvcc=nvcc
g++=g++
# 编译参数
cflags="-O2"
cuda_libs="-lcudart -lcublas"
# 源文件
main_file="main.cpp"
kernel_file="kernel.cu"
# 生成的文件
main_o="main.o"
kernel_o="kernel.o"
exe="demo"
# 编译主程序
$g++ $cflags -c $main_file -o $main_o
# 编译CUDA kernel
$nvcc $cflags -c $kernel_file -o $kernel_o
# 链接可执行文件
$g++ $main_o $kernel_o -o $exe $cuda_libs
# 清理
clean(){
rm -f $main_o $kernel_o $exe
}
要运行这个shell脚本,主要有以下几个步骤:
1. 将脚本保存为一个文件,比如取名为 compile.sh
2. 打开终端,切换到脚本所在目录
3. 为脚本添加可执行权限:
chmod +x compile.sh
4. 执行脚本:
./compile.sh
这样就可以运行这个编译CUDA程序的shell脚本了。
5. 清理编译结果运行时加上clean选项可以清理生成的文件:
./compile.sh clean
6. 增加执行权限对编译生成的可执行文件也可以添加执行权限:
chmod +x demo
另外,还可以:
- 通过bash执行: bash compile.sh
- 直接作为可执行文件运行: ./compile.sh
- 参数执行: ./compile.sh clean (执行脚本的clean函数)
所以主要记住以下步骤:
1. 保存为脚本文件
2. additions 权限
3. 在终端通过 ./脚本名 执行
4. 可以加参数执行脚本内特定函数
如果脚本文件不在当前目录,也可以通过完整路径EXECUTEPATH/compile.sh来运行。
自用
nvcc=nvcc
g++=g++
cflags="-O2"
cuda_libs="-lcudart -lcublas"
kernel_files="matrixMultiple.cu a.cu b.cu"
cpp_files="A.cpp B.cpp C.cpp"
kernel_o="matrixMultiple.o a.o b.o"
cpp_o="A.o B.o C.o"
exe=hmw
$g++ $cflags -c $cpp_files -o $cpp_o
$nvcc $cflags -c $kernel_files -o $kernel_o
$nvcc $cflags -o $exe $cpp_o $kernel_o $cuda_libs
#nvcc链接的参数顺序不是非常严格,只要包含了所有必要的选项即可。
#$nvcc $cflags $cpp_o $kernel_o -o $exe $cuda_libs
#所以上面这个也可
简化
bash
#!/bin/bash
set -x
# 设置CPATH指向cudnn头文件目录
OLD_PATH=$CPATH
CPATH=/usr/local/cuda-11.8/include
export CPATH
# 编译参数
cflags="-O2"
cuda_libs="-lcudart -lcublas -lcudnn"
# 源代码目录
#cpp_src_dir=" "
#cuda_src_dir=" "
# 获取源文件列表
#cpp_files=($cpp_src_dir/*.cpp)
#cu_files=($cuda_src_dir/*.cu)
# 源文件数组
cpp_files=(A.cpp B.cpp C.cpp)
cu_files=(matrixMultiply.cu a.cu b.cu)
# 生成对象文件名数组
cpp_objs=${cpp_files[@]/%.cpp/.o}
cu_objs=${cu_files[@]/%.cu/.o}
# 生成可执行文件名字
exe="exec"
# 编译cpp源文件
echo "Compiling C++ source files..."
for f in ${cpp_files[@]}; do
g++ $cflags -c $f -o ${f/%.cpp/.o}
if [ $? -ne 0 ]; then
echo "C++ compile failed. Exiting..."
exit 1
fi
done
# 编译cuda源文件
echo "Compiling CUDA source files..."
for f in ${cu_files[@]}; do
nvcc $cflags -c $f -o ${f/%.cu/.o}
if [ $? -ne 0 ]; then
echo "CUDA compiling failed. Exiting..."
exit 1
fi
done
# 链接可执行文件
echo "Linking final executable..."
nvcc ${cpp_objs[@]} ${cu_objs[@]} -o $exe $cuda_libs
echo "Compile and link success!"
# 恢复原有CPATH
CPATH=$OLD_CPATH
export CPATH