1.编程步骤
典型的CUDA编程结构包括5个主要步骤:
- 分配GPU内存。 //cudaMalloc();
- 从CPU内存中拷贝数据到GPU内存。//cudaMemcpy(dst, src, size, cudaMemcpyHostToDevice);
- 调用CUDA内核函数来完成程序指定的运算。
- 将数据从GPU拷回CPU内存。//cudaMemcpy(dst, src, size, cudaMemcpyDeviceToHost);
- 释放GPU内存空间。 //cudaFree();
2.编程思想
CUDA编程思想:
- 数据局部性。指的是数据重用,以降低内存访问的延迟。数据局部性有两种基本类型:
- 时间局部性:相对较短的时间段内数据和/或资源的重用。
- 空间局部性:相对较接近的存储空间内数据元素的重用。
- CUDA核中有3个关键抽象:线程组的层次结构,内存的层次结构以及障碍同步。
- CUDA toolkit:https://developer.nvidia.com/cuda-toolkit
- cuda 在线文档:CUDA Toolkit Documentation 12.6
- NVIDIA为C和C++开发人员提供了综合的开发环境以创建GPU加速应用程序,包括以下几种:
- NVIDIA Nsight集成开发环境
- CUDA-GDB命令行调试器
- 用于性能分析的可视化和命令行分析器
- CUDA-MEMCHECK内存分析器
- GPU设备管理工具
3. 编程实操
3.1 设备端核函数定义
__global__ void kernel_function(argument list); //返回类型必须为void。__global__函数类型限定符,表示在设备端执行。
核函数限制:
- 只能访问设备内存
- 必须具有void返回类型
- 不支持可变数量的参数
- 不支持静态变量
- 显示异步行为
3.2 错误处理
定义一个错误处理宏封装所有的CUDA API调用,这简化了错误检查过程
#define CHECK(call) { \
const cudaError_t error = call; \
if(error != cudaSuccess) { \
printf("Error: %s:%d, ",__FILE__,__LINE__); \
pirntf("code:%d, reason: %s\n", error, cudaGetErrorString(error)); \
exit(1); \
}
}
kernel_function<<<grid,block>>>(argument list);
CHECK(cudaDeviceSynchronize());
3.3 命令行分析工具
nvprof:帮助从应用程序的CPU和GPU活动情况中获取时间线信息,其包括内核执行、内存传输以及CUDA API的调用。nvprof --help
nvprof [nvprof_args] <application> [application_args]