Upload
others
View
9
Download
0
Embed Size (px)
Citation preview
第 1 章
异构并行计算的过去、现状和未来
在正式进入本章的主题前,先让我们重温一下异构并行计算的概念。异构并行计算包
含两个方面的内容:异构和并行。异构是指:计算单元由不同的多种处理器组成,如 X86 CPU+GPU、ARM CPU+GPU、X86 CPU+FPGA、ARM CPU+DSP 等。并行是指:要发挥异
构硬件平台的全部性能必须要使用并行的编程方式。这通常包含两个层次的内容:
1)多个不同架构的处理器同时计算,要发挥异构系统中所有处理器的性能,可通过并
行编程使每个处理器都参与运算,避免处理器闲置。相比于只让某一种类型的处理器参与工
作,这种方式提高了性能上限,简单举例来说,在 X86 CPU+GPU 平台上,X86 CPU 的计算
能力为 1TFLOPS,GPU 的计算能力为 4TFLOPS,如果只使用 GPU,那么最大可发挥的性能
是 4TFLOPS,而如果加上 X86 CPU,则最大可发挥的性能是 5TFLOPS。1
2)每个处理器都是多核向量处理器 ,这要求使用并行编程以发挥每个处理器的计算能
力。通常每个处理器包括多个核心,每个核心包含一个或多个长向量,如 AMD GCN GPU 中
就包含数量不等的核心,每个核心包含 4 个向量,每个向量能够同时处理 16 个 4 字节长度
的数据。如果没能很好地并行,则可能不能完美地发挥多核和向量化的性能。
作为本书的开篇,本章将主要介绍异构并行计算的历史、现状和未来:
1)异构并行计算的历史。即在异构并行计算出现之前,处理器是如何提升性能,使用
了哪些提升性能的方法,这些方法为什么又遇到困难了。读史使人明智,通过了解异构并行
计算的历史,读者可以了解到为什么异构并行计算会大行其道,也了解了为什么笔者会编写
本书。
2)异构并行计算的现状。今天异构并行计算已经得到充分的发展并且还在进一步快速
本书的向量处理器指支持 MIMD 执行或 SIMD 指令集的处理器。
Chapter 1
2 OpenCL 异构并行计算:原理、机制与优化实践
发展中,OpenCL 和其他的异构并行计算工具已经应用到许多图像处理、视频处理及科学计
算项目上,而这些工具自身也在快速进化中。近两年,许多科学计算以外的行业和领域(如
互联网行业)正在应用异构并行计算来加快研究和产品化的步伐。
3)异构并行计算的未来。计算的未来是异构并行的,异构并行的概念、应用在计算机
及相关领域会越来越广。任何参与计算机及相关行业的人员都应当了解并学习异构并行相关
的内容。在不久的将来,不懂异构并行计算就意味着不懂计算机。
在具体介绍异构并行计算的历史、现状和未来之前,笔者想介绍几个始终贯穿本书的相
关概念:
1)向量化。向量化是一种一条指令同时处理多个数据的方法,从这一点来说,它是一
种数据并行技术。主流的向量化技术有两种:SIMD(Single Instruction Multiple Data,单指
令多数据)和 SIMT(Single Instruction Multiple Thread,单指令多线程),大多数 CPU(如
AMD Zen 处理器)都使用 SIMD 向量化技术,而大多数 GPU(如 AMD GCN)都使用 SIMT向量化技术。关于 SIMD 的具体描述请参看图 1-1。
Vector Lanes 8 in this example
图 1-1 向量化示例
SIMD 操作可简单描述为一些具有如下特点的操作:对两个长向量寄存器中的数据按元
素进行操作,结果向量寄存器和源向量寄存器长度相同。例如,对两个长度为 512 位的向量
进行 SIMD 操作,按照单精度进行浮点加操作,假设单精度浮点类型占用空间大小为 4 个字
节,那么 512 位向量可一次同时处理 16(512 位 /8 位每字节 /4 字节)个单精度浮点数据得到
16 个结果,其中第一个向量的第 1 个元素和第二个向量的第 1 个元素相加产生结果向量的第
1 个元素,第一个向量的第 15 个元素和第二个向量的第 15 个元素相加产生结果向量的第 15个元素,其余类推。
2)多核。多核是指:在一块芯片上,集成多个处理器核心,这多个处理器核心共享或
不共享缓存层次结构。图 1-2 是 ARM 公司设计的 ARM Cortex-A72 多核处理器,从中可以
看出其最多具有 4 个核心(为了应对不同细分市场的需求,ARM 处理器核心数量通常可调
第 1 章 异构并行计算的过去、现状和未来 3
整),每个核心具有 32KB 一级数据缓存(L1 Cache),48KB 一级指令缓存,4 个核心共享
512KB 到 2MB 二级缓存(L2 Cache)。多核处理器通
常会共享主板上的物理内存。
3)多路。硬件生产商会将多个多核处理器互联
(如 AMD 的 HT(Hyper Transport)总线)在同一个主
板上,各个多核处理器之间通常共享缓存(如三级缓
存或 eDRAM)或内存来交换数据。由于主板的设计
会导致 NUMA(非一致性内存访问)特性,感兴趣的
读者可参考刘文志(花名风辰)的著作《并行算法设
计与性能优化》 中的 2.6 节。2
多核和向量化是现代处理器提升性能的两种主
要途径,今天的绝大多数处理器都已经是多核向量化
处理器。在介绍为什么多核或向量化处理器如此流行
之前,先让我们了解一下之前的单核标量处理器遇到了什么问题。
1.1 单核标量处理器的困境
在 2005 年之前,大多数处理器都是单核的,一些处理器已经开始支持向量化(如 X86处理器支持的 MMX(多媒体扩展)和 SSE(流式 SIMD 扩展)指令集),但是绝大多数应用
程序并没有进行向量化,故绝大多数代码只能利用到单核处理器的标量性能。对于单核标量
处理器(或者运行在单核向量处理器上的标量代码)来说,处理器生产商只能考虑如何提升
单核标量处理器的性能。处理器生产商通过提升单核标量处理器的频率和指令级并行处理能
力(即提升指令流水线性能)来提升处理器的计算性能,如图 1-3 所示。
从图 1-3 中可以看出,在 2005 年之前,单核标量处理器的性能基本上是每 18 个月近似
提升一倍,这称为摩尔定律。关于摩尔定律有许多不同的表述,也有一些表述上的不同和争
议,本节就不追究其原因和细节,只简单地称“单核标量处理器性能每 18 个月提升一倍”
为摩尔定律。
在 2005 年之前,单核标量处理器性能提升能满足摩尔定律的时期称为提升软件性能的
“免费午餐”时期,因为单核标量代码的性能可以满足摩尔定律描述的速度提升,在这个前提
下,应用程序无须修改,只需要等待下一代处理器的推出,到时现在的代码自然就能够跑得
更快。处理器生产商、研究人员和软件开发人员都非常高兴且享受摩尔定律带来的成果:
1)对处理器生产商来说,能够稳定地推出性能更好的产品能够帮助他们顺利推动产品的
更新换代,卖出更多新产品,淘汰旧产品,获得更多利润。处理器生产商获得了更多利润就能
够进一步增加研发投入,以推出性能更好的产品。对处理器生产商来说,这是一个良性循环。
机械工业出版社华章公司出版,书号 978-7-111-50102-2。
图 1-2 ARM Cortex-A72 多核向量
处理器架构
4 OpenCL 异构并行计算:原理、机制与优化实践
2)对研究人员来说,他们基于当前处理器的计算能力来设计应用,获得研究结果,并
依据摩尔定律来估计下一代处理器能够提供的性能,设计在下一代处理器上能够快速运行的
应用。在下一代处理器推出后,就可以获得更好的结果。
3)对软件开发人员来说,无须花费太多精力来优化程序性能,只需要建议老板购买新
硬件即可获得性能提升。
4)在这种处理器生产商和软件开发人员相互促进的良性循环下:软件开发人员依据当
时处理器的性能设计应用,并依据摩尔定律对下一代处理器的性能提出预期(设计在下一代
处理器上能够流畅运行的应用),处理器生产商生产新处理器以满足摩尔定律对性能的要求,
并将新处理器卖给软件开发人员,周而复始,相互促进。
107
106
105
104
103
102
101
100
1975 1980 1985 1990 1995 2000 2005 2010 2015
Transistorsthousands
Single-threadPerformance
SpecINTFrequency
MHz
Typical PowerWatts
Number ofCores
图 1-3 处理器频率、性能、功耗和核数变化
Original data collected and plotted by M.Horowitz, F. Labonte, O. Shacham, K. Olukotun, L. Hammond and C. Batten Dotted line extrapolations by C. Morre
在 2005 年之后,单核标量处理器的性能基本上达到顶峰,很难进一步大幅度(超过
10%)提升性能。在回答为什么单核标量处理器的性能无法接着以摩尔定律要求的速度提升
之前,先让我们看一下,在 2005 年之前单核标量处理器如何提升性能,因为只有知道之前
如何提升性能,才能知道为什么不能以同样的方式接着提升性能。
1.1.1 单核标量处理器如何提高性能
在 2005 年之前,单核标量处理器以近似摩尔定律的方式提升性能,其主要通过以下几
种方式提升性能:
1)提升处理器的时钟频率:处理器的时钟频率表示处理器 1 秒内可以运行多少个基本
第 1 章 异构并行计算的过去、现状和未来 5
操作,这些基本操作需要一个时钟周期运行。在某个固定的处理器上,一些复杂的操作可能
需要多个时钟才能执行完成,或由多个基本操作组成。一条指令从开始到执行完成所需要的
时钟周期数,称为指令的延迟。一个具体的操作在不同的处理器上,其所花费的时钟周期数
量可以相同,也可以不同。通过提升某个单核标量处理器的时钟频率,在指令的延迟保持不
变的前提下,处理器 1 秒内就可以执行更多的基本操作,这提升了处理器上运行的所有指令
的执行速度。
2)提升指令级并行能力:单核标量处理器上具有许多不同的部件,每个部件执行不同
的指令操作,如有的部件负责从内存中加载数据,有些部件负责计算乘加指令,一些部件负
责计算内存地址。如果能够让多条做不同动作的指令同时操作,那么多个部件就可以同时进
行指令操作,这称为指令级并行。如果在一个处理器上,能够同时操作 3 条指令,在提升指
令级并行能力后,它可能能够同时处理多达 5 条指令。提升指令级并行能力并没有减少某条
指令的延迟,但是它提升了处理器能够同时处理的指令数量。
在“免费午餐”时期,通过提升处理器的时钟频率以大幅度提升性能,如图 1-3 中的绿
线 所示。而通过将一条指令拆分成多个阶段以提高指令级并行能力已得到广泛使用,关于
为什么将一条指令拆分成多个阶段能够提高处理器的性能,以经典的 5 阶段流水线为例,请
参考图 1-4。3
t0 t1 t2 t3 t4 t5 t6i0i1i2i3i4i5i6i7i8i9
i
tIF ID
ID
IDID
ID
IDEX
EX
EXEX
EX
EXMEMMEM
MEMMEM
WBWB
MEMMEM
WBWB
IFIFIF
IF
IFIF
IDID
EXEX
MEMMEM
WBWB
MEMMEM
WBWB
WBWB
EXEX
IDID
IFIF
IF
图 1-4 流水线示例
五阶段流水线将指令的执行过程划分成:取指令(Instruction Fetch,IF)、指令解码
(Instruction Decode,ID)、执行(Execution,EX)、访存(Memory Access,MEM)和写回(Write Back,WB)。同时假设处理器支持两条流水线同时操作。在开始执行时(t0),有两条指令
(i0, i1)在取指令;在 t1 时,指令 i0、i1 在解码,而两条新指令 i2、i3 可以进行取指令操作;
在 t2 时,又有两条新指令 i4、i5 进行取指令操作,而此时指令 i2、i3 进行解码操作,而指令
i0、i1 正在执行;在 t3 时,又有两条新指令 i6、i7 进行取指令操作,而此时指令 i4、i5 进行
解码操作,而指令 i2、i3 正在执行,指令 i0、i1 正在访存;在 t4 时,又有两条新指令 i8、i9在进行取指令操作,而此时指令 i6、i7 进行指令解码操作,指令 i5、i4 正在执行,指令 i2、
图 1-3 中从上向下数,第三条线为绿色线,本书为单色印刷,特为读者指出,带来不便,敬请谅解。
6 OpenCL 异构并行计算:原理、机制与优化实践
i3 正在访存,指令 i0、i1 正在写回,写回结束后,指令 i0、i1 就完成了,以此类推,在随后
的每个周期内,都会有两条指令执行完成,两条新指令加入执行。从整体来看,若没有使用
流水线执行,则原来需要 5 个周期才能完成 2 个操作,而使用流水线执行后,则每个周期能
够完成 2 个操作。
要完全利用硬件指令流水线的所有性能,程序代码需要提供足够多样(不同种类)的指
令,编译器需要从源代码中获得足够多的信息以安排流水线获得最好性能。另外,不是所有
的指令都需要执行硬件指令流水线的所有阶段,现代处理器采用了许多不同的办法来处理这
个问题。这里就不展开讨论这些问题了。
在介绍完单核标量处理器如何提升性能之后,我们接着来了解单核标量处理器无法继续
以摩尔定律的速度提升性能,即为什么单核标量处理器性能到达瓶颈。
1.1.2 为什么单核标量处理器性能到达瓶颈
1.1.1 节提到,在 2005 年之前,提升单核标量处理器性能以满足摩尔定律描述的速度提
升,而在 2005 年之后,单核标量处理器的性能不能再以摩尔定律的速度提升,这主要是因为:
1)功耗限制了频率的继续提升:从物理定理来看,随着处理器工艺制程的推进,处理
器的最大功耗(主要是漏电功耗)越来越大,大致来说处理器的功耗和处理器的频率的三次
方近似成正比(硬件设计实践中有许多方法降低指数,具体细节请读者参考相关著作,笔者
就不详细解释了),这意味着随着处理器频率的增加,处理器功耗会大幅度增加。处理器功耗
增加,则处理器工作时越来越热,对散热系统的要求会越来越高。而今天散热系统已经从风
冷、散热片、水冷到油冷。在处理器散热要求已经达到现实环境、技术能够满足的界限情况
下,如果再增加频率,那么硬件组件可能不能正常工作,甚至烧掉。
2)提升指令级并行遇到瓶颈:指令级并行能够让处理器的多个不同的流水线组件同时
工作。如图 1-4 所示,在一条指令取指的阶段,另外一条指令正在解码,与此同时其他的指
令正在计算、写回存储器等。指令级并行能够增大处理器组件的利用率,极大地提高处理器
的性能。但是要利用好处理器的指令级并行能力需要代码优化人员、编译器作者和处理器设
计师共同努力。处理器设计师在硬件层次提供了重排缓冲区(Reorder Buffer,ROB)、发射队
列(issue queue)和寄存器重命名单元(register renaming)等来挖掘指令执行时的不相关性。
硬件层面的支持能够挖掘出软件层面不知道的信息,如是否存在存储器别名。编译器作者要
让编译器合理安排生成的指令,尽量让生成的指令没有依赖性,或者让依赖指令的距离足够
远,合理重用寄存器等。代码优化人员需要以编译器和处理器友好的方式编写代码,以便编
译器生成处理器能够高效执行的代码。为了提高硬件的指令级并行执行能力,处理器设计通
常会增加硬件流水线的级次,而现在这一方法也达到其局限。
在提升指令级并行遇到瓶颈后,硬件设计师通过增加硬件寄存器的长度提升性能。例
如,原来寄存器长度为 32 位,现在提升到 128 位,这意味着如果原来一个寄存器只能保存
一个单精度浮点数据,而现在一个寄存器能够保存 4 个这样的数据。如果同时增加指令的操
第 1 章 异构并行计算的过去、现状和未来 7
作能力,让指令可以同时对一个 128 位的向量寄存器中的数据进行操作,那么就能够成倍地
增加处理器的吞吐量,这称为 SIMD 向量化。SIMD 向量化也是一种数据并行形式的指令级
并行,通过向量化,硬件只需要增加很少的单元就能够成倍地提升峰值性能。
由于散热导致处理器的频率不能接着提升,硬件生产商转而采用将多个处理器组成到一
个芯片上,这称为多核。通过稍微降低频率和电压,多核处理器能够以稍微增加的功耗获得
更高的性能。
1.2 多核并行计算与向量化的出现
由于散热技术和硬件生产技术无法满足提高处理器频率对功耗的设计要求,现代处理器
的频率近似停滞。为了提供更高性能的处理器,处理器硬件生产商通过增加寄存器的宽度和
指令的宽度来同时处理多个数据,这称为向量化。多核和向量化的出现提升了处理器的执行
能力。通过稍微降低频率,现在的散热技术能够满足处理器对功耗的需求。
今天的绝大多数处理器,如 X86 多核 CPU、ARM 多核 CPU、GPU 及 DSP 等,都已经
是多核向量处理器。多核和向量化的出现满足了应用对计算能力的需求,但是它们也带来了
两个重要的问题:如何编程以发挥多核和向量的计算能力;如何保证随着核数和向量长度的
增加,性能的提升依旧接近线性。
1.2.1 为什么会有多核
多核通过复制处理器核心成倍增加了处理器的计算能力,多核的出现除了功耗的原因
外,还有许多其他的原因。
许多应用需要同时进行许多不相关的处理,如希望用户界面在不影响用户处理的同时进
行计算,以提升用户体验;如网页服务器需要同时满足多个用户的访问请求。多核能够支持
线程级并行处理,通过将线程映射到硬件核心上,以同时处理多个不同的请求,提高用户使
用体验。
随着数据量越来越大,处理大量数据需要的计算性能的需求也越来越大,如科学计算领
域需要长时间、细精度的模拟以重现真实系统;深度学习系统需要在大量数据集上训练以调
整模型,通常其运行时间需要几天、几周。应用对性能的要求促使处理器硬件生产商想方设
法、不断提升处理器的性能。
如果处理器生产商没有办法提供性能更好的处理器,他就没有办法要求用户为他的新处
理器埋单。
1.2.2 为什么会有向量化
许多算法、应用中具有向量级并行(数据并行)能力,如一些算法需要对多个不同数据
执行同一个操作,如代码清单 1-1 所示。
8 OpenCL 异构并行计算:原理、机制与优化实践
代码清单 1-1 向量化示例代码
for(int i = 0; i < n; i++){ _ _m128 da = a[i]; _ _m128 db = b[i]; c[i] = _mm_add_ps(da, db);}
代码使用 128 位向量处理单精度数据,那么每次可以同时处理 4 个数据。
向量化提升了处理器的计算能力,能够满足应用软件对计算性能的需求。同时向量化并
没有大幅度增加处理器功耗。
1.2.3 如何利用多核和向量化的能力
要同时发挥向量化和多核的计算能力,必须要编写向量化和多线程代码。这通常表现为
两种实现方式:① 分别编写向量化代码和线程级代码;② 统一编写向量化代码和多线程代码。
常见的编程语言,如 C11/C++11 和 Java 等语言本身已经内置了线程级并行能力,而其他
的语言则需要使用语言自身的机制。要发挥多核向量处理器的向量计算能力,则需要使用硬件
生产商提供的内联汇编(也称为内置函数)。例如,Intel/AMD 为其 X86 处理器提供了 SSE/AVX指令集的 C 语言内置函数,ARM 也为其 CPU 处理器提供了 NEON 指令集的 C 语言内置函数。
一些新出现的 C 编程语言扩展,如 CUDA 和 OpenCL,它们通过层次化的线程/编程模
型使得一份代码同时支持向量化和多核。目前 CUDA 和 OpenCL 已经广泛应用在基于 GPU的异构并行计算中。
OpenCL 不但支持 GPU,还支持 X86 CPU 和 ARM,一些移动处理器的 GPU 也开始支
持 OpenCL,如高通的 Adreno 系列、Imagnation Technology 的 PowerVR 系列和 ARM 的 Mali系列都已经支持 OpenCL。目前一些 FPGA 和 DSP 的厂商也已经提供了 OpenCL 的支持。
1.2.4 多核和向量化的难点
虽然多核和向量化能够大幅度地提升代码性能,但是它依旧面临许多现实的困境:
1)无论是向量化还是多核并行化,这两者都意味着需要并行化代码,依据 Amdahl 定
律,程序中的串行代码比例限制了并行化代码能够取得的最好效果。一个比较简单的示例
是:如果你的代码中有 10% 的代码是必须要串行执行的,那么无论你怎么优化,都不可能获
得超过 10 倍的加速。
2)现在的多核向量处理器(尤其是 X86)为了减少获取数据的延迟,使用了大量的缓存
来保存多次重复访问的数据,在很多情况下,缓存能够增加程序的现实计算能力。但是缓存
并不贡献硬件的原生计算能力,这意味着实际上处理器的大量缓存并没有用来提升硬件的计
算能力(从某种程度上说,缓存是对处理器资源的一种浪费)。
3)有些代码不能使用多核并行化或向量化。一些算法的运算具有内在的串行特点,因
第 1 章 异构并行计算的过去、现状和未来 9
此必须要串行执行。如果代码无法多核并行化或向量化,那么多核向量处理器的一些计算能
力就会浪费。
4)在许多情况下,要发挥向量化和多核的计算能力,可能需要多份代码,这增加了代
码维护代价。要编写向量化代码或多核并行代码,需要分析代码中数据和操作的依赖关系,
处理任务和数据划分,并将其高效地映射到向量化和多核硬件上。
5)在一些应用严格的应用场景下,限制了其不能允许向量化和多线程导致的计算结果
出现偏差。
虽然多核和向量化具有许多现实的困难,但是软件开发人员和硬件设计人员正在紧密合
作,以减轻这些因素的影响。
1.3 异构并行计算的崛起
从 2007 年 NVIDIA 推出 CUDA 计算环境开始,异构并行计算逐渐得到大众的认同,从
学术界走向了工业界。异构并行计算包含两个子概念:异构和并行。
1)异构是指异构并行计算需要同时处理多个不同架构的计算平台的问题,如目前主流
的异构并行计算平台 X86+GPU、X86+FPGA,以及目前正在研发中的 ARM/Power+GPU。
2)并行是指异构并行计算主要采用并行的编程方式,无论是 X86 处理器,还是 ARM和 GPU 处理器以及 DSP,这里所有的处理器都是多核向量处理器,要发挥多种处理器混合
平台的性能也必须要采用并行的编程方式。
最先拥抱异构并行计算的领域是科学计算,如分子动力学模拟中需要长时间的运算,使
用 X86+GPU 的异构并行模式能够将模拟时间由几周缩短为 1 周或几天。
异构并行计算的出现缓解了处理器发展面临的两个主要问题:性能问题和功耗问题。
1)由于不同的硬件适合处理不同的计算问题。合理地将不同类型的计算分发到异构平
台的不同硬件上能够获得更好的计算性能,如将需要短时间运行的串行计算分发给 X86,而
将需要长时间运行的并行计算部分分发给 GPU。
2)由于采用为特定应用优化的处理器。处理器设计可以依据应用的具体特点来优化,
故功耗方面也会获得更好的结果。
在性能和功耗都比较重要的情况下,如何衡量处理器的性能就变得复杂起来,对于计算
性能至上的应用来说,性能更为重要。而对于功耗有特殊要求的应用来说,性能功耗比可能
更为合适。性能功耗比,即每瓦功耗能够支撑的处理器计算能力。
1.3.1 GPGPU 的理念
在 NVIDIA 推出其 CUDA 计算环境之前,许多科学家就已经意识到如果能够利用 GPU提供的强大计算能力来计算一些通用运算,那么就能够获得很高的计算速度。但是那个时候
的 GPU(姑且称之为 GPU)还是为图形渲染特殊设计的流水线,那时 GPU 的每个部件都是
10 OpenCL 异构并行计算:原理、机制与优化实践
为了图形渲染的某一个阶段特殊设计。渲染一个图形需要顺序地经过所有流水线的处理。在
那个时代,要使用 GPU 计算,则必须要将算法映射成图形的渲染过程,那时用来进行图形
编程的主要应用编程接口是 OpenGL,故更明确的说法是:使用 OpenGL 将计算过程映射成
为图形渲染过程,进而达成计算的目的,这称之为 GPGPU。
由于需要将计算过程映射为图形的渲染过程,故编写 GPGPU 程序需要专家级的 GPU 硬
件的图形渲染知识和 OpenGL 编程知识。
1.3.2 CUDA 的崛起
在 2007 年,NVIDIA 推出了 GTX8800 GPU,与之前为图形渲染的每个阶段独立设计流
水线不同,GTX8800 采用了统一的渲染架构,同一处理器会处理图形渲染的全部流水线,这
不仅提升了硬件的利用率,获得了图形渲染的高性能;同时统一架构也使得在 GPU 上进行
通用计算更为容易。为了和传统的 GPGPU 计算相区别,称之为 GPU 计算时代。
在 NVIDIA 推 出 GTX8800 的 同 时,NVIDIA 也 推 出 了 其 通 用 GPU 计 算 编 程 工 具
CUDA, 在 一 开 始,NVIDIA 称 CUDA 是 计 算 统 一 设 备 架 构(Computing Unif ied Device Architecture)的缩写,但是今天 CUDA 的范围已经远远超出了 NVIDIA 当初的定义,CUDA已经成为 NVIDIA 通用 GPU 并行计算编程平台和编程模型的抽象,故其已经变成了一个 符号,一个生态系统。
CUDA 平台本身提供了 CUDA C 语言扩展,相比普通 C 语言,CUDA C 增加了使用
NVIDIAGPU 进行通用计算必不可少的一些语言扩展,其他功能则都通过函数库提供。
CUDA C 以 C/C++ 语法为基础而设计,因此对熟悉 C 系列语言的程序员来说,CUDA的语法比较容易掌握。另外 CUDA 只对 ANSI C 进行了最小的扩展,以实现其关键特性:线
程按照两个层次进行组织、共享存储器(shared memory)和栅栏(barrier)同步。
由于 CUDA 完美地结合了 C 语言的指针抽象,NVIDIA 不断升级其 CUDA 计算平台,
CUDA 获得了大量科学计算人员的认可,已经成为目前世界上使用最广泛的并行计算平台。
通过 CUDA,NVIDIA 成功打破了 Intel 在超算市场上的绝对主导地位。在今天,大多数大中
小型超算中心中都有 GPU 的身影。
由于 CUDA 由 NVIDIA 一家设计,并未被 Intel 和 AMD 等接受,因此目前使用 CUDA编写的程序只支持 NVIDIA GPU,而 OpenCL 的出现解决了这一问题。
1.3.3 OpenCL 横空出世
OpenCL 全称为 Open Computing Language(开放计算语言),先由 Apple 设计,后来交
由 Khronos Group 维护,是异构平台并行编程的开放的标准,也是一个编程框架。Khronos Group 是一个非盈利性技术组织,维护着多个开放的工业标准,并且得到了业界的广泛支持。
OpenCL 的设计借鉴了 CUDA 的成功经验,并尽可能地支持多核 CPU、GPU 或其他加速器。
OpenCL 不但支持数据并行,还支持任务并行。同时 OpenCL 内建了多 GPU 并行的支持。这
第 1 章 异构并行计算的过去、现状和未来 11
使得 OpenCL 的应用范围比 CUDA 广。为了能适用于一些更低端的嵌入式设备(如 DSP+ 单
片机这种环境),OpenCL API 基于纯 C 语言进行编写,所以 OpenCL API 的函数名比较长,
参数也比较多(因为不支持函数重载),因此函数名相对难以熟记。不过,借助像 Xcode、
Visual Studio 等现代化的集成开发环境,利用代码智能感知自动补全,其实开发人员也不需
要刻意去死背 OpenCL 的 API。OpenCL 覆盖的领域不但包括 GPU,还包括其他的多种处理器芯片。到现在为止,支
持 OpenCL 的硬件主要局限在 CPU、GPU、DSP 和 FPGA 上,目前在桌面端和服务器端提
供 OpenCL 开发环境的主要有 Apple、NVIDIA、AMD、ARM 和 Intel,其中 Apple 提供了一
个独立的 OpenCL 框架并与自家的 OS X 系统完整地融合在一起;NVIDIA 和 AMD 都提供了
基于自家 GPU 的 OpenCL 在 Windows 和 Linux 上的实现,而 AMD 和 Intel 提供了基于各自
CPU 在 Windows 和 Linux 上的 OpenCL 实现。目前除了 OS X 系统,NVDIA、AMD 与 Intel提供的 OpenCL 实现都不约而同地不支持自家产品以外的产品。由于硬件的不同,为了写出
性能优异的代码,可能需要为不同的平台做相应的优化,这会对可移植性造成影响,这个需
要权衡。
OpenCL 包含两个部分:一是 OpenCL C 语言(OpenCL 2.1 将开始使用 OpenCL C++ 作
为内核编程语言)和主机端 API ;二是硬件架构的抽象。为了使 C 程序员能够方便简单地学
习 OpenCL,OpenCL 只是给 C11 进行了非常小的扩展,以提供控制并行计算设备的 API 以
及一些声明计算内核的能力。软件开发人员可以利用 OpenCL 开发并行程序,并且可获得比
较好的在多种设备上运行的可移植性。
为了使得 OpenCL 程序能够在各种硬件平台上运行,OpenCL 提供了一个硬件平台层。
同时各种不同设备上的存储器并不相同,相应地,OpenCL 提供了一个存储器抽象模型。与
CUDA 相似,OpenCL 还提供了执行模型和编程模型。
OpenCL 不但包括一门编程语言,还包括一个完整的并行编程框架,通过编程语言、API以及运行时系统来支持软件在整个平台上的运行。
相比 CUDA,OpenCL 的优点在于它提供了一种能够在不同平台上可移植的编程方式,
另外其原生支持的多设备并行也是一大亮点。
1.4 异构并行计算的未来(百花齐放)
近十年来,异构并行计算平台、标准如雨后春笋般地出现,发展也是一日千里。从私有
的 CUDA、C++AMP、Direct3D、Metal API, 到 开 放 的 OpenCL、OpenACC、OpenGL。 而
老牌共享存储器编程环境 OpenMP 和分布式编程环境 MPI 也增加了对异构计算的支持,这些
无一不显示着这个领域现在的辉煌,这是一个异构计算正在百花齐放的时代,在短期内这个
领域依旧会呈现百花齐放的形态,甚至还会有新的平台、新的标准出现。而长期来说,最有
可能笑到最后的,必定是 OpenCL,这主要是因为 OpenCL 具有以下特点:
12 OpenCL 异构并行计算:原理、机制与优化实践
��高性能:OpenCL 是一个底层的 API,它能够很好地映射到更底层的硬件上,充分发
挥硬件中各个层次的并行性,故能够获得很好的性能。
��适用性强:OpenCL 是一个抽象的 API,它抽象了当前主流的异构并行计算硬件的不
同架构的共性,同时又兼顾了不同硬件的特点,因此具有广泛的适用性。
��开放:OpenCL 是由开放组织开发、维护的标准,不会被一家厂商所控制,故能够获
得最广泛的硬件支持,如 AMD、Intel、NVIDIA、ARM、Qualcomm 和联发科等都已
经或正在其硬件上支持 OpenCL。
��无替代选项:无论是 NVIDIA 的 CUDA,还是微软的 C++ AMP 和 Google 的 Render Script,都没有获得大量厂商的支持,只有 OpenCL 得到了几乎所有相关主流硬件厂
商的支持。
由于具有高性能、适用性强、开放和没有替代方案,未来 OpenCL 必将在异构并行计算
领域占有不可动摇的地位,甚至一统异构并行计算领域。
1. MPI 3 对异构并行计算的支持经典的分布式存储并行标准 MPI 在其版本 3 中明确了允许在数据传输函数调用时使用异
构平台上的指针的内容,并且同时在标准中扫清了相关的数据匹配等问题。这使得 MPI 在进
行数据传输时,能够直接传递指向 GPU 或其他硬件上内存地址的指针。从另外一个角度来
说,MPI 3 对异构并行计算的支持是象征性的,只是把几家 MPI 实现厂商关于 CUDA 的优
化升级为标准中的一个内容固定下来,未来的 MPI 4 会有更多对异构并行计算支持的重量级 内容。
对于使用 OpenCL 的软件开发人员来说,MPI 3 对异构的支持几乎为 0,因此本节也就
不展开了。
2. OpenMP 对异构并行计算的支持经典的共享存储器并行编程环境 OpenMP 在其 4.0 版标准中增加了许多支持异构计算的
构造,如 target、target data 等,考虑到本书的定位,关于 OpenMP 4 对异构并行计算的具体
支持内容,请参见刘文志(花名风辰)的著作《并行编程方法与优化实践》 的 3.5 节。4
在笔者编写此书时,GCC 和 Clang 的 OpenMP 都在实现 OpenMP 4 标准,相信当读者拿
到本书时,GCC 已经部分或完全支持 OpenMP 4 标准了。
3. OpenCL 无处不在在 OpenCL 推出之前,异构并行计算领域主要是基于 X86 CPU + GPU,这个市场主要有
NVIDIA 和 AMD 两个玩家,其中 NVIDIA 的 CUDA 占据了绝大多数的市场份额,AMD 的
brook+ 勉力维持,因此 NVIDIA 实际上成了这个市场的垄断霸主。
AMD 很 清 楚 brook+ 不 是 CUDA 的 对 手, 因 此 在 OpenCL 推 出 后, 立 刻 全 面 转 向
机械工业出版社华章公司出版,书号 978-7-111-50194-7。
第 1 章 异构并行计算的过去、现状和未来 13
OpenCL,放弃 brook+。AMD 不但在其 GPU 上全面支持 OpenCL,还在其 X86 CPU 上支持
OpenCL,是第一个提供 CPU+GPU 全面支持 OpenCL 的厂家。AMD 转向 OpenCL 后,和
OpenCL 的编程模型紧密结合,推出了备受赞誉的 GCN 系列 GPU,GCN 系列使得 AMD 有
实力在异构并行计算市场上和 NVIDIA 一较高下,并且不断地从 NVIDIA 口中夺食。
NVIDIA CUDA 进入超算中心后,Intel 意识到危险,如是开启了 GPU 计算硬件项目(即
larrabee,是今天 MIC 的前身),但是 Intel 之前的大多数工具都不是为了异构并行计算设计
的,Intel 迫切地需要设计或改进一种语言以满足其 GPU 计算硬件的需要。一开始 Intel 选择
了 MPI 和 OpenMP,由于在 MIC 上运行 MPI 程序遇到了许多问题,因此 OpenMP 成为事实
上的唯一选项。Intel 通过修改其 OpenMP 实现,在其 OpenMP 实现中加入了支持异构并行计
算的内容,Intel 的 OpenMP 实现中关于异构并行计算的扩展后来成为 OpenMP 4 的基础,在
OpenMP 4 标准制定时,标准委员会有 OpenACC(参见附录 B)和 Intel 的 OpenMP 扩展两个
选择,最终 Intel 赢了。不久之后,Intel 意识到 OpenMP 并不能很好地发挥 MIC 的性能,而
且随着其业务扩展,Intel 可能要为每种产品都设计一个扩展(如为其集成 GPU 架构 Gen 设
计一个,为以后的嵌入式 CPU/GPU 又要设计一个)。Intel 最终很不情愿地推出了其 OpenCL支持方案,但是这种支持是全面且一劳永逸的,无论是 Intel 的 X86 CPU、MIC GPU,还是
Intel X86 CPU 集成的 GEN 架构 GPU 都得到了支持。一个统一的、基于 OpenCL 的平台,能
够让开发人员编写的一份代码运行在 Intel 的所有处理器上,这将会是 Intel 历史上非常明智
的举动之一。
2013/2014 年,主流的移动处理器厂商也推出了基于其移动 GPU 的 OpenCL 编译运行环
境,如 Imagination Technology 的 PowerVR 系列移动 GPU、高通的 Adreno 系列移动 GPU、
ARM 的 Mali 系列 GPU 都支持了 OpenCL。与此同时,主流的 FPGA 厂商 Altera 和 Xilinx 也
推出了其 OpenCL 编译运行环境。
今天 OpenCL 已经广泛应用于分子动力学模拟、计算流体力学、图形图像处理、视频音
频处理,目前正在用于计算机视觉等领域。许多研究人员正在研究如何将 OpenCL 应用于大
数据处理等更多领域。
1.5 本章小结
本章介绍了异构并行计算的历史、现状和未来,并且介绍了关联的许多概念。在 2005年之前,处理器通常提高频率来提高计算性能,由于性能是可预测的,因此在硬件生产商、
研究人员和软件开发人员之间形成了一个良性循环。由于功耗的限制,处理器频率不能接着
提升,硬件生产商转而使用向量化或多核技术。
随着 GPU 计算的兴起,CUDA 和 OpenCL 渐渐获得了广泛的关注,异构并行计算从学
术界走向工业界,获得了大众的认可。今天几乎所有主流的处理器硬件生产商都已经在支持
OpenCL,未来 OpenCL 必将无处不在。