CUDA学习笔记-基于Cmake环境搭建
本文记录在windows10-WSL2-Ubuntu22.04.3 RTX3060 环境下安装CUDA,并使用Clion-Cmake创建CUDA工程,同时在Jetson Orin Nano上进行开发的过程。
环境信息
- 系统:Windows10-WSL2-Ubuntu22.04.3
- 显卡:RTX3060-Driver566.07-CUDA12.7
- 软件:Clion+捆绑的Cmake
- CUDA Toolkit 12.6.3
本文默认WSL2系统和Clion都已经安装完毕。
CUDA安装
Windows的显卡驱动已经完成支持WSL2,因此无需在WSL中安装驱动,建议在安装前对显卡驱动进行更新。
检查支持的CUDA版本
在WSL中输入以下命令,查看CUDA Version项:
$ nvidia-smi
此处为版本为12.7,说明当前驱动最高支持到该版本的cuda,需要安装<=12.7版本的CUDA Toolkit,这里安装CUDA Toolkit 12.6.3。
下载CUDA
在这个网站中可以找到各种版本的CUDA,下载CUDA Toolkit 12.6.3。
选择Linux->X86_64->WSL-Ubuntu->2.0->deb(network),根据指令下载:
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt-get update
sudo apt-get -y install cuda-toolkit-12-6
添加环境变量
安装CUDA不会安装到系统搜索路径中,需要手动添加环境变量。临时变量可以直接执行以下命令,永久添加自行搜索。当然也可以在Cmake中指定。
export PATH=/usr/local/cuda/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
第一行添加可执行程序如nvcc的地址,第二行添加链接库的地址,如果是32位系统将lib64改为lib32。
输入命令验证是否安装成功。
nvcc --version
使用CMake搭建一个CUDA工程
工程目录
├── culib //cu库
│ ├── SARlib //静态链接库
│ │ ├── include
│ │ │ └── sar.cuh
│ │ └── libSARlib.a
│ └── bitlib //代码库
│ ├── include
│ │ └── bit.cuh
│ └── bit.cu
├── include //用户代码的include
│ └── myinclude.h
├── lib //c/c++库
│ └── mylib
│ ├── include
│ │ └── mylib.h
│ └── mylib.c
├── main.c //主程序
└── user //用户代码
└── add.c
在culib下有多个文件夹,均为cuda编写的库,每个文件夹中有一个include文件夹包含头文件,还会有其他.cu文件或者.a文件。
同样的,lib文件夹下也是这样,但为C++编写的库。
include文件夹为自己程序的头文件。
user文件夹为自己程序的源文件。
在进行编译时,需要include根目录下的include文件夹,以及各个库文件夹下面的include文件夹,需要链接各个库文件夹中的.a文件,将各个库文件夹中的代码编译成静态链接库并连接到主程序。除此之外,cu库文件和自己的程序都还要使用cuda的库。
CMakeLists.txt编写
普通代码库的添加
对普通源码库的添加,使用add_library()实现,在lib/mylib下创建CMakeLists.txt文件,写入以下代码:
add_library(mylib STATIC "${CMAKE_CURRENT_SOURCE_DIR}/mylib.cpp")
target_include_directories(mylib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inlcude)
上面的代码将mylib文件夹下的mylib.cpp以及其include文件夹组成了一个mylib库目标,其他目标可以通过target_link_libraries()指令链接到该库,同时获得该库目标的代码和PUBLIC的include路径。
CUDA代码库的添加
相对于普通代码库,CUDA代码库需要依赖一些CUDA组件,在普通代码库的基础上需要添加依赖,对于bitlib库,使用了cudaruntime环境,在其CMakeLists.txt最后一行添加:
target_link_libraries(bitlib CUDA::cudart)
CUDA::cudart在后文解释。
静态链接库的添加
有一些库只提供了编译好的.a文件,如上面的libSARlib.a,对于这种库,可以使用Cmake的IMPORTED Target特性创建一个库目标。在SARlib文件夹下创建CMakeLists.txt,写入:
add_library(SARlib STATIC IMPORTED GLOBAL)
set_target_properties(SARlib PROPERTIES
IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libSARlib.a
INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include)
上面指定SARlib为一个导入的目标,无需再编译,直接使用即可。同时利用set_target_properties()设置IMPORTED_LOCATION指定位置,INTERFACE_INCLUDE_DIRECTORIES指定接口头文件。
接口头文件还可以使用target_include_directories(SARlib INTERFACE xxx)来指定,与普通头文件的不同在于,只起到暴露接口的作用,不参与编译(因为已经编译好了)。
注意,一定要添加GLOBAL属性,不然其他文件夹的CMake会找不到这个库。
根目录CMAKE
根目录的Cmake即工程的Cmake,写入如下代码:
cmake_minimum_required(VERSION 3.22) #从3.11开始支持cuda
# 添加 CUDA 环境变量,这里添加CUDA安装章节的环境变量(如果修改过环境变量文件则不用)
list(APPEND CMAKE_PROGRAM_PATH "/usr/local/cuda/bin")
list(APPEND CMAKE_LIBRARY_PATH "/usr/local/cuda/lib64")
project(addtest)
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall") # 开启全部警报,便于调试
SET(CMAKE_CUDA_ARCHITECTURES native) # 设置cuda编译器自行决定显卡架构
enable_language(CXX)
enable_language(CUDA)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CUDA_STANDARD 11)
# find_package是官方准备好的一些查找函数
find_package(CUDAToolkit REQUIRED)
IF(CUDAToolkit_FOUND)
message(STATUS "found CUDA")
message(STATUS " version:${CUDAToolkit_VERSION}")
else(CUDAToolkit_FOUND)
message(STATUS "CUDA is not found.")
endif(CUDAToolkit_FOUND)
set(LIBS mylib SARlib bitlib CUDA::cudart CUDA::cufft)
add_subdirectory(culib/bitlib)
add_subdirectory(culib/SARlib)
add_subdirectory(lib/mylib)
add_executable(${PROJECT_NAME})
target_sources(${PROJECT_NAME} PRIVATE
main.cpp
"${PROJECT_SOURCE_DIR}/user/add.cpp")
target_include_directories(${PROJECT_NAME} PRIVATE "${PROJECT_SOURCE_DIR}/include")
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS})
首先需要设置cmake_minimum_required() 的最小版本,这个根据使用的Cmake特性来进行设定。
然后是设置工程名并设置语言和标准:
project(addtest) # 工程名为addtest
enable_language(CXX) # 使用C++
enable_language(CUDA) # 使用CUDA
set(CMAKE_CXX_STANDARD 11) # 使用C++11
set(CMAKE_CUDA_STANDARD 11) # 使用cuda C11
然后是找到CUDA库,对于正常安装CUDA的电脑,Cmake官方已经支持了对CUDA库的查找,可以使用CMAKE官方的提供的查找函数find_package()查找CUDAToolkit,使用REQUIRED强制依赖,使用CUDAToolkit_FOUND判断是否找到。关于官方支持的查找库可以到这里查找。
find_package(CUDAToolkit REQUIRED)
该函数执行后,会引入一些库目标例如CUDA::cudart(CUDA Runtime Library) CUDA::cufft(FFT函数),根据使用的库来链接这些目标。
然后需要将其他含有CMakeLists.txt的文件夹添加进来参加构建,使用add_subdirectory()命令。
之后,创建最终可执行文件的目标:
add_executable(${PROJECT_NAME})
将user文件夹下的代码通过target_sources()添加进来,同时添加include目录:
target_sources(${PROJECT_NAME} PRIVATE
main.cpp
"${PROJECT_SOURCE_DIR}/user/add.cpp")
target_include_directories(${PROJECT_NAME} PRIVATE "${PROJECT_SOURCE_DIR}/include")
最后,将库文件目标链接到程序文件:
target_link_libraries(${PROJECT_NAME} PRIVATE ${LIBS})
使用该方式,目标会继承库目标的PUBLIC和INTERFACE include路径,不用再手动添加。
之后创建build文件夹进入,执行命令:
cmake ..
make
如果要区分Dubug和Release:
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake -DCMAKE_BUILD_TYPE=Debug ..
Cmake的语法规则非常灵活(乱的一批),对于特定的工程结构,建议问一下GPT让它帮你生成构建代码。
CUDA的分离编译,在.cu文件调用其他.cu的核函数时使用,否则不会生成可重定位设备代码,仅允许单文件内的设备代码调用。
set_property(TARGET your_target PROPERTY CUDA_SEPARABLE_COMPILATION ON) #单目标开启 set(CMAKE_CUDA_SEPARABLE_COMPILATION ON)# 全局开启
如果想使用其他编译器
# 在project()前使用 set(CMAKE_CUDA_HOST_COMPILER "/path/to/compiler") set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -ccbin /path/to/compiler")
如果想添加编译选项
set(CMAKE_CUDA_FLAGS "-Wall") # 全局 target_compile_options(my_target PRIVATE $<$<COMPILE_LANGUAGE:CUDA>:--generate-line-info>) # 单独
使用Vscode Remote
Vscode环境搭建非常简单,首先安装以下插件:
- WSL——微软的WSL支持链接到WSL
- C/C++ Extension Pack——微软的C/C++支持插件
- Nsight Visual Studio Code Edition——英伟达的CUDA开发组件
- CMake Tools——微软的Cmake支持插件
到这里,程序编写编译运行都没有问题了,如果想要Debug,还需要进一步设置。
根据debug入口的不同有多种设置方法,这里记录Nsight Visual Studio Code Edition官方推荐推荐的方法,在该插件的介绍页有动图展示。
我们这里直接点击Vscode右侧的Debugger,点击create launch.json,在弹出的文件中编辑"program"项,指定可执行文件,然后就可以直接debugger了。
从Cmake入口修改(将对所有工程生效)
见这里
在cmake工具设置中的Debug设置中,添加"miDebuggerPath":"/usr/local/cuda/bin/cuda-gdb"
使用Clion Remote
环境也十分简单,直接通过CLion的WSL支持,打开上面CMake工程即可。
附录-Armadillo库的安装使用
Armadillo是一个高效的线代库,并且整个库的API风格类似于Matlab,方便快速将Matlab等科研代码转换成C++代码,这在科研中十分友好。并且还默认自动使用OpenMP等并行加速手段进行加速,在运行效率上也会有一定的保障。
安装依赖
Armadillo依赖于OpenBLAS和LAPACK,需要预先安装好相关组件。顺带一提,这两个东西一个是矩阵的初级运算库一个是高级运算库,涉及到计算机数值计算领域。
sudo apt install libopenblas-dev liblapack-dev libarpack2-dev libsuperlu-dev
除此之外,库的编译还需要CMAKE,也需要安装。
下载源码编译安装
可以通过apt直接下载软件包,但这不能保证最新版本。
sudo apt install libarmadillo-dev
在这个界面获得最新稳定版本的下载地址,此处为14.4.0版本。
输入命令下载解压:
wget https://sourceforge.net/projects/arma/files/armadillo-14.4.0.tar.xz
tar -xvf armadillo-14.4.0.tar.xz
armadillo是一个标准Cmake工程,可以直接进入文件后执行以下命令生成Makefile:
mkdir build
cd build
cmake ..
之后输入命令进行安装:
make
sudo make install
之后在Cmake中使用find_package()即可添加库文件:
#armadillo
find_package(Armadillo REQUIRED)
if(Armadillo_FOUND)
message(STATUS "Armadillo_INCLUDE_DIR:" ${ARMADILLO_INCLUDE_DIR})
message(STATUS "Armadillo_LIBRARY:" ${ARMADILLO_LIBRARY})
include_directories(${ARMADILLO_INCLUDE_DIR})
else(Armadillo_FOUND)
message(FATAL_ERROR "Armadillo not found")
endif(Armadillo_FOUND)
add_executable(${PROJECT_NAME})
target_link_libraries(${PROJECT_NAME} PRIVATE ${ARMADILLO_LIBRARY})
find_package()详细信息在Cmake官网查看:FindArmadillo
Armadillo的使用教程可以在官网查看:arma docs
附录-低版本Cmake中的CUDA
FindCUDAToolkit于cmake3.17版本加入,但是5.1.3的Jetpack中的cmake还是3.16,刚好用不了。
在较低版本中(Version ❤️.17;Version >3.10),不需要查找库,直接在project(xxx CUDA)或者enable_language(CUDA)即可,cmake会自动找CUDA相关库。
对上面的文件修改,只需要删除find_package()相关代码和连接库中的CUDA::xxx即可。
该方法只会为.cu文件添加CUDA库,请不要在普通.CPP文件中涉及CUDA相关代码。
针对CPP和CUDA混合使用的奇怪代码,建议全部改为.cu文件。否则,使用更低版本的FindCUDA方法。
对更低版本,使用FindCUDA,这里只给出一个简单的示例(一点也不合规的方法):
find_package(CUDA REQUIRED)
IF ( CUDA_FOUND)
message(STATUS "found CUDA.")
message(STATUS "CUDA status:")
message(STATUS " version: ${CUDA_VERSION}")
message(STATUS " libraries: ${CUDA_LIBRARIES}")
include_directories(${CUDA_INCLUDE_DIRS})
link_libraries(${CUDA_LIBRARIES} ${CUDA_CUFFT_LIBRARIES} ${CUDA_cusolver_LIBRARY} ${CUDA_curand_LIBRARY} ${CUDA_cudart_LIBRARY} ${CUDA_cublas_LIBRARY})
message(STATUS " CUSOLVERLibraries: ${CUDA_cusolver_LIBRARY}")
message(STATUS " CUFFTLibraries: ${CUDA_CUFFT_LIBRARIES}")
message(STATUS " CuRanDLibraries: ${CUDA_curand_LIBRARY}")
message(STATUS " CUDARTLibraries: ${CUDA_cudart_LIBRARY}")
message(STATUS " CUDABLASLibraries: ${CUDA_cublas_LIBRARY}")
ELSE()
message(STATUS "CUDA is not found.")
ENDIF()
附录-交叉编译
我们的代买最终需要在Jetson板卡上运行,但是Jetson板卡编译代码实在是太慢了,因此这里使用交叉编译在主机上编译最终运行的代码。
——>经研究,很不幸,这是不行的,只能在Jetson上编译。

浙公网安备 33010602011771号