跑一个最简单的Cuda程序
好,现在我们先来看一个最简单的Cuda程序。就像你们学习C语言的时候,直接运行里一个打印“Hello,world”的程序之后老师才会开始跟你解释,我们边练边学。
#include <stdio.h>
__global__ void vector_add(const int *a, const int *b, int *c) {
*c = *a + *b;
}
int main(void) {
const int a = 2, b = 5;
int c = 0;
int *dev_a, *dev_b, *dev_c;
cudaMalloc((void **)&dev_a, sizeof(int));
cudaMalloc((void **)&dev_b, sizeof(int));
cudaMalloc((void **)&dev_c, sizeof(int));
cudaMemcpy(dev_a, &a, sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(dev_b, &b, sizeof(int), cudaMemcpyHostToDevice);
vector_add<<<1, 1>>>(dev_a, dev_b, dev_c);
cudaMemcpy(&c, dev_c, sizeof(int), cudaMemcpyDeviceToHost);
printf("%d + %d = %d, Is that right?\n", a, b, c);
cudaFree(dev_a);
cudaFree(dev_b);
cudaFree(dev_c);
return 0;
}
先用nsight创建一个Cuda运行时项目,然后把代码抄进去,运行一下。
如果你喜欢命令行的方式,你可以把代码抄进一个文本里面,命名为vector_add.cu
,然后执行命令nvcc vector_add.cu
,然后执行编译生成的a.out
文件。
没错,nvcc就像gcc一样用,大多数功能都是相同的,至于.cu
这个后缀,这仅仅是个为了说明这是个cuda的程序而已。
如果顺利的话,你应该会得到这样的结果输出
2 + 5 = 7. Is that right?
没错,得到这样的结果就说明的Cuda程序已经可以正确运行了。
现在,让我们来分析一下,上面的代码究竟做了些什么东西。
/*
* 为了方便起见,我甚至把一些出错判断都去掉了。
* 比如申请内存空间,谁都没法保证内存一定有空间给你申请的不是吗?
* 之所以这样,是为了方便我们看清楚一个基本的cuda程序的执行流程。
*/
#include <stdio.h>
/*
* 这个是要放在显卡上面跑的函数,除了前面多了一个__global__之外,
* 这个函数其实很简单不是吗?
*/
__global__ void vector_add(const int *a, const int *b, int *c) {
*c = *a + *b;
}
int main(void) {
//这里定义了三个整形变量a,b和c。他们位于主存上。
const int a = 2, b = 5;
int c = 0;
//这里定义了三个整形指针。为什么要用指针?
//因为这是在显存上申请空间,对于主机来说,它只能也只需要知道一个映射的地址就够了。
int *dev_a, *dev_b, *dev_c;
//为三个指针申请显存空间,虽然只是一个int,4个字节的大小。
cudaMalloc((void **)&dev_a, sizeof(int));
cudaMalloc((void **)&dev_b, sizeof(int));
cudaMalloc((void **)&dev_c, sizeof(int));
//将内存上的数据复制到显存上,这里复制了a和b两个加数。
cudaMemcpy(dev_a, &a, sizeof(int), cudaMemcpyHostToDevice);
cudaMemcpy(dev_b, &b, sizeof(int), cudaMemcpyHostToDevice);
//调用那个要在显卡上面执行的函数。还记得main前面的那个函数么?
vector_add<<<1, 1>>>(dev_a, dev_b, dev_c);
//这里把dev_c上面的值复制到内存的变量c上。
cudaMemcpy(&c, dev_c, sizeof(int), cudaMemcpyDeviceToHost);
//输出结果
printf("%d + %d = %d, Is that right?\n", a, b, c);
//释放显存
cudaFree(dev_a);
cudaFree(dev_b);
cudaFree(dev_c);
return 0;
}
这就是一个简单的Cuda程序流程,在主机上准备好数据,然后把数据送到显卡,显卡处理好数据后,再把显卡送回到主机内存上。
这个例子非常简单,相信如果你写过几个C语言的程序,很容易就看懂这段代码,毕竟有个几个函数,除了过了个cuda前缀之外,和平常使用的没有很多差别。
你肯定想问,那个__global__
是个什么鬼?那个<<<1, 1>>>
又是个什么鬼?这些等到下节我们再来解答。