本文在实现过程中参考了诸多其他博客大佬的文章,内容太多,不便一一放出原文链接。
在工作中要接触cuda计算相关的内容,本人计算机小白,关于如何在windows下使用cuda平台完全不懂。工作地点已经搭建好了linux下的cuda环境,但是本人想要自己在自己的电脑上实现这一环境的搭建,在这个过程中熟悉一下cuda编程,同时自己也可以在私下练习。
首先是硬件的准备,以下是本人的笔记本和系统配置:
系统:windows 11
显卡:NVIDIA GeForce 3070 Laptop
基本上知道以上两个参数配置也就可以了,当然可以决定使用哪种编译器,可以选择Microsoft Visual Studio或者QT、CMake这种可以编译大型项目的软件进行开发的首选,由于项目上的需求,只能使用vscode+CMake作为编译工具,相比于Microsoft Visual Studio搭建环境难度稍大,而对于纯新手小白而言,不建议使用QT这类编译器进行工程项目搭建,上手难度较大。
个人而言难度排名:Microsoft Visual Studio < vscode < QT
安装cuda
这是最为核心的一步,最为关键的一步在于为你的显卡选择正确的cuda版本。
在Windows端,我们通常使用power shell或者cmd命令进行查看cuda所适合的版本。
这一步的前提是要安装正确的显卡驱动,注意显卡驱动跟cuda不是一个东西,显卡驱动一般在计算机系统安装时都会自然安装成功,否则你的屏幕显示可能会不太正常。一般当你看到这个图标的时候,代表N卡的驱动已经被安装成功。
在显卡驱动安装成功的前提下,打开cmd,输入:
nvidia-smi
会输出一个关于显卡驱动的信息列表,在右上角有一个:CUDA Version,这个就是你能够安装的最高版本的cuda。
在这里提一嘴,可能是我电脑的问题,第一次装cuda时选择了12.5的版本之后,总是提示各种库缺失或“找不到xx.exe”等编译器识别不到,亦或者是干脆来一个内存冲突等的报错信息,无从查起。后来安装了一个双系统ubuntu 20.04系统后,更新显卡驱动之后识别到了cuda的最高版本仅为12.4,我在安装12.4版本cuda之后进行编译正常运行。
因此在之后windows安装时,我也同样选择了12.4的版本进行安装,同样可以顺利编译,因此如果大家在这方面遇到问题,可以选择尝试选择较低版本的cuda。当然版本也不可以选择太低,比如我曾经尝试过安装11.0版本的cuda,显示直接安装失败。
后在NVIDIA官网下载cuda:
NVIDIA 工具包档案 下载
选择想要的版本工具包点击进入:
这里一步步按照你的系统版本进行选择:
这里推荐使用network安装方案,也就是下载在线安装版本的安装程序,更加稳定。
我在这里使用的是本地安装_离线版,打开之后会有提示:
注意这个路径只是解压或者下载的缓存文件路径,等待安装结束会自动删除掉,因此可以不用太过纠结选择这个路径。点击OK即可。
开始解压之后等待:
>>同意并继续
第一次安装cuda时尽可能选择自定义而非精简安装,尽可能将所有的库文件全部安装,当然如果你的开发项目仅需要很特定库的更新,那么就可以选择“自定义”选项的单个工具库进行安装。
>>下一步
>>下一步
这里会显示安装的位置,可以点击浏览按钮选择安装路径,一般我的个人习惯都是库等工具都直接安排在C盘,因此没有更改安装目录。
>>下一步
等待安装结束即可。
在安装cuda时,根据网络上的一些介绍,如果需要使用MSVC的话,需要先安装cuda再安装MSVC,否则会提示安装错误的问题,但是我在12以上的cuda上没有遇到过这种错误,倒是在11的版本上遇到过与我本身VS2019不兼容的问题,因此如果在12以下的版本遇到了安装失败的问题,那么先选择不安装VS功能组件或者卸载掉VS后再进行安装。
MSVC编译器的同学可以看一下这篇文章里的安装过程,能避免的坑都已经在我这里或者那里写过了:
CUDA超详细安装教程(windows版)_windows安装cuda-CSDN博客
安装完成之后需要配置环境变量,有的电脑可能已经将路径配置好了,但是大部分应该是需要自己再去手动配置的。
打开“系统属性”窗口,可以直接在搜索框中搜索“环境变量”,选择“编辑系统环境变量”:
>>选择环境变量
一般在系统变量中是有CUDA_PATH和CUDA_PATH_VXX_X这两个条目的。
>>选择“Path”条目
>>选择新建->浏览,将安装路径下的bin文件夹和libnvpp文件夹放入环境变量中,如果你是默认安装路径,可以直接复制以下路径:
C:\program files\NVIDIA GPU Computing Toolkit\CUDA\vxx.x\binC:\program files\NVIDIA GPU Computing Toolkit\CUDA\vxx.x\libnvvp
同时还要将libx64,include文件夹放入环境变量中,由于在系统变量中已经定义了CUDA_PATH这个条目,因此可以直接拿来使用。
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4\binC:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4\libnvvpC:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4\extras\CUPTI\libx64C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4\include等效于%CUDA_PATH%\bin%CUDA_PATH%\libnvvp%CUDA_PATH%\extras\CUPTI\libx64%CUDA_PATH%\include
建立完毕之后在退出时一定要全部点击确定、完成,否则可能会失效。
顺利完成之后,打开cmd输入如下命令:
nvcc --version
或者:
nvcc -V
如果安装成功,则会输出cuda的安装版本
注意:nvcc其实是cuda编译器的程序,等同于MSVC的cl.exe或者gcc.exe这样的程序。
安装设置vscode
进入官网直接下载安装vscode即可,官网链接如下:
Visual Studio Code - Code Editing. Redefined
安装完成后打开,在配置时安装C/C++、CMake、code-runner等插件
此时,vscode GUI的配置就已经完成了。
综合编译环境配置
由于现在很主流的编译器分为MSVC和MinGW,这两个编译器可以适用于各大系统,但是由于MSVC是微软亲儿子,在Windows系统下开发,使用MSVC编译的过程可能要更加简单方便。
当然为了保险起见,可以将各个编译器环境都配置齐全,在编译过程中也可以有多种排查错误的选择。
MSVC
这个没什么好说的,只能够通过Microsoft Visual Studio的 installer 进行安装。
在这里要注意一个事项,在NVIDIA官网的安装指南中,显示了CUDA各版本支持的MSVC版本,因此在使用MSVC时一定要注意版本的选择。
CUDA Installation Guide for Microsoft Windows
由于CUDA出现应用的时间比较晚,因此这里需要的MSVC版本也要求比较新,所以大家在选择时要注意版本。
MinGW
官网下载:
MinGW 官网下载
MinGW的版本选择有一个很坑的地方,官网下载只提供最新版的在线安装包,或者是8.1及之前的离线安装包,在中间的版本都需要在GitHub上进行下载
Github MinGW-builds-binaries List
我在这里找了一个能够下载历史离线安装包的链接,可以点击下载。
在这篇博客中有对相应平台安装包选择的介绍:MinGW-w64的安装详细步骤(c/c++的编译器gcc、g++的windows版,win10、win11真实可用)-CSDN博客
在线版可以直接下载安装,离线版可以直接解压,就是程序本体。
在安装时要记录安装路径,接下来配置环境变量。过程类似于上文cuda配置环境的过程,将你的MinGW安装路径添加到环境变量中。
C:\msys64\mingw64\bin#如果提前定义了MinGW的安装路径为MINGW_PATH,则可以表示为:%MINGW_PATH%\mingw64\bin
打开cmd后输入:
gcc --versiong++ --version
则会有版本提示,那么说明安装配置成功了。
CMake & make
在编译一些比较大型的文件时,不能单独只靠编译器进行编译,还需要有能够组织各文件的工程文件,而CMake就是起到了这样作用的工具。在编译众多文件时,如果使用shell命令行,我们要将工程的源文件、头文件、库文件、项目名称等等众多内容都要包含进去,这就会让编译的过程变得异常复杂。因此我们选择CMake这样的软件进行综合组织。
我们进入CMake官网直接下载,安装即可。
在安装完毕之后,同样也要将CMake的安装路径下的bin文件夹添加到环境变量目录中。
C:\Program Files\CMake\bin
至于make,在本质上与CMake起到的作用几乎一致,可以看以下这位大佬写的博客:
Make与CMake_cmake和make-CSDN博客
至于为什么要安装完CMake还要安装make,我这边在使用CMake时遇到了一个问题,那就是在vscode平台上使用make指令无效,于是我就安装了makefile,如果同学们没有遇到这个问题的话,那不用再额外安装make。
在这里就不再额外附上makefile的安装过程,有需要的同学可以直接参考如下的文章:
如何在系统中安装make命令_make下载安装-CSDN博客
编写单文件cuda程序
经过以上的安装过程之后,已经基本配置好了cuda运行所需要的环境,此时开始编写第一个cuda程序。
进入到工程文件夹中,打开终端,在终端中输入:
code .
此功能是要将vscode添加到环境变量中才可以使用,如果安装时没有选中此选项,那么可以先打开vscode,然后在vscode中将我们的工程文件夹添加到工作目录即可。
此时新建一个名为“hello.cu”的文件,cuda程序的文件后缀都是“.cu”或者“.cuh”。然后打开文件,输入如下程序:
#include <stdio.h>__global__ void hello_world(void){printf("GPU: Hello World!\n");}int main(void){printf("CPU: Hello World!\n");hello_world<<<1, 10>>>();cudaError_t err = cudaGetLastError();if (err != cudaSuccess) { printf("CUDA Error: %s\n", cudaGetErrorString(err));} cudaDeviceReset();return 0;}
程序使用了CUDA编程(三):Hello world_cuda编程hello world-CSDN博客中的示例程序,同学们可以去看看对程序的解释,这里就不放出具体的解释了。
注意,一定要及时按Ctrl+S保存程序,如果不保存程序的话,编译器是无法检测到你对文件的改动的,等于每次都在编译未改动的版本!
保存之后,打开vscode的终端,不知道怎么打开的同学可以点击界面上的这个按钮:
然后在下面的终端内输入如下指令:
nvcc hello.cu -o hello# nvcc: cuda编译器编译指令# hello.cu: 要编译的源文件# -o: 优化速度生成# hello: 生成的目标文件
这时会发现它在目录下生成了三个文件,分别是hello.exe、hello.exp、hello.lib:
我们先不去管其他的文件,继续在终端中输入指令来运行已经生成的exe文件:
.\hello.exe
此时会发现它输出了我们想要的结果
使用nvcc编译cuda程序的过程其实就是:使用nvcc指令编译生成可执行文件,然后运行可执行文件。
PS:这里要注意一个问题,由于我个人的电脑上安装了杀毒软件,可能个别杀毒软件会检测到此程序调用了比较核心(诸如GPU)的函数,则会认为其是病毒然后立刻删除掉这个文件,因此会遇到一运行exe文件就会消失的情况,可以关掉杀毒软件再尝试一下。
编写多文件cuda工程项目
测试完成cuda组件可以使用之后,就要进入实际的工程需求项目建立过程中了。
进入到实际的工程文件夹,按照如下的文件结构建立文件夹和相应的文件:
Project||--include| || |--foo.cuh||--src| || |--foo.cu||--main.cpp||--CMakeLists.txt
现在对各个文件依次写入如下内容:
foo.cuh
#pragma once#define BX 256void h_vec_add(int *a, int *b, int *c, int n);
foo.cu
#include "foo.cuh"#include <cuda_runtime.h>#include <device_launch_parameters.h>//Kernel__global__ void d_vec_add(int *d_a, int *d_b, int *d_c, int n){ int i = blockIdx.x * blockDim.x + threadIdx.x; if (i < n) d_c[i] = d_a[i] + d_b[i];}void h_vec_add(int *a, int *b, int *c, int n){ int *d_a, *d_b, *d_c; cudaMalloc((void **)&d_a, sizeof(int) * n); cudaMalloc((void **)&d_b, sizeof(int) * n); cudaMalloc((void **)&d_c, sizeof(int) * n); cudaMemcpy(d_a, a, sizeof(int) * n, cudaMemcpyHostToDevice); cudaMemcpy(d_b, b, sizeof(int) * n, cudaMemcpyHostToDevice); dim3 DimGrid(n / BX + 1, 1, 1); dim3 DimBlock(BX, 1, 1); d_vec_add<<<DimGrid, DimBlock>>>(d_a, d_b, d_c, n); cudaMemcpy(c, d_c, sizeof(int) * n, cudaMemcpyDeviceToHost); cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);}
main.cpp
#include "./include/foo.cuh"#include <iostream>#define N 512int main(){ int h_a[N]; int h_b[N]; int h_c[N]; for (int i = 0; i < N; ++i) h_a[i] = h_b[i] = i; h_vec_add(h_a, h_b, h_c, N); for (int i = 0; i < N; ++i) std::cout << h_c[i] << '\t'; std::cout << '\n';}
此时我们可以进行一个示例,构建完成这三个文件之后,可以在vscode的终端中输入如下指令:
nvcc ./src/foo.cu main.cpp -I./include -o cuda# nvcc: 编译器编译指令# ./src/foo.cu main.cpp: 所有的源文件,如果在次级目录可以用./进入# -I./include: -I后面跟包含库目录,在此工程中也就是./include目录# -o: 优化生成# cuda: 目标文件名称为cuda
可以看到其实多个文件的情况同样也可以使用命令行的形式来进行编译,但是我们在编译一个仅有三个文件的工程项目就非常复杂,所以我们采用更加便捷的方法,来构建大型的cuda工程。
这里可以参考这位大佬的博客,其写的CMake+cuda项目构建过程非常详细:
https://zhuanlan.zhihu.com/p/701581020
CMakeLists.txt文件要按照环境的具体配置情况依次来书写:
1.CMake的版本*
cmake_minimum_required (VERSION 3.26)
首先使用 cmake_minimum_required() 指定使用的 CMake 最低版本号。值得注意的是,只有CMake 3.11版本以上才支持cuda,因此建议大家安装最新的CMake。
2.项目名称*
project (cmake_main)
确定项目名称为“cmake_main”。
3.语言支持*
enable_language(CXX)enable_language(CUDA)
这里的CXX代表的是C++语言支持,在编写cuda程序时,主要还是建立在C/C++语言基础上,只有核函数才会运行在显卡上。
4.查找CUDA工具包*
find_package(CUDAToolkit REQUIRED)
find_package用于添加外部库或软件包, 如果有不同版本的软件包也可以指定版本号.
REQUIRED: 如果指定的包找不到, CMake 将报错并停止进一步的配置过程
QUIET: 安静模式. 即使找不到包, CMake 也不会在控制台输出任何警告或错误信息
EXACT: 查找的包必须完全匹配指定的版本
5.设置CUDA C++版本*
set(CMAKE_CXX_STANDARD 17)set(CMAKE_CUDA_STANDARD 17)
6.变量存储文件夹路径*
set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include")set(SRC_DIR "${CMAKE_SOURCE_DIR}/src")
这里应该是要添加头文件的目录和源文件的目录,但是在实际上由于我的环境在配置过程中出现了一些问题,在运行的过程中提示缺少了一些文件,所以我只能将MSVC的源目录等一些文件目录设置为了INCLUDE_MSVC、INCLUDE_CMAKE,然后将其添加到了include包含头文件夹中了,仅供参考,大家在配置时如果未出现问题,可以不用这样设置。
set(INCLUDE_MSVC "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133")set(INCLUDE_CMAKE "C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/ucrt")set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include" "${INCLUDE_MSVC}/include" "${INCLUDE_CMAKE}")set(SRC_DIR "${CMAKE_SOURCE_DIR}/src")
7.生成文件列表*
file(GLOB SRC_FILES "*.cpp" "${SRC_DIR}/*.c" "${SRC_DIR}/*.cpp" "${SRC_DIR}/*.cc" "${SRC_DIR}/*.cxx" "${SRC_DIR}/*.cu")
此处是要按照固定的格式去匹配文件名,相当于去寻找目标路径下所有以此为后缀的文件进行构建,比如我们在/src/文件夹中如果加入了更多的cu文件或者cpp文件,那么可以直接搜索添加到工程中,不必修改CMakeLists文件。
除此之外,由于我们的main.cpp文件是在/src/文件夹上一级的,所以也要加入对同级文件夹cpp文件的搜索。
8.打印信息
message(STATUS "Src files: ${SRC_FILES}")
打印所有搜索到的源文件,可以使用message() 函数进行输出make的信息。
if (CMAKE_CUDA_COMPILER) message(STATUS "nvcc path : ${CMAKE_CUDA_COMPILER}")else () message(WARNING "nvcc not found. Please check CUDA is installed correctly!")endif ()
同时可以使用条件语句来检查CUDA是否能够正常使用。
9.向构建目标链接引用、库文件目录
set(LINK_LIBRARIES_KERNEL32 "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.19041.0/um/x64")set(LINK_LIBRARIES_MSVC "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/lib/x64")set(LINK_LIBRARIES_UCRT "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.19041.0/ucrt/x64")# link directorieslink_directories("${LINK_LIBRARIES_KERNEL32}" "${LINK_LIBRARIES_MSVC}" "${LINK_LIBRARIES_UCRT}") include_directories("${LINK_LIBRARIES_KERNEL32}" "${LINK_LIBRARIES_MSVC}" "${LINK_LIBRARIES_UCRT}")
同样是因为我的环境配置出了问题,再次将这些静态库目录添加到了链接库中。
10.添加构建目标*
add_executable(${PROJECT_NAME})
11.向目标链接源文件、头文件*
target_sources(${PROJECT_NAME} PRIVATE ${SRC_FILES})target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIR})
在以上的环节中,9-11的环节顺序不能颠倒,否则构建目标就不会成功,或者构建目标得到的makefile文件无法被make。
完成构建
由于我在安装配置环境时踩坑太多,导致安装配置一些引用库、链接库等的环节出了问题,如果配置环境没有问题的话其实是不需要第9步的环节,且第6步也可以不用如此复杂,所以大家根据自己的环境进行配置即可,如果出现问题,再来进行添加相应的库文件目录。
纵观整个环节,其实就是类似于Microsoft Visual Studio工程项目配置的过程,在建立项目时要添加静态库和动态库,将缺失的库目录补足即可完成项目的配置。
以下是我的项目配置CMakeLists.txt文件内容,仅供参考:
cmake_minimum_required (VERSION 3.26) project (cmake_main)enable_language(CXX)enable_language(CUDA)# set(CMAKE_CUDA_COMPILER_WORKS TRUE)# set(CMAKE_CXX_COMPILER_WORKS TRUE)find_package(CUDAToolkit REQUIRED)set(CMAKE_CXX_STANDARD 17)set(CMAKE_CUDA_STANDARD 17)#set(CMAKE_CUDA_ARCHITECTURES 75)set(INCLUDE_MSVC "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133")set(INCLUDE_CMAKE "C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/ucrt")set(INCLUDE_DIR "${CMAKE_SOURCE_DIR}/include" "${INCLUDE_MSVC}/include" "${INCLUDE_CMAKE}")set(SRC_DIR "${CMAKE_SOURCE_DIR}/src")file(GLOB SRC_FILES "*.cpp" "${SRC_DIR}/*.c" "${SRC_DIR}/*.cpp" "${SRC_DIR}/*.cc" "${SRC_DIR}/*.cxx" "${SRC_DIR}/*.cu")message(STATUS "Src files: ${SRC_FILES}")if (CMAKE_CUDA_COMPILER) message(STATUS "nvcc path : ${CMAKE_CUDA_COMPILER}")else () message(WARNING "nvcc not found. Please check CUDA is installed correctly!")endif ()set(LINK_LIBRARIES_KERNEL32 "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.19041.0/um/x64")set(LINK_LIBRARIES_MSVC "C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.29.30133/lib/x64")set(LINK_LIBRARIES_UCRT "C:/Program Files (x86)/Windows Kits/10/Lib/10.0.19041.0/ucrt/x64")# link directorieslink_directories("${LINK_LIBRARIES_KERNEL32}" "${LINK_LIBRARIES_MSVC}" "${LINK_LIBRARIES_UCRT}") include_directories("${LINK_LIBRARIES_KERNEL32}" "${LINK_LIBRARIES_MSVC}" "${LINK_LIBRARIES_UCRT}") add_executable(${PROJECT_NAME})target_sources(${PROJECT_NAME} PRIVATE ${SRC_FILES})target_include_directories(${PROJECT_NAME} PRIVATE ${INCLUDE_DIR})
完成构建后记得保存。
此时要选择相应的编译器和编译模式,在这里我选择的是 MSVC-Debug 模式,在vscode的界面下方可以进行选择:
方法1:使用CMake控件
可以点击最下方状态栏的生成按钮,将会进行生成,这时我们看到出现了一个build文件夹,里面包含了cmake构建的makefile文件,这是我们再点击生成按钮右侧的运行按钮,在build文件夹中生成exe等一系列文件,同时它会帮助你运行这个exe文件,输出想要的结果。
方法2:使用命令行
在vscode的终端内,执行如下语句:
mkdir build #创建build文件夹cd build #进入build文件夹cmake .. #生成makefile文件cmake --build . #生成可执行exe文件./Debug/cmake_main.exe #运行可执行exe文件
同样可以有相同的效果,不一样的是,执行此代码会生成/Debug文件夹和/x64文件夹,而可执行exe同样也会生成在/Debug文件夹内。
值得一提的是,当构建完CMakeLists文件时,按下Ctrl+S保存之后,会自动进入构建状态,等待构建项目完成之后,此时进入build文件夹内,会有一个makefile文件,在终端内输入:
make
即可完成构建,同时在build文件夹内生成可执行exe文件,此时运行则有相同的结果。
同样可以切换编译器为MinGW,但在Windows端下,可能成功的概率较低。
此时,我们就完成了构建的全过程。
总结
以上就完成了一个最简单的cuda工程文件的配置到构建的全过程,等下次进行构建项目时,可以直接将CMakeLists文件拿来用,只需要增加相应的命令语句即可。
在以上的构建cuda工程项目的过程中,踩到了很多坑,同时也学习了很多关于编程的知识。本着计算机小白入门学习的宗旨,特开此系列入门级教程,在之后的时间里会随着项目的深入,持续更新有关的内容,欢迎各位同学讨论交流!