CUDA入门

CUDA是什么

CUDA(Compute Unified Device Architecture),是由NVIDIA公司创立的基于他们公司生产的图形处理器GPUs(Graphics Processing Units,可以通俗的理解为显卡)的一个并行计算平台和编程模型。通过CUDA,GPUs可以很方便地被用来进行通用计算(有点像在CPU中进行的数值计算等等)。在没有CUDA之前,GPUs一般只用来进行图形渲染(如通过OpenGL,DirectX)。开发人员可以通过调用CUDA的API,来进行并行编程,达到高性能计算目的。NVIDIA公司为了吸引更多的开发人员,对CUDA进行了编程语言扩展,如CUDA C/C++,CUDA Fortran语言。注意CUDA C/C++可以看作一个新的编程语言,因为NVIDIA配置了相应的编译器nvcc,CUDA Fortran一样。

如果粗暴的认为C语言工作的对象是CPU和内存条(称为主机内存),那么CUDA C工作的的对象就是GPU及GPU上的内存(称为设备内存),且充分利用了GPU多核的优势及降低了并行编程的难度。一般通过C语言把数据从外界读入,再分配数据,给CUDA C,以便在GPU上计算,然后再把计算结果返回给C语言,以便进一步工作,如进一步处理及显示,或重复此过程。

Ubuntu系统安装CUDA

1. 检查

$ lspci | grep -i nvidia
该命令会列出和NVIDIA相关的硬件设备

