我的一点观点
我个人觉得 nvidia 的 CUDA 太封闭了,不是很能明白这样封闭的产品怎么能够长久生存,仅仅是因为家大业大么,如果有家公司推出了八成性能的 GPU,但是整套开发的生态非常友好,是不是会像 Android 取代诺基亚一样,还是 nvidia 就是苹果,就算封闭环境也能保证强劲体验,这我也不好说了。
安装
CUDA+cudnn 装起来挺麻烦的,反正如果有错误的话,可以检查 CUDA samples 里面的 deviceQuery 是否成功,如果不成功可以用 strace 看一下少了什么东西,再想办法安装上去。检查 cudnn samples 是否成功也是一样的,里面有一个 mnistDNN 的例子。
Hello World!
1 | #include <stdio.h> |
以上就是一段 GPU 的 Hello world,执行下面的代码可以看到我们在 GPU 上并行执行了十个 “Hello world!” 的打印。
1 | nvcc -arch sm_20 hello.cu -o hello |
CUDA
CUDA 全称 Compute Unified Device Architecure,用于定义 GPU 的架构标准。GPU 的工作方式主要依赖于多核心的并行计算,CUDA 提供了方便的模型进行这种模式的编程,下面就会简单介绍一下 CUDA 的架构以及基于 GPU 的编程。
CUDA 一次启动的线程称为网格(grid),网格中包含块,每个块包含新程,是一个二维的模式,这张图片就说明得很清晰,首先是由 Grid 组成,然后每个 Block 有 shared memory,同 block 的线程可以访问 shared memory,不同的 Block 的线程只能访问全局的内存,这种结构也方便设计和实现并行算法。
CUDA 的编译器 nvcc 是 gcc 的一个扩展,支持编写运行在 GPU 上的函数,其中的,<<x, y>>
扩展就是用来指定 block 和 thread 的数量的。
比如典型的俩个向量相加的例子:
1 | void sumArrayOnHost(float *A, float *B, float *C, const int N) { |
但是 GPU 的核函数怎么写呢,是这样的。
1 | __global__ void sumArrayOnGPU(float *A, float *N, float C) { |
然后,如果 N 是 32 的话,可以如下调用,__global__
表示这个函数可以在 host 调用也可以在 GPU 上调用,用个 32 个线程的 block 算这个向量和,N 隐性包含在了定义中。
1 | sumArrayOnGPU<<1, 32>>(float *A, float *B, float *C) |
线程组织
基于 CPU 和 GPU 的异构计算平台可以优势互补,CPU 负责处理逻辑复杂的串行程序,而 GPU 重点处理数据密集型的并行计算程序,从而发挥最大功效。
线程组织主要依据网格(现在网络这个词其实比较容易混淆,可以是神经网络可以是计算机网络,这里指的是线程的组织形式)模型。主要分 grid, block, thread,组织的方式就靠索引来决定,可以通过 block 索引和 thread 索引进行线程的定位。CUDA 提供了块内线程同步的方法,但是没有提供块间同步的原语。
线程束分化
线程束 = warp
CPU 有很强的分支预测的能力,会预加载指令,如果预测正确,执行代价就很小,但是 GPU 在这方面是很弱的,因为线程束当中如果执行分支出现不同,那出现分支的线程就会被禁止执行,这也是用 GPU 做并行编程的时候需要参考的一个很重要的因素,保证并行的线程出现线程束分化的情况尽量少。
下面就是线程束分化的例子。
并行归约问题
并行归约是指如何对并行问题进行归约,比如说相邻配对和交错配对。
设备管理
管理 GPU 主要是通过 CUDA API 或者 nvidia-smi
命令来获取。
SM(流式多处理器)
上面讲的是抽象上的分层,但是实际的物理层面承载 GPU 的是 SM。最早的 GPU 架构叫 Fermi,然后是 Kepler,然后才是 Tesla。一般说的 cm_20
, sm_20
就指这种计算能力和架构,新款的 GPU 计算能力要更强一点。
内存模型
CUDA 的内存模型和 CPU 是类似的也是多级的结构。线程有局部内存,块之间有全局内存。GPU 和 CPU 都是主要采用 DRAM 来做主存,CPU 的一级缓存会用 SRAM 做。CPU 的多级缓存对用户来说不是很需要考虑,尽量屏蔽了其中的细节,但是 GPU 相对来说会把这种分级结构暴露给用户,这对编程来说也是一种新的挑战。
后面
用 GPU 计算一些计算密集型的程序,速度是真快,比 CPU 块很多,所以这也是为什么大量深度学习的应用都要通过 GPU 加速的原因。