cuda c++必须用nvcc编译,因g++不识别__global__等关键字;.cu文件需用nvcc -o输出;kernel启动三尖括号非模板语法,须填运行时确定的grid/block尺寸;数据须显式拷贝,注意方向与同步;__global__函数无返回值,禁用stl,可用thrust替代。

CUDA C++ 代码必须用 nvcc 编译,不能用 g++/clang++ 直接编译
CUDA C++ 不是“加个 flag 就能跑的 C++”,nvcc 是专用编译器,负责拆分主机(host)和设备(device)代码。用 g++ 直接编译 .cu 文件会报一堆 undefined reference to 'cudaLaunchKernel' 或 __global__ 语法错误——因为 g++ 根本不认识 __global__、__device__ 这些关键字。
实操建议:
- 源文件后缀必须为
.cu(哪怕里面只有纯 C++ 函数) - 编译命令固定用:
nvcc -o myapp myapp.cu - 如果要链接 OpenCV 或其他库,把
-lopencv_core等放nvcc命令末尾,别写在前面 - 调试时加
-Xcompiler -g让主机端支持 gdb,但设备端调试得用cuda-gdb
kernel 启动语法里 >> 不是模板,是运行时配置,括号里不能写变量名
my_kernel>>(arg1, arg2) 这个三尖括号不是 C++ 模板语法,而是 nvcc 特殊解析的 kernel 启动符。里面填的是运行时确定的整数:线程块数量(grid)和每块线程数(block),不能是未初始化变量或非常量表达式。
常见错误现象:
立即学习“C++免费学习笔记(深入)”;
-
error: expected an expression—— 写了my_kernel>>(),但N和M是函数参数或运行时读入的值(必须是常量?不,其实是允许变量,但必须是已知值;真正错因常是类型不对,比如传了size_t却没显式转int) - 启动后卡死或结果全零 ——
grid算少了,比如用(N / block)忘了向上取整,导致最后几个元素没被覆盖
正确做法:
- 用
(N + block - 1) / block算 grid 维度 - block 大小优先选 32 的倍数(如 128、256),避开 17、43 这种非 warp 对齐值(否则浪费资源)
- 启动前加
cudaError_t err = cudaGetLastError(); if (err != cudaSuccess) printf("%s\n", cudaGetErrorString(err));,否则 kernel 失败静默吞掉
GPU 显存和 CPU 内存不互通,所有数据都要显式拷贝
写完 kernel 一运行就 cudaErrorInvalidValue 或结果乱码?八成是忘了把输入数据从 CPU 内存搬到 GPU 显存。GPU 不能直接读 int* h_data = new int[N],必须用 cudaMalloc 分配显存,再用 cudaMemcpy 拷过去。
关键细节:
-
cudaMalloc(&d_data, N * sizeof(int))分配的是 device 指针,类型是int*,但语义上它不能传给普通函数 - 拷贝方向容易搞反:
cudaMemcpy(d_data, h_data, ..., cudaMemcpyHostToDevice),第三个参数别写成cudaMemcpyDeviceToHost - 别在 kernel 调用后立刻
cudaMemcpy回 CPU —— 默认是异步的,得先cudaDeviceSynchronize(),否则可能读到旧数据 - 显存不释放会累积泄漏,对应
cudaFree(d_data),且不能对同一指针调两次
__global__ 函数不能有返回值,也不能调用标准 C++ STL 容器
想让 kernel 返回一个 std::vector<float></float>?不行。写 __global__ void foo() { return 42; }?编译直接报错。GPU kernel 是 void 函数,所有输出都靠写显存地址实现。
为什么限制这么死?因为 kernel 在成百上千个并行线程里执行,没有统一“返回”概念;STL 容器依赖主机堆管理、异常、RTTI 等,GPU 端 runtime 不提供。
替代方案:
- 输出结果写进预分配的 device 数组(
float* d_out),CPU 端再拷回来 - 需要动态数组?用
thrust::device_vector(需包含<thrust></thrust>),它是 CUDA 封装好的、可在 device 上构造的容器 - 想用 sort 或 reduce?直接调
thrust::sort(d_begin, d_end),底层自动 dispatch 到 GPU,比手写 kernel 更快也更稳
设备端能用的数学函数有限,比如 sqrtf()(单精度)可用,std::sqrt() 不行;printf 在 kernel 里能用但仅限于计算能力 2.0+ 且要加 -arch=sm_35 类似参数,还只支持基础格式符。











