【OpenCL】OpenCL性能与AMD的显卡架构其间的思考

【OpenCL】OpenCL性能与AMD的显卡架构之间的思考

引言

对于OpenCL程序来说,开发者只要知道大概的OpenCL C的语法和Runtime API调用,把for循环替换成kernel线程,就能使用OpenCL获得明显的加速比。例如,矩阵的加减法运算,模版匹配算法等。这从另一方面体现出了OpenCL的强大之处:我们不需要详细了解运行目标设备的参数和架构,就能用统一的方法跨平台,多、超线程编程,实现算法的加速。不过对于有些复杂的算法,我们发现即使为它分配了大量线程,做到了高度的并发,但由于内核程序撰写质量的问题,导致GPU占用率并不高,或者内存访问方式缺陷,使得性能离预期的加速比相差甚远,很可能是由于CPU设备上的程序(C++)写法并不适合GPU架构;另一方面,有时候我们要为特殊的OpenCL设备进行优化,例如移动平台、AMD HD79xx系列的GCN架构等,就得充分利用目标设备的特性。

AMD的显卡CU架构

AMD的GPU是由多个Comput Unit(CU,运算单元)组成的,CU的数目和架构因不同的显卡系列和型号有差别。一个CU含有多个Processing Elements,又被称为Streaming Processor,一个work-item线程运行时将占用一个PE。非GCN架构,以Evergreen和Northen Islands设备为代表。我们先来了解一下出现在GCN之前的AMD传统显卡架构。简单来讲,每一个Compute Unit都是一个SIMD(Single Instruction, Multiple Data)运算部件,共享同一个Program counter。每一个Compute Unit含有16个Processing Element(PE)。每个PE有自己的General-Purpose Registers(GPR)寄存器;根据设备不同,可能含有3到4个负责向量运算的ALU,一个标量运算的ALU和一个分支执行单元。

不同的CU之间是独立运行的;但是对于同一个CU里的PE所执行的每个work-item,则是按SIMD的模式执行。

【OpenCL】OpenCL性能与AMD的显卡架构其间的思考

Figure 1, Evergreen GPU架构图[1]

在GCN设备之前,PE执行的指令被称为Very Long Instruction Word(VLIW),根据PE所含ALU(Arithmetic logic unit)的个数不同,有VLIW4和VLW5的区别,PE中的4或5个ALU将协作完成一个VLIW指令。借助于编译器优化,编译器会把2~5个没有依赖性的指令操作合成1个的VLIW指令。对于常见的计算机图形处理,操作的数据一般是RGBA各个颜色通道分别计算,VLIW这样的设计能提高ALU的占用率,减少GPU核心的时钟周期次数,从而提高GPU的运算效率,开发人员也可以借助向量化手动实现这个操作。

另一方面,最差情况下,1个VLIW指令只含有1条原始指令,导致剩下的ALU会闲置,将会大大减少GPU的运算效率。一段核函数程序将被编译成一串连续的VLIW指令,分配到Wavefront的规划在编译期间就是确定的了。核函数运行时,CU会依次从指令队列中读取VLIW指令。用户设定的总线程数被驱动以64个线程一组划分(称作一个Wavefront)来执行,分布到各个CU上。每个CU的16个PE组会同时执行同样的VLIW指令,循环4次,组成一个Wavefront。如下图,临近的颜色相同的方块表示可以向量化的指令:

【OpenCL】OpenCL性能与AMD的显卡架构其间的思考

但是,普遍意义上的运算中由于数据依赖性通常要比图形像素操作高的多,因此很多情况下并不能做到向量化操作,使得PE的使用不够充分。并且,由于VLIW指令都是在编译期确定的,函数运行中并不能动态的规划指令的分发,就算是非常优秀的编译器,依然会产生运算效率不高的情况。例如,由于线程是严格的按照64个线程来执行的,如果运行时同一个Wavefront的某1个线程进入了不同的分支,将会导致剩下的63个work-item线程闲置,直到这个线程分支结束;另一方面,由于VLIW指令的复杂度令优化、分析瓶颈和预估性能也产生了困难。

以AMD Radeon HD5870为例:20个CU,每个CU有16个PE,每个PE有5个ALU(1个标量, 4个向量),总计1600个ALU。就是说,这个设备能同时支持20个wavefront的并发,因此同时并发的work-item线程数为20 * 64 = 1280。

GCN架构,以Southern Islands设备代表

Graphics Cores Next,GCN架构是AMD专门为通用计算设计的显卡架构,始于AMD HD79xx系列显卡,以HD7970显卡为代表。

【OpenCL】OpenCL性能与AMD的显卡架构其间的思考

Figure 2, GCN运算单元架构图[3]

与Northern Islands架构不同,GCN架构的一个CU含有4个SIMD。每个SIMD更接近CPU的SIMD架构,如下图,每个SIMD含有16个向量ALU(或称为PE)和一个64KB的缓存区域(包含寄存器,外加10个Wavefront的指令缓存)。

【OpenCL】OpenCL性能与AMD的显卡架构其间的思考

这种架构使得每个CU能同时执行4条不同的指令。一个Wavefront中,指令在每个SIMD的16个PE执行4次循环(4个时钟周期)。通过这种方式,依然使得Wavefront的大小限制在了64。每个SIMD有一个专门用来储存10个Wavefront指令的缓存,这样一个CU所能缓存的Wavefront个数达到了40个;同时,这些指令的提取是通过硬件(而不是编译器编译期间确定的),使得GPU硬件的控制设备能规划Wavefront的分发,SIMD根据优先级从10个Wavefront中选择要去执行的指令。

由于GCN架构CU中SIMD的Wavefront缓存机制,使得GCN架构同时并发的work-item线程数大大增加。如AMD Radeon™ HD 7970,有32个CU,每个CU能同时并发40个Wavefront,使得同能最大能并发的线程数达到了32*40*64 = 81920个work item。对比来看VLIW4 SIMD和GCN SIMD,每4个时钟周期,非GCN架构执行1条VLIW4指令,一个VLIW4指令最多执行4个ALU操作指令,受数据依赖性限制;而GCN架构的SIMD每4个时钟周期只执行1个ALU操作指令,但是每个CU的4个SIMD可以执行不同的指令,使得GPU性能主要受占用率影响。后面我们将会提到,GCN的架构使得向量化成为不必要的操作。

VLIW的问题在于Wavefront的线程可能有依赖性存在导致的GPU性能利用率降低;GCN架构中,每个SIMD有一个可以储存10个Wavefront信息的buffer,并且由于指令依赖性的分离(例如barrier同步指令),SIMD可以在一条指令结束后,跳到不同的Wavefront来进行工作,增加GPU设备的利用率。另一方面,由于指令的简化,编译器的优化也变得十分简单,而且更容易做性能优化。为VLIW由于架构的优化可能并不适合GCN架构,例如向量化并不能在GCN架构上获得任何性能提高,反而可能会由于指令的依赖性导致GPU不能更大程度的动态调整指令的分配。GCN与非GCN架构不同之处还在于每一个CU加入了一个单独的标量ALU。这个特殊的ALU可以用来加速处理特殊的条件分支指令和跳转指令:由于这个ALU每个指令只执行一次循环,可以减少对于SIMD(需要4次循环)的运算开销。

参考文献

[1] AMD APP Programming Guide

[2]AMD GRAPHICS CORES NEXT (GCN) ARCHITECTURE

[3]AMD's Graphics Core Next Preview: AMD's New GPU, Architected For Compute

=============================================================== 转载请注明出处:http://blog.csdn.net/songzitea/article/details/12151171 ===============================================================