CUDA编程实战-1

前言:
GPU作为如今主流的异构计算平台,收到越来越多的追捧,尤其是深度学习的发展,更加催生了其应用。作为并行计算领域的一枚,必须要好好学习一番啦。

GPU各种型号整理

G80、 Fermi、Kepler、 Maxwell 和 Pascal 5 代架构整理:TODO

安装

TODO

基础知识

(1)函数的类型。
__host__ float HostFunc():默认情况。被host函数调用,在host函数上执行。(host指cpu端)
__devide__ float DeviceFunc(); 被设备函数调用,在设备上执行。(设备指gpu)
__global__ void KernelFunc(): 被host函数调用,在设备上执行。
注意:
(a)__global__ 函数返回值必须为void
(b)设备上执行的函数:不能是递归的,函数参数必须固定,不能再函数内部使用static变量。

(2)变量类型
__shared__ A[4];//在share memory,块内线程共享。
设备上的函数,声明的变量都是存在register上的,存不下的放到local memory;
cudaMalloc()的空间是在设备的global memory上的。
(3)几个cuda头文件

    #include<cuda_runtime.h>:  里面包含cuda 的基本routine
    #include <helper_cuda.h>:里面包含error处理等routine
    #include <helper_functions.h>: 这里面是什么???

几个例子

CUDA安装完后,默认安装路径(下文成为:$CUDA_ROOT)是:

/usr/local/cuda-8.0(假设安装的是8.0)

在$CUDA_ROOT下的samples下,有很多例子。先从这些例子学起。

学习列表

1. vectorAdd.cu:实现向量加(一位dim) 

2. cudaOpenMP.cu:使用openmp多线程,调用不同的gpu(多gpu编程) 

3. simpleMPI:mpi与cuda的结合。跟孙乔跟我说的差不多。mpi负责分配任务,然后每个任务都是在cuda上执行的。设计的很分离,不耦合。good

4. simplePrintf:在cuda上打印。

5. simpleAssert:通过是第几个线程,若线程数超过N, 就assert error

6. matrixMul:分块的矩阵乘,用到了share memory。

7. matrixMulCUBLAS:调用cublas计算gemm的例子。

8. samples/6_Advanced/scan:good。看看这个scan是如何实现的

9. samples/6_Advanced/ reduction:

10. samples/6_Advanced/ transpose :

11. samples/6_Advanced/ mergeSort:

12. samples/3_Imaging/convolutionFFT2D : fft版本的卷积

例子讲解

实战1只介绍前5个函数。我们把cuda routine挑出来。挨个学习。
变量:

    1. cudaError_t err = cudaSuccess;
    //cudaError_t类型,表示错误类型。cudaSuccess表示成功。一般cuda routine的返回值都是cudaError_t类型,表示函数是否执行成功。  

    2. printf("%s\n", cudaGetErrorString(cudaGetLastError()));
    //输出错误时,使用以上函数转化为string。

函数:
1. err = cudaMalloc((void **)&d_A, size);
//动态内存申请函数,在设备的global memory上申请size个字节空间。

2. err = cudaMemcpy(d_A, h_A, size, cudaMemcpyHostToDevice);or
    err = cudaMemcpy(h_A, d_A, size, cudaMemcpyDeviceToHost);
    //内存拷贝函数:从cpu上的内存h_A上拷贝size个字节数据到gpu上的内存d_A。反之,一样。

3. int threadsPerBlock = 256;
    int blocksPerGrid =(nElements + threadsPerBlock - 1) / threadsPerBlock;
    vectorAdd<<<blocksPerGrid, threadsPerBlock>>>(d_A, d_B, d_C, nElements);
    //前2句,表示Grid,block都是1维时,设置网格内的块数,每块内的线程数。
    //最后一句,启动kernel(运行在gpu端的函数)函数。
    //注意前2句可以改成。dim3 threadsPerBlock(256);这种形式。

4. err = cudaGetLastError();
//启动kernel函数时,并没有返回值,通过这个调用这个函数,查看kernel函数是否启动成功。

5. err = cudaFree(d_A);
//释放使用cudaMalloc申请的空间。

6. err = cudaMemset(d_a, 0, size)
//类似于memset函数。将d_A的size个字节置0.

当gpu有好几块时,获取设备,选择设备的几个函数:

1. int num_gpus = 0;
  cudaGetDeviceCount(&num_gpus);//若num_gpus为0,表示没有检测到gpu。
2.  cudaDeviceProp dprop;
    cudaGetDeviceProperties(&dprop, id);//i代表第几块gpu,从0开始标号。
    printf("   %d: %s\n", i, dprop.name);or
    printf("Device %d: \"%s\" with Compute %d.%d capability\n",devID, props.name, props.major, props.minor);

3. err = cudaSetDevice(id);//选择第id个设备。
    err = cudaGetDevice(&gpu_id);//获取选择的设备号,通过id返回。

4. devID = findCudaDevice(argc, (const char **)argv);
    //devID为int类型。系统帮你选择最合适的gpu设备。此时DevID里存的是被选择的设备号吗。自己试试看看。
    err = cudaGetDevice(&devID);
    //选完之后,获取设备号。
5. error = cudaDeviceSynchronize();//TODO???
6. 

关于计算地址:个人感觉这里是最迷糊人的。需要好好想清楚。
思路:先求块首地址,再加块内地址。

  1. grid是一维,block是一维,如何计算线程号:

    注意:这个好理解。blockDim相当于一个块内的线程总数。blockIdx.x相当于当前线程是第几块。threadIdx.x相当于当前线程时第几个线程(块内)。(有空时画个图便于理解)

  2. Grid是2维,block是3维:

    dim3 dimGrid(2, 2);
    dim3 dimBlock(2, 2, 2);

全局块号:blockIdx.y*gridDim.x+blockIdx.x
全局块内线程号:threadIdx.z*blockDim.x*blockDim.y+threadIdx.y*blockDim.x+threadIdx.x
注意:2维的可以以列优先存储的矩阵方式来理解。3维的时候,可以理解成多层的矩阵。

关于在设备上打印(输出,主要用于debug):

compute capability less than 2.0, the function cuPrintf is called.
others call printf();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值