运行以下命令:
# 检查 nvcc 编译器
nvcc --version
# 检查 GPU 驱动和设备
nvidia-smi
# 检查 CUDA 库路径
echo $LD_LIBRARY_PATH | grep cuda如果 nvcc 找不到,需要将 CUDA 添加到 PATH:
export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH可能原因:
- 驱动未安装或版本不匹配
- 内核更新后驱动失效
- Secure Boot 阻止加载驱动
解决方案:
# 检查驱动模块
lsmod | grep nvidia
# 重新安装驱动
sudo apt-get purge nvidia*
sudo apt-get install nvidia-driver-xxx # 替换为合适版本
sudo reboot| CUDA 版本 | 最低驱动版本 |
|---|---|
| CUDA 12.x | 525.60+ |
| CUDA 11.x | 450.80+ |
| CUDA 10.x | 410.48+ |
查看兼容性:CUDA Toolkit Release Notes
CUDA 未添加到 PATH,添加环境变量:
# 添加到 ~/.bashrc
export PATH=/usr/local/cuda/bin:$PATH
source ~/.bashrcGCC 版本过高,CUDA 不支持。
解决方案 1:安装兼容版本的 GCC
sudo apt-get install gcc-9 g++-9
nvcc -ccbin g++-9 your_file.cu -o output解决方案 2:使用 --allow-unsupported-compiler(不推荐用于生产)
nvcc --allow-unsupported-compiler your_file.cu -o output常见原因:
- 缺少头文件
- 使用了 CPU 函数在 GPU 代码中
示例错误:在核函数中使用 malloc
__global__ void kernel() {
int* p = malloc(100); // 错误!GPU 不能用 malloc
}修复:使用设备端分配或预分配内存
__global__ void kernel(int* pre_allocated) {
// 使用预分配的内存
}链接时找不到 CUDA 库。
# 确保链接 cudart
nvcc your_file.cu -o output -lcudart
# 如果使用其他库,也要链接
nvcc your_file.cu -o output -lcublas -lcudnn某些 API 已弃用。查看警告信息,使用新 API 替代:
| 旧 API | 新 API |
|---|---|
cudaThreadSynchronize() |
cudaDeviceSynchronize() |
cudaThreadExit() |
cudaDeviceReset() |
cudaBindTexture() |
纹理对象 API |
编译时的架构与运行时 GPU 不匹配。
解决方案:指定正确的计算能力
# 查看你的 GPU 计算能力
nvidia-smi --query-gpu=compute_cap --format=csv
# 编译时指定(例如 sm_75 对应计算能力 7.5)
nvcc -arch=sm_75 your_file.cu -o output
# 或者编译多个架构
nvcc -gencode arch=compute_60,code=sm_60 \
-gencode arch=compute_75,code=sm_75 \
your_file.cu -o output核函数调用了不支持的特性。
常见原因:
- 使用了高版本特性但编译目标是低版本
- 动态并行需要
-rdc=true
# 动态并行必须使用
nvcc -rdc=true -lcudadevrt your_file.cu -o output线程块请求的资源超出限制。
检查限制:
cudaDeviceProp prop;
cudaGetDeviceProperties(&prop, 0);
printf("每块最大线程数: %d\n", prop.maxThreadsPerBlock);
printf("每块最大共享内存: %zu\n", prop.sharedMemPerBlock);
printf("每块最大寄存器: %d\n", prop.regsPerBlock);解决方案:减少线程数或共享内存使用
// 从 1024 减少到 256
kernel<<<blocks, 256>>>();核函数执行时崩溃,通常是非法内存访问。
调试方法:
# 使用 compute-sanitizer
compute-sanitizer ./your_program设备端断言失败。
# 查看详细信息
CUDA_LAUNCH_BLOCKING=1 ./your_programGPU 显存不足。
解决方案:
- 减小数据规模
- 分批处理
- 使用统一内存(自动分页)
- 及时释放不用的内存
// 检查可用显存
size_t free_mem, total_mem;
cudaMemGetInfo(&free_mem, &total_mem);
printf("可用: %zu MB, 总计: %zu MB\n",
free_mem/1024/1024, total_mem/1024/1024);常见原因:
- 忘记拷贝数据到 GPU
- 忘记拷贝结果回 CPU
- 核函数没有执行(配置错误)
- 数组越界
检查清单:
// 1. 确保 H→D 拷贝
cudaMemcpy(d_data, h_data, size, cudaMemcpyHostToDevice);
// 2. 确保核函数执行
kernel<<<blocks, threads>>>(d_data, N);
cudaDeviceSynchronize(); // 等待完成
// 3. 确保 D→H 拷贝
cudaMemcpy(h_result, d_result, size, cudaMemcpyDeviceToHost);
// 4. 检查每一步的错误
cudaError_t err = cudaGetLastError();
if (err != cudaSuccess) {
printf("Error: %s\n", cudaGetErrorString(err));
}# 使用 compute-sanitizer 的 memcheck 工具
compute-sanitizer --tool memcheck ./your_program
# 或使用 cuda-memcheck(旧版本)
cuda-memcheck ./your_program| 场景 | 推荐 |
|---|---|
| 数据完全在 GPU 处理 | cudaMalloc |
| CPU/GPU 频繁交互 | cudaMallocManaged |
| 追求最高性能 | cudaMalloc + 手动管理 |
| 快速原型开发 | cudaMallocManaged |
常见原因:
-
数据量太小:GPU 有启动开销,小数据不划算
建议:N > 10000 才考虑 GPU -
传输时间占主导:计算量小但数据量大
// 优化:使用 pinned memory 加速传输 cudaMallocHost(&h_data, size); // 而不是 malloc
-
核函数效率低:
- 分支发散
- 非合并访存
- 占用率低
cudaEvent_t start, stop;
cudaEventCreate(&start);
cudaEventCreate(&stop);
cudaEventRecord(start);
kernel<<<blocks, threads>>>(args);
cudaEventRecord(stop);
cudaEventSynchronize(stop);
float ms = 0;
cudaEventElapsedTime(&ms, start, stop);
printf("耗时: %.3f ms\n", ms);
cudaEventDestroy(start);
cudaEventDestroy(stop);线程束(32线程)同时访问连续内存地址时,可以合并为一次事务。
好的访问模式:
// 连续访问 - 合并
data[tid] = value; // tid = 0,1,2,3...差的访问模式:
// 跨步访问 - 不合并
data[tid * stride] = value; // 跳跃访问共享内存分为 32 个 bank。同一 warp 内的线程访问同一 bank 的不同地址会串行化。
避免方法:
// 有冲突:所有线程访问 bank 0
__shared__ float s[32][32];
float val = s[threadIdx.x][0];
// 无冲突:每个线程访问不同 bank
float val = s[threadIdx.x][threadIdx.x];
// 技巧:padding 避免冲突
__shared__ float s[32][33]; // 33 而不是 32// 1. 查询占用率
int blockSize = 256;
int minGridSize, gridSize;
cudaOccupancyMaxPotentialBlockSize(&minGridSize, &blockSize, kernel, 0, 0);
// 2. 减少每线程寄存器使用
nvcc -maxrregcount=32 your_file.cu -o output
// 3. 减少共享内存使用
// 4. 调整线程块大小(通常 128-512)# 设置环境变量,所有 CUDA 调用变为同步
export CUDA_LAUNCH_BLOCKING=1
./your_program__global__ void kernel(float* data, int n) {
int tid = threadIdx.x + blockIdx.x * blockDim.x;
// 只让第一个线程打印
if (tid == 0) {
printf("n = %d\n", n);
}
// 或者限制打印范围
if (tid < 5) {
printf("tid %d: data = %f\n", tid, data[tid]);
}
}#define CHECK_CUDA(call) do { \
cudaError_t err = call; \
if (err != cudaSuccess) { \
fprintf(stderr, "CUDA 错误 %s:%d: %s\n", \
__FILE__, __LINE__, cudaGetErrorString(err)); \
exit(EXIT_FAILURE); \
} \
} while(0)
#define CHECK_LAST_ERROR() CHECK_CUDA(cudaGetLastError())
// 使用
CHECK_CUDA(cudaMalloc(&d_data, size));
kernel<<<blocks, threads>>>(d_data);
CHECK_LAST_ERROR();| 工具 | 用途 |
|---|---|
nvprof(旧) |
基础性能分析 |
nsys (Nsight Systems) |
系统级时间线分析 |
ncu (Nsight Compute) |
核函数详细分析 |
compute-sanitizer |
内存错误检测 |
# Nsight Systems
nsys profile ./your_program
nsys-ui report.qdrep
# Nsight Compute
ncu ./your_program
ncu --set full -o report ./your_program不能。核函数必须是 void 返回类型。通过输出参数返回结果:
__global__ void kernel(int* output) {
output[0] = 42; // 通过指针返回
}可以,但需要动态并行支持:
nvcc -rdc=true -lcudadevrt your_file.cu -o output不能直接使用。GPU 代码不支持 STL。替代方案:
- 使用 Thrust 库(CUDA 的 STL 替代)
- 使用 CUB 库(高性能原语)
int deviceCount;
cudaGetDeviceCount(&deviceCount);
for (int i = 0; i < deviceCount; i++) {
cudaSetDevice(i);
// 在设备 i 上分配内存和执行核函数
}- 官方文档:CUDA C++ Programming Guide
- 开发者论坛:NVIDIA Developer Forums
- Stack Overflow:搜索
[cuda]标签 - 本教程示例:查看对应主题的
.cu文件