GPU程序的一般步骤
**CUDA 除了是并行计算架构外,还是 CPU 和 GPU 协调工作的通用语言。**在CUDA 编程模型中,主要有 Host(主机)和 Device(设备)两个概念,Host 包含 CPU 和主机内存,Device 包含 GPU 和显存,两者之间通过 PCI Express 总线进行数据传输。
在具体的 CUDA 实现中,程序通常划分为两部分,在主机上运行的 Host 代码和在设备上运行的 Device 代码。Host 代码负责程序整体的流程控制和数据交换,而 Device 代码则负责执行具体的计算任务。
一个完整的 CUDA程序是由一系列的设备端函数并行部分和主机端的串行处理部分共同组成的,主机和设备通过这种方式可以高效地协同工作,实现 GPU 的加速计算。
CUDA程序的具体编写方法:
- 分配host内存,并进行数据初始化;
- 分配device内存,并从host将数据拷贝到device上;
- 调用CUDA的核函数在device上完成指定的运算;
- 将device上的运算结果拷贝到host上(性能);
- 释放device和host上分配的内存。
一个典型的cuda编程案例:
// CUDA runtime头文件
\#include <cuda_runtime.h>
\#include <assert.h>
// 核函数
__global__ void add(int *a, int *b, int *c) {
// 获取全局线程ID
int tid = blockIdx.x * blockDim.x + threadIdx.x;
// 线程对应数组下标
int idx = tid;
// 数组加法
c[idx] = a[idx] + b[idx];
}
int main() {
// 定义数组大小
int N = 1024;
int size = N * sizeof(int);
// 主机端数组
int *a, *b, *c;
// 在主机内存中为数组分配空间
a = (int*)malloc(size);
b = (int*)malloc(size);
c = (int*)malloc(size);
// 初始化数组
for (int i=0; i<N; i++) {
a[i] = i;
b[i] = 2 * i;
}
// 设备端数组
int *d_a, *d_b, *d_c;
// 在设备上为数组分配空间
cudaMalloc(&d_a, size);
cudaMalloc(&d_b, size);
cudaMalloc(&d_c, size);
// 从主机复制数据到设备
cudaMemcpy(d_a, a, size, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, b, size, cudaMemcpyHostToDevice);
// 定义线程块和网格
int threadsPerBlock = 256;
int blocksPerGrid = (N + threadsPerBlock - 1) / threadsPerBlock;
// 启动内核
add<<<blocksPerGrid, threadsPerBlock>>>(d_a, d_b, d_c);
// 同步设备,确保内核完成
cudaDeviceSynchronize();
// 将结果复制回主机
cudaMemcpy(c, d_c, size, cudaMemcpyDeviceToHost);
// 释放设备内存
cudaFree(d_a);
cudaFree(d_b);
cudaFree(d_c);
// 验证结果
for (int i=0; i<N; i++) {
assert(c[i] == a[i] + b[i]);
}
// 释放主机内存
free(a);
free(b);
free(c);
return 0;
}