这一部分将介绍CUDA的并行编程方式
矢量求和运算
假设有两组数据,我们需要将这两组数据中对应的元素两两相加,并将结果保存在第三个数组中。
基于CPU的矢量求和
首先,下面的代码是通过传统的C代码来实现这个求和运算
#include "../common/book.h"
#define N 10
void add(int *a, int *b, int *c){
int tid = 0; //这是第0个CPU,因此索引从0开始
while(tid < N){
c[tid] = a[tid] + b[tid];
tid += 1; //由于只有一个CPU,因此每次递增1
}
}
int main(){
int a[N], b[N], c[N];
//在CPU上为数组"a"和"b"赋值
for(int i = 0; i < N; i++){
a[i] = -i;
b[i] = i * i;
}
add(a, b, c);
//显示结果
for(int i = 0; i < N; i++){
printf("%d + %d = %d\n", a[i], b[i], c[i]);
}
return 0;
}
add()中使用while循环而不是for循环是为了代码能够在拥有多个CPU或者多个CPU核的系统上并行运行,比如双核处理器上可以将每次递增的大小改为2。
//第一个CPU核
void add(int *a, int *b, int *c){
int tid = 0;
while(tid < N){
c[tid] = a[tid] + b[tid];
tid += 2;
}
}
//第二个CPU核
void add(int *a, int *b, int *c){
int tid = 1;
while(tid < N){
c[tid] = a[tid] + b[tid];
tid += 2;
}
}
基于GPU的矢量求和
下面是基于GPU的矢量求和代码
#include "../common/book.h"
#define N 10
__global__ add(int *dev_a, int *dev_c, int *dev_c){
int tid = blockIdx.x; //计算该索引处的数据
if(tid < N){
c[tid] = a[tid] + b[tid];
}
}
int main(){
int a[N], b[N], c[N];
int *dev_a, *dev_b, *dev_c;
//在GPU上分配内存
HANDLE_ERROR(cudaMalloc((void**)&dev_a, N * sizeof(int)));
HANDLE_ERROR(cudaMalloc((void**)&dev_b, N * sizeof(int)));
HANDLE_ERROR(cudaMalloc((void**)&dev_c, N * sizeof(int)));
//在CPU上为数组"a"和"b"赋值
for(int i = 0; i < N; i++){
a[i] = -i;
b[i] = i * i;
}
//将数组"a"和"b"复制到GPU
HANDLE_ERROR(cudaMemcpy(dev_a, a, N * sizeof(int), cudaMemcpyHostToDevice));
HANDLE_ERROR(cudaMemcpy(dev_b, b, N * sizeof(int), cudaMemcpyHostToDevice));
add<<<N, 1>>>(dev_a, dev_b, dev_c);
//将数组"c"从GPU复制到CPU
HANDLE_ERROR(cudaMemcpy(c, dev_c, N * sizeof(int), cudaMemcpyDeviceToHost));
//显示结果
for(int i = 0; i < N; i++){
printf("%d + %d = %d\n", a[i], b[i], c[i]);
}
//释放在GPU上分配的内存
cudaFree(dev_a);
cudaFree(dev_b);
cudaFree(dev_c);
return 0;
}
在示例代码中,调用add函数的尖括号内的数值是«<N, 1»>,其中第一个参数表示设备在执行核函数时使用的并行线程块的数量。比如如果制定的事kernel«<256, 1»>(),那么将有256个线程块在GPU上运行。
在add函数里面,我们可以使用blockIdx.x获取具体的线程块(blockIdx是一个内置变量,不需要定义它),通过这种方式可以让不同的线程块并行执行数组的矢量相加。
下一章将会详细解释线程块以及线程之间的通信机制和同步机制。
Comments