我们将CPU以及主存称为主机,将GPU以及显存称为设备。
#include
__global__ void kernel()
{
}
int main()
{
kernel<<<1,1>>>();
printf("hello world\n");
return 0;
}
和普通的C代码很相似,也有所不同。kernel()函数带有修饰符__global__;主函数中对kernel()的调用,带有修饰符<<<1,1>>>。
在GPU设备上执行的函数通常被称为核函数。其实,cuda中为标准C增加的__global__修饰符就是告诉编译器,该函数应该被交给编译设备代码的编译器,而不是主机编译器。至于调用设备代码为什么要用尖括号,我们后续再说。
核函数当然是可以传参的,看看代码。
#include
__global__ void add(int a, int b, int *c)
{
*c = a + b;
}
int main()
{
int c;
int *dev_c;
cudaMalloc((void **)&dev_c,sizeof(int));
add<<<1,1>>>(10,20,dev_c);
cudaMemcpy(&c,dev_c,sizeof(int),cudaMemcpyDeviceToHost);
printf("10+20=%d\n",c);
}
输出结果如下:
这里有一些要注意的点
1)当设备执行任何有用的操作时,都要分配内存。cudaMalloc非常类似C中的malloc,其目的是在设备中分配某字节大小的空间。
在本例中,dev_c指针就是设备中的分配地址,但是dev_c是存储在主存中的,因此cudaMalloc的第一个参数类型是(void **),指向指针的指针,代表了dev_c在主存中的地址。换个简单的说法就是,这块地址里面放的是dev_c,而dev_c的值是设备中分配的地址。
2)可以将设备指针传递给核函数;可以在核函数中对设备指针进行内存读写;可以将设备指针传递给主机上执行的函数;但一定不能在主机代码中对设备指针进行内存读写(解引用)
3)cudaMemcpy和memcpy类似,是内存拷贝函数。
主机到设备:cudaMemcpy(d_A,h_A,nBytes,cudaMemcpyHostToDevice)
设备到主机:cudaMemcpy(h_A,d_A,nBytes,cudaMemcpyDeviceToHost)
4)主机指针只能访问主机代码中的内存,设备指针只能访问设备代码的内存。这也是为什么调用核函数add()时,要分配dev_c,再将计算好的dev_c中的值传回主机。如果直接是&c作为参数传进核函数,则会出现设备代码访问主机内存的问题,不可。
写了几个基本的练练手
1)swap
#include
__global__ void swap(int *a,int *b)
{
int t;
t=*a;
*a=*b;
*b=t;
}
int main()
{
int a=10;
int b=20;
int *dev_a,*dev_b;
cudaMalloc((void **)&dev_a,sizeof(int));
cudaMalloc((void **)&dev_b,sizeof(int));
cudaMemcpy(dev_a,&a,sizeof(int),cudaMemcpyHostToDevice);
cudaMemcpy(dev_b,&b,sizeof(int),cudaMemcpyHostToDevice);
swap<<<1,1>>>(dev_a,dev_b);
cudaMemcpy(&a,dev_a,sizeof(int),cudaMemcpyDeviceToHost);
cudaMemcpy(&b,dev_b,sizeof(int),cudaMemcpyDeviceToHost);
printf("After change a is %d b is %d\n",a,b);
}
#include
__global__ void change(int *a)
{
for(int i=0;i<5;i++)
{
a[i]+=10;
}
}
int main()
{
int k[5]={1,2,3,4,5};
int *dev_k;
cudaMalloc((void **)&dev_k,sizeof(int)*5);
cudaMemcpy(dev_k,k,sizeof(int)*5,cudaMemcpyHostToDevice);
change<<<1,1>>>(dev_k);
cudaMemcpy(k,dev_k,sizeof(int)*5,cudaMemcpyDeviceToHost);
for(int i=0;i<5;i++)
{
printf("%d\n",k[i]);
}
}
#include
#include
using namespace std;
__global__ void DevAdd(int a[4][4],int b[4][4],int c[4][4])
{
int i=blockIdx.x*blockDim.x+threadIdx.x;
int j=blockIdx.y*blockDim.y+threadIdx.y;
c[i][j]=a[i][j]+b[i][j];
}
int main()
{
int a[4][4]={{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4}};
int b[4][4]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int c[4][4];
memset(c,0,4*sizeof(int[4]));
dim3 threadPerB(2,2);
dim3 numB(4/threadPerB.x,4/threadPerB.y);
int (*deva)[4];
int (*devb)[4];
int (*devc)[4];
cudaMalloc(&deva,4*sizeof(int[4]));
cudaMalloc(&devb,4*sizeof(int[4]));
cudaMalloc(&devc,4*sizeof(int[4]));
cudaMemcpy(deva,a,4*sizeof(int[4]),cudaMemcpyHostToDevice);
cudaMemcpy(devb,b,4*sizeof(int[4]),cudaMemcpyHostToDevice);
DevAdd<<<numB,threadPerB>>>(deva,devb,devc);
cudaMemcpy(c,devc,4*sizeof(int[4]),cudaMemcpyDeviceToHost);
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
cout<<c[i][j]<<" ";
}
cout<<endl;
}
cudaFree(deva);
cudaFree(devb);
cudaFree(devc);
}
要注意的是,数组传参实际上传的是指针,整型或浮点型是值传递,注意区别!