参考了很多大神的内容,并非完全原创,只是为了查漏补缺,记录自己的学习过程。个人水平有限,错误难免,欢迎讨论。
CUDA(Compute Unified Device Architecture),是由Nvidia推出的通用并行计算架构。 开发人员现在可以使用类C语言来为CUDA™架构编写并行程序!
CUDA编程允许你的程序执行在异构系统上,即CPU和GPU,二者有各自的存储空间,并由PCI-Express 总线区分开。因此,我们应该先注意二者术语上的区分:
Device: GPU and its memory (device memory))
代码中,一般用h_前缀表示host memory,d_表示device memory。
kernel是CUDA编程中的关键,他是运行GPU设备上的代码,用标示符global修饰。
host可以独立于host进行大部分操作。当一个kernel启动后,控制权会立刻返还给CPU来执行其他额外的任务。所以,CUDA编程是异步的。一个典型的CUDA程序包含由并行代码补足的串行代码,串行代码由host执行,并行代码在device中执行。host端代码是标准C,device是CUDA C代码。我们可以把所有代码放到一个单独的源文件,也可以使用多个文件或库。NVIDIA C编译器(nvcc)可以编译host和device生成可执行程序。
这里再次说明下CUDA程序的处理流程:
cuda程序将系统区分成host和device,二者有各自的memory。kernel可以操作device memory,为了能很好的控制device端内存,CUDA提供了几个内存操作函数,为了保证和易于学习,CUDA C 的风格跟C很接近:
标准C | CUDA C |
---|---|
malloc | cudaMalloc |
memcpy | cudaMemcpy |
memset | cudaMemset |
free | cudaFree |
我们主要看看cudaMencpy,其函数原型为:
cudaError_t cudaMemcpy ( void* dst, const void* src, size_t count,cudaMemcpyKind kind )
其中cudaMemcpykind的可选类型有:
cudaMemcpyHostToHost
cudaMemcpyHossToDevice
cudaMemcpyDeviceToHost
cudaMemcpuDeviceToDevice
对于返回类型cudaError_t,如果正确调用,则返回cudaSuccess,否则返回cudaErrorMemoryAllocation。可以使用char* cudaGetErrorString(cudaError_t error)将其转化为易于理解的格式。
CUDA线程分成Grid和Block两个层次,由一个单独的kernel启动的所有线程组成一个grid,grid中所有线程共享global memory。一个grid由许多block组成,每个block由许多线程组成,grid和block都可以是一维二维或者三维。
这里介绍几个CUDA内核函数的私有变量:
- blockIdx:block的索引,blockIdx.x表示block的x坐标。
- threadIdx:线程索引,同理blockIdx。
- blockDim:block维度,上图中blockDim.x=5.
- gridDim:grid维度,同理blockDim。
CUDA kernel的调用格式为:
kernel_name<<<grid, block>>>(argument list);
其中grid和block即为上文中介绍的类型为dim3的变量。通过这两个变量可以配置一个kernel的线程总和,以及线程的组织形式。例如:
kernel_name<<<4, 8>>>(argumentt list);
该行代码表明有grid为一维,有4个block,block为一维,每个block有8个线程,故此共有4*8=32个线程。
注意,不同于c函数的调用,所有CUDA kernel的启动都是异步的,当CUDA kernel被调用时,控制权会立即返回给CPU。
kernel的限制: