第1章 CUDA简介与环境搭建
记得我刚接触CUDA那会儿,还觉得GPU不过是用来打游戏的。直到有一次,一个图像处理项目在CPU上跑了整整三天还没跑完。老板催得紧,我急得不行。后来听同事说试试CUDA,结果你猜怎么着?同样的算法,跑完只用了两个小时。从那天起,我就彻底入了GPU计算的坑。
这一章咱们就从头聊起。CUDA到底是什么?它从哪儿来?能用在哪些地方?还有最重要的——怎么把环境搭起来。别小看环境配置,我见过太多人第一步就卡壳,代码写得再好也跑不起来。
1.1 CUDA的发展史
CUDA的全称是Compute Unified Device Architecture,统一计算设备架构。它由NVIDIA在2006年推出。目标是让GPU不仅能画图,还能做通用计算。
为什么要用GPU算数?你想想看,CPU就像一辆法拉利,跑得快但车里只能坐俩人。GPU就像一辆大巴,速度没那么快,但能拉几十上百人。很多计算任务可以同时处理,比如矩阵乘法、图像滤波、神经网络训练——这些事GPU干起来比CPU快几十倍。
我从2008年开始用CUDA 1.0版本,那时候API还特别原始,连printf都得用cuda-gdb调试。后来版本一路迭代,CUDA 6.0出了统一内存,CUDA 8.0支持Pascal架构,CUDA 10开始引入Tensor Cores……到现在最新的CUDA 12.x,功能已经非常成熟。我个人习惯是尽量用新版本,但生产环境里还得看显卡驱动和硬件的兼容性。
| 版本 | 发布年份 | 核心亮点 |
|---|---|---|
| CUDA 1.0 | 2006 | 首个通用GPU计算平台 |
| CUDA 2.0 | 2008 | 支持双精度浮点 |
| CUDA 6.0 | 2014 | 统一内存 (Unified Memory) |
| CUDA 8.0 | 2016 | Pascal架构优化 |
| CUDA 10 | 2018 | Tensor Cores支持 |
| CUDA 12.x | 2023+ | Hopper架构、动态并行增强 |
1.2 CUDA的应用场景
说白了,凡是“可以并行的计算密集型任务”都能用CUDA加速。我梳理了几个最常见的场景:
- 深度学习与AI:训练和推理,TensorFlow、PyTorch底层都调CUDA。
- 科学计算:分子动力学(比如GROMACS)、天气预报、宇宙模拟。
- 图像与视频处理:实时滤镜、编解码、医学影像重建。
- 金融工程:期权定价、风险模拟、蒙特卡洛计算。
- 数据库与数据挖掘:GPU加速的SQL查询(如RAPIDS)。
我自己之前在医疗影像公司做过一个项目——给病人做CT断层重建。一堆二维切片要合成三维模型,全是矩阵运算。原来用CPU,一张图要三秒钟。换成CUDA之后,整个流程压到了五十毫秒以下。医生当场就感叹,这速度能救命。真的,不夸张。
1.3 安装CUDA Toolkit
好,咱们动起手来。安装前先看下你的显卡是否支持。NVIDIA官网上有个兼容性列表,但最简单的办法是打开命令行,输入nvidia-smi。如果能看到GPU型号和驱动版本,那就没问题。
安装步骤分三块:
- 下载安装包:去NVIDIA官网,选对应操作系统(Windows/Linux/macOS)。我建议选本地安装包(runfile或exe),这样可控性高。
- 执行安装:
- Linux下:
sudo sh cuda_12.4.0_550.54.14_linux.run,记得不装自带驱动(除非驱动版本太低)。 - Windows下:双击exe,一路Next。默认路径C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.4。
- Linux下:
- 验证安装:
在终端敲
nvcc --version,如果显示CUDA版本号,就说明装好了。
我曾经有一次装完nvcc报找不到命令,急得满头汗。后来发现是环境变量没配。所以这一步千万别省——手动把CUDA的bin目录加到PATH里。Linux下这样做:
# 编辑 ~/.bashrc,追加以下两行
export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
# 然后刷新
source ~/.bashrc
update-alternatives或者软链接。我习惯把/usr/local/cuda做成软链接指向具体版本目录,这样只需改链接就能切版本,非常方便。
1.4 配置开发环境
环境搭好了,还得配个顺手的IDE。我个人习惯用Visual Studio Code + CUDA插件。如果你是Windows用户,也可以直接用Visual Studio 2022,它自带CUDA项目模板。
下面是一个最小的CUDA程序——向量加法,用来测试环境是否真能用。
// vec_add.cu
#include <stdio.h>
__global__ void vecAdd(const float* a, const float* b, float* c, int n) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
if (idx < n) {
c[idx] = a[idx] + b[idx];
}
}
int main() {
int n = 1024;
size_t size = n * sizeof(float);
float *h_a, *h_b, *h_c;
float *d_a, *d_b, *d_c;
// 在主机端分配内存
h_a = (float*)malloc(size);
h_b = (float*)malloc(size);
h_c = (float*)malloc(size);
// 在设备端分配显存
cudaMalloc(&d_a, size);
cudaMalloc(&d_b, size);
cudaMalloc(&d_c, size);
// 初始化数据
for (int i = 0; i < n; i++) {
h_a[i] = i * 1.0f;
h_b[i] = (n - i) * 1.0f;
}
// 从主机拷贝到设备
cudaMemcpy(d_a, h_a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, h_b, size, cudaMemcpyHostToDevice);
// 启动核函数:256个线程/块,需要 ceil(n/256) 个block
int threadsPerBlock = 256;
int blocksPerGrid = (n + threadsPerBlock - 1) / threadsPerBlock;
vecAdd<<<blocksPerGrid, threadsPerBlock>>>(d_a, d_b, d_c, n);
// 把结果拷回主机
cudaMemcpy(h_c, d_c, size, cudaMemcpyDeviceToHost);
// 验证结果
for (int i = 0; i < n; i++) {
if (h_c[i] != h_a[i] + h_b[i]) {
printf("错误: 位置 %d 的结果不对\n", i);
break;
}
}
printf("向量加法执行完毕,结果正确!\n");
// 释放内存
cudaFree(d_a); cudaFree(d_b); cudaFree(d_c);
free(h_a); free(h_b); free(h_c);
return 0;
}
编译运行:
nvcc -o vec_add vec_add.cu
./vec_add
如果看到“结果正确”,那恭喜你——环境彻底通了。
cudaDeviceSynchronize(),否则输出缓冲区不会刷新。上面主函数里其实可以加一句cudaDeviceSynchronize();来确保所有核执行完再打印。
嗯,到这里环境就配好了。下面这张图帮你把本章的知识体系串起来——从发展历史到应用场景,再到安装和配置,最后用一个实际程序验证。
这张图把本章内容串成了四条线:历史线、场景线、环境线、验证线。你跟着这个框架学,思路就不会乱。