// 检查系统版本
$ uname -m && cat /etc/*release
// 检查gcc版本,是否符合ubuntu系统要求
$ gcc --version
2. 官网下载驱动

https://developer.nvidia.com/cuda-downloads 
根据提示选择合适的安装包(Deb包,Deb包安装较为简单,但是安装过程中提示不稳定,不过用着也没啥出错的地方)
3. 安装
先安装必要的库:

sudo apt-get install freeglut3-dev build-essential libx11-dev libxmu-dev libxi-dev libgl1-mesa-glx libglu1-mesa libglu1-mesa-dev
cd到刚刚下载的deb包所在的目录,然后执行:

$ sudo dpkg -i cuda-repo-<distro>_<version>_<architecture>.deb // 下载的deb包
$ sudo apt-get update
$ sudo apt-get install cuda
可能需要下载较长时间,但是没关系,放在那等着就是。
4. 配置环境

// 酌情修改
$ export PATH=/usr/local/cuda-6.5/bin:$PATH
$ export LD_LIBRARY_PATH=/usr/local/cuda-6.5/lib64:$LD_LIBRARY_PATH
// 配置完环境后,执行如下命令,使其立刻生效
$ source .bashrc
5. 验证

// 查看GPU信息
nvidia-smi
主要概念与名称

主机

将CPU及系统的内存(内存条)称为主机。

设备

将GPU及GPU本身的显示内存称为设备

线程(Thread)

一般通过GPU的一个核进行处理(可以表示成一维,二维,三维)

线程块(Block)

1. 由多个线程组成。

2. 各block是并行执行的,block间无法通信,也没有执行顺序。

3. 注意线程块的数量限制为不超过65535(硬件限制)。

线程格(Grid)

由多个线程块组成。

线程束

在CUDA架构中,线程束是指一个包含32个线程的集合,这个线程集合被“编织在一起”并且“步调一致”的形式执行。在程序中的每一行,线程束中的每个线程都将在不同数据上执行相同的命令。

核函数(Kernel)

1. 在GPU上执行的函数通常称为核函数。

2. 一般通过标识符__global__修饰,调用通过<<<参数1,参数2>>>,用于说明内核函数中的线程数量,以及线程是如何组织的。

3. 以线程格(Grid)的形式组织,每个线程格由若干个线程块(block)组成,而每个线程块又由若干个线程(thread)组成。

4. 是以block为单位执行的。

5. 能在主机端代码中调用。

6. 调用时必须声明内核函数的执行参数。

7. 在编程时,必须先为kernel函数中用到的数组或变量分配好足够的空间,再调用kernel函数,否则在GPU计算时会发生错误,例如越界或报错,甚至导致蓝屏和死机。

dim3结构类型

1. dim3是基亍uint3定义的矢量类型,相当亍由3个unsigned int型组成的结构体。uint3类型有三个数据成员unsigned int x; unsigned int y; unsigned int z;

2. 可使用亍一维、二维或三维的索引来标识线程,构成一维、二维或三维线程块。

3. dim3结构类型变量用在核函数调用的<<<,>>>中。

4. 相关的几个内置变量 :

4.1  threadIdx,获取线程thread的ID索引;如果线程是一维的那么就取threadIdx.x,二维的还可以多取到一个值threadIdx.y,以此类推到三维threadIdx.z。

4.2 blockIdx,线程块的ID索引;同样有blockIdx.x,blockIdx.y,blockIdx.z。

4.3 blockDim,线程块的维度,同样有blockDim.x,blockDim.y,blockDim.z。

4.4 gridDim,线程格的维度,同样有gridDim.x,gridDim.y,gridDim.z。

5. 对于一维的block,线程的threadID=threadIdx.x。

6. 对于大小为(blockDim.x, blockDim.y)的 二维 block,线程的threadID=threadIdx.x+threadIdx.y*blockDim.x。

7. 对于大小为(blockDim.x, blockDim.y, blockDim.z)的 三维 block,线程的threadID=threadIdx.x+threadIdx.y*blockDim.x+threadIdx.z*blockDim.x*blockDim.y。

8. 对于计算线程索引偏移增量为已启动线程的总数。如stride = blockDim.x * gridDim.x; threadId += stride。


函数修饰符
1. __global__,表明被修饰的函数在设备上执行,但在主机上调用。

2. __device__,表明被修饰的函数在设备上执行,但只能在其他__device__函数或者__global__函数中调用。

常用的GPU内存函数
cudaMalloc()
1. 函数原型: cudaError_t cudaMalloc (void **devPtr, size_t size)。
2. 函数用处:与C语言中的malloc函数一样,只是此函数在GPU的内存你分配内存。
3. 注意事项:
3.1. 可以将cudaMalloc()分配的指针传递给在设备上执行的函数;
3.2. 可以在设备代码中使用cudaMalloc()分配的指针进行设备内存读写操作;
3.3. 可以将cudaMalloc()分配的指针传递给在主机上执行的函数;
3.4. 不可以在主机代码中使用cudaMalloc()分配的指针进行主机内存读写操作(即不能进行解引用)。
cudaMemcpy()
1. 函数原型:cudaError_t cudaMemcpy (void *dst, const void *src, size_t count, cudaMemcpyKind kind)。
2. 函数作用:与c语言中的memcpy函数一样,只是此函数可以在主机内存和GPU内存之间互相拷贝数据。
3. 函数参数:cudaMemcpyKind kind表示数据拷贝方向,如果kind赋值为cudaMemcpyDeviceToHost表示数据从设备内存拷贝到主机内存。
4. 与C中的memcpy()一样,以同步方式执行,即当函数返回时,复制操作就已经完成了,并且在输出缓冲区中包含了复制进去的内容。
5. 相应的有个异步方式执行的函数cudaMemcpyAsync(),这个函数详解请看下面的流一节有关内容。
cudaFree()
1. 函数原型:cudaError_t cudaFree ( void* devPtr )。
2. 函数作用:与c语言中的free()函数一样,只是此函数释放的是cudaMalloc()分配的内存。

下面实例用于解释上面三个函数

#include <stdio.h>
#include <cuda_runtime.h>
__global__ void add( int a, int b, int *c ) {
    *c = a + b;
}
int main( void ) {
    int c;
    int *dev_c;
    //cudaMalloc()
    cudaMalloc( (void**)&dev_c, sizeof(int) );
    //核函数执行
    add<<<1,1>>>( 2, 7, dev_c );   
    //cudaMemcpy()
    cudaMemcpy( &c, dev_c, sizeof(int),cudaMemcpyDeviceToHost ) ;
    printf( "2 + 7 = %d\n", c );
    //cudaFree()
    cudaFree( dev_c );
 
    return 0;
}


















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值