CUDA_基础
CUDA编程形式
主函数 接口函数 内核函数
主函数调用接口函数,接口函数需要的形参在主函数中声明分配内存空间(malloc())、赋好值,调用完毕释放参数内存(free())。
接口函数调用内核函数(__global void kernel(参数)),内核函数需要用到的形参在接口函数中声明分配显存空间(cudaMalloc()),将数据复制到显存(cudaMemcpy),调用内核(设置好线程块数量,线程数量),计算结果从显存复制回主机,释放参数内存(cudaFree())。
Hello World
将CPU以及系统的内存称为主机,将GPU及其内存称为设备,在GPU设备上执行的函数通常称为核函数(Kernel);
核函数编写规则:带有修饰符__global__;
#include <iostream> __global__ void kernel(void) { }
这个修饰符主要作用是告诉编译器,函数应该编译为在设备而不是主机上运行,在这个例子中,函数kernel()将被交给编译设备代码的编译器,而main()函数将被交给主机编译器
核函数调用规则:带有修饰字符<<<1,1>>>;
参数传递
1 #include <iostream> 2 #include "book.h" 3 { 4 int main() 5 { 6 int c; 7 int *dev_c; 8 HANDLE_ERROR(cudaMalloc((void **)&dev_c,sizeof(int))); 9 add<<<1,1>>>(2,7,dev_c); 10 HANDLE_ERROR(cudaMemcpy(&c,dev_c,sizeof(int),cudaMemcpyDeviceToHost)); 11 printf("2+7=%d\n",c); 12 cudaFree(dev_c); 13 return 0; 14 } 15 } 16 17 __global__ void add(int a,int b,int *c) 18 { 19 *c=a+b; 20 }
尖括号表示要将一些参数传递给运行时系统,这些参数并不是传递给设备代码的参数,而是告诉运行时如何启动设备代码。
1 int main(void) 2 { 3 kernel<<<1,1>>>(); 4 printf("Hello World\n"); 5 return 0; 6 }
当设备执行任何有用的操作时,都需要分配内存;
cudaMalloc()分配内存:作用是告诉CUDA运行时在设备上分配内存,第一个参数是一个指针,指向用于保存新分配内存地址的变量,第二参数是分配内存的大小。
设备指针的使用限制总结如下:
可以将cudaMalloc()分配的指针传递给设备上执行的函数;可以在设备代码中使用cudaMalloc()分配的指针进行内存的读/写操作;
可以将cudaMalloc()分配的指针传递给在主机上执行的函数;不能在主机代码中使用cudaMalloc()分配的指针进行内存的读/写操作。
简明之,主机指针只能访问主机代码中的内存,设备指针只能访问设备代码中的内存。
cudaMemcpy()函数:其最后一个参数来指定设备内存指针究竟是源指针还是目标指针。
设备的使用
通过调用cudaGetDeviceCount()获得CUDA设备的数量,然后对每个设备进行迭代并查询各个设备的相关信息,但此种迭代操作执行起来过于繁琐,因此提供了一种自动方式来执行这个迭代操作。
(1)找出我们希望设备拥有的属性并将这些属性填充到一个cudaDeviceProp结构;
(2)将其传递给cudaChooseDevice()函数;
(3)cudaChooseDevice()函数返回一个设备ID,可将这个ID传递给cudaSetDevice(),随后,所有的设备操作都将在这个设备上执行。
1 cudaDeviceProp prop; 2 int dev; 3 HANDLE_ERROR(cudaGetDevice(&dev)); 4 printf("ID of current CUDA device: %d\n",dev); 5 //属性填充 6 memset(&prop,0,sizeof(cudaDeviceProp)); 7 prop.major=1; 8 prop.minor=3; 9 //设备是否满足条件 10 HANDLE_ERROR(cudaChooseDevice(&dev,&prop)); 11 printf("ID of CUDA device closest to revision 1.3: %d\n",dev); 12 HANDLE_ERROR(cudaSetDevice(dev));
首先我看了讲解CUDA基础部分以后,大致对CUDA的基本了解如下:
第一:CUDA实行并行化的过程分为两部分,一个是线程块之间的并行(这是在每个线程网格中grid进行的),一个是对于每一个线程块内部各线程之间的并行化(每个block内部);
第二:CUDA程序编写的流程:
分为主机端的程序(CPU)与设备端的程序(GPU)
主机端:
初始化GPU→数据准备工作→为输入输出参数分配显存空间→将输入参数从主机端复制到显存→内核启动设置→将输出参数复制到显存空间→释放在设备端分配的显存空间
数据准备工作:这里的数据指的是待放入GPU上执行的计算任务
在显存中可以分配的空间有两种:线性存储器和CUDA数组
将输入参数从主机端复制到显存:执行cudaMemcpyHostToDevice
将输出参数复制到显存空间:执行cudaMemcpyDeviceToHost
内核启动设置:调用内核函数kernelFunc<<<N,M>>>(d_a,d_b,d_c) N表示执行一个grid中有多少个并行block块,M表示执行一个block中有多少个并行线程(thread)
CUDA在医学图像上的应用
提前发现乳腺癌:
超声波成像技术与乳房X线照片结合使用,以辅助乳腺癌的治疗与诊断,受到常规的乳房超声波技术的局限性影响,已经研发了TechniScan医疗系统,其采用了一种三维的超声波成像方法(该方法由于计算量的限制而无法投入实际应用)。简而言之,在将采集到的超声波数据转换为3D图像时需要执行非常耗时的计算,因此使得该项技术无法投入实际应用。
CUDA计算流体力学及环境科学方面的应用
未完待续。。。