异构并行计算

异构并行计算包括两个方面的内容:异构和并行。异构是指:计算单元由不同的多种处理器组合,如X86 CPU+GPU、ARM CPU+GPU、X86 CPU+FPGA、ARM CPU+DSP等。并行是指:要发挥异构硬件平台的全部性能必须要使用并行的编程方式。通常包含两个层次的内容:

  • 多个不同架构的处理器同时计算,要发挥异构系统中所有处理器的性能,可通过并行程序使得每个处理都参与运算,避免处理器的闲置。相比于只让某一种类型的处理器参与工作,这种方式提高了性能的上限。
  • 每个处理都是多核向量处理器。这要求使用并行编程以发挥每个每个处理器的计算能力。通常每个处理器包括多个核心,每个核心包括一个或多个长向量。

单核标量处理器的困境

在2005年之前,大多数处理器都是单核的,一些处理器已经开始支持向量化(如X86处理器支持的MMX多媒体扩展)和SSE(流式SIMD扩展)指令集。但是绝大多数应用程序并没有进行向量化。

在2005年之后,单标量处理器的性能基本上达到了顶峰,很难进一步大幅度提升性能。

单核标量处理器是如何提高性能

主要通过以下的方式提升性能:

  • 提升处理器的时钟频率:处理器的时针频率表示处理器1秒内可以运行多个基本操作,这些基本操作需要一个时钟周期运行。在某个固定的处理器上,一些复杂的操作可能需要多个时钟才能执行完成,或由多个基本操作组成。一条指令从开始到执行完成所需要的时针周期数,称为指令的延迟。
  • 提升指令级并行能力:单核标量处理上具有很多不同的部件,每个部件执行不同的指令。如果能够让多条做不同动作的指令同时操作,那么多个部件就可以同时进行指令操作,这称为指令级并行。

异构并行计算架构 异构计算包括哪两个_异构并行计算架构


流水线示例

五阶段流水线将指令的执行过程划分为:取指令(Instruction Fetch,IF)、指令解码(Instrcution Decode,ID)、执行(Execution,EX)、访存(Memory Access,MEM)和写回(Write Back,WB)。假设处理器支持两条流水线同时操作。每个周期内,都会有两条指令执行完成,两条新指令加入执行。执行的步骤如上图所示。从整体来看,若没有使用流水线执行,则原来需要5个周期才能完成2个在操作,而使用流水线执行后,则每个周期能够完成2个操作。

单核处理器性能到达瓶颈

单核标量处理器的性能不能再以摩尔处理器的速速提升,主要的原因:

  • 功耗限制了频率的继续提升:从物理定理来看,随着处理器工艺制程的推进,处理器的最大功耗(主要是漏电功耗)越来越大,这意味着随着处理器频率的增加,处理器功耗会大幅度增加。处理器功耗增加,则处理器工作时越来越热,对散热系统要求越来越高。
  • 提升指令级并行遇到瓶颈:指令级并行能够让处理器的多个不同的流水线组件同时工作。指令级并行能够增大处理器组件的利用率,极大地提高处理器的性能。处理器设计师在硬件层次提供了重排缓冲区(Reorder Buffer,ROB)、发射队列(issue queue)和寄存器重命名单元(register renameing)等来挖掘指令执行时不相关性。

在提升指令级并行遇到瓶颈后,硬件设计师通过硬件寄存器的长度提升性能。例如原来的寄存器长度为32位,现在提供到128位,这意味着原来的一个寄存器能够存放一个单进度浮点数据,而现在一个寄存器能够保存4个这样的数据。

多核并行计算和向量化的出现

今天的绝大多数处理器,如X86多核CPU、ARM多核CPU、GPU以及DSP等,都已经是多核向量处理器。多核和向量化的出现满足了应用对计算机能力的需求。

多核的原因

多核通过复制处理器核心成倍了处理器的计算能力,多核的的出现除了消耗的原因外,还有很多其他原因。

随着数据量越来越大,处理大量数据需要的计算性能的需求也越来远大,但是处理器商没有办法提供性能更好的处理器、

向量化的原因

如果代码使用128位向量化处理单精度数据,那么每次可以同时处理4个数据。

利用多核和向量化的能力

要同时发挥向量化和多核的计算能力,必须要编写向量化和多线程代码。主要有两种:

  • 分别编写向量化代码和线程级代码
  • 统一编写向量化代码和多线程代码

常见的编写语言,如C11/C+11和Java等语言本身已经内置了线程级并行能力,而其他的语言则需要使用语言自身到机制。要发挥多核向量处理器的向量计算能力,则需要使用能够硬件生产商提供了内联汇编(也称为内置函数)。例如Intel/AMD为其X86提供了SSE/AVX指令集的C语言内置函数,ARM也为其CPU处理器提供了NEON指令集的C语言内置函数。

一些新的C编程语言扩展,如CUDA和OpenCL,通过层次化的线程/编程模型使得一份代码同时支持向量化和多核。

OpenCL不但支持GPU,还支持X86 CPU和ARM,一些移动处理器的GPU也开始支持OpenCL、目前一些FPGA和DSP的厂商也提供了OpenCL的支持。

多核和向量化的难点

  1. 无论是向量化还是多核并行化,这两者意味着需要并行haul代码,但是根据Amdahl定理程序中串行代码的比例限制了并行化代码能够取得的最好的效果。
  2. 现在的多核向量处理器(尤其是X86)为了减少获取数据的延迟,使用了大量的缓存来保存多次重复访问的数。在很多的情况下,缓存能够增加程序的现实计算能力。但是缓存并不贡献硬件的原生计算能力(从某种程度上说,缓存是对处理器资源的一种浪费)。
  3. 有些代码不能使用多核并行化或向量化。一些算法的运算具有内在的串行特点,因为必须串行化。
  4. 在很多情况下,要发挥向量化和多核的计算能力,可能需要多份代码,这增加了代码维护代价。要编写向量化代码或多核并行代码,需要分析代码中数据和操作的依赖关系,处理任务和数据划分,并将其高效地映射到向量化和多核硬件上。
  5. 在一些应用严格的应该场景下,限制了其不能允许向量化和多线程导致的计算结果出现了偏差。

异构并行计算的崛起

从2007年NVIDIA退出了CUDA计算环境开始,异构并行计算逐渐得到了大众的认同。异构并行计算包含两个子概念:异构和并行

  1. 异构:异构并行计算需要同时处理多个不同架构的计算平台问题,如目前主流的异构并行计算平台:X86+GPU、X86+FPGA、以及目前正在研发中的ARM/Power+GPU。
  2. 并行:异构计算主要采用并行的编程方式,无论是X86处理器,还是ARM和GPU处理器以及DSP,这里所有的处理器都是多核向量处理器,要发挥多种处理器混合平台的性能必须采用并行的编程方式。

异构并行计算出现的主要的两种问题:

  1. 性能问题
  2. 功耗问题

GPGPU的理念

在NVIDIA推出其CUDA计算环境之前,很多科学家就已经意识到如果能力利用GPU提供的强大计算能力计算一些通用运算,就能够获得很高的计算速度。在哪个时代,要使用GPU计算,则必须要将算法映射成图形的渲染过程,那时用来进行图形编程的主要应用编程接管口是OpenGL,即使用OpenGL将计算过程映射成为图形渲染过程,进而达成计算的目的,这称之为GPGPU。

CUDA的崛起

在2007年,NVIDIA退出了GTX8800 GPU,与之前为图形渲染的每个阶段独立设计流水线不同,GTX8800采用统一的渲染架构。同一处理器会处理图形渲染的全部流水线,这不仅提升了硬件的利用率,获得图形渲染的高性能。

NVIDIA称CUDA是计算统一设备架构(Computing Unified Device Architecture)缩写,但是今天CUDA的范围已经远远超出了NVIDIA当初的定义,CUDA已经成为NVIDIA通用GPU并行计算编程平台和编程模型的抽象,一个符号,一个生态系统。

CUDA平台提供了CUDA C语言扩展,相比普通C语言,CUDA C增加了使用NVIDIA CPU进行通用计算必不可少的一些语言扩展,其他功能功能都通过函数库提供。CUDA C以C/C++语言为基础而设计,因此对熟悉的C系统语言的程序员来说,CUDA的语法比较容易掌握。另外CUDA只对 ANSI C 进行了最小的扩展,以实现其关键特性::形成按照两个层次进行组织、共享存储器(shared memory)和栅栏(barrier)同步。

由于CUDA由NIVDIA一家设计,并未被Intel和AMD接受,因此目前使用的CUDA编写的程序只支持NVIDIA GPU,而OpenGL的出现解决了这一问题。

OpenCL的发展

OpenCL全称为Open Computing Language(开放计算语言),先由Apple设计,后来交由Khronos Group维护,是异构平台并行编程的开发的标准,也是编程架构。OpenCL的设计借鉴了CUDA的成功经验,并尽可能地支持多核CPU,GPU或其他加速器,OpenCL不断支持数据并行,还支持任务并行。同时OpenCL内建了多GPU并行的支持。OpenCL API 基于纯C语言进行编写,所以OpenCL API的函数名比较长,参数也比较多(不支持函数重载),因此函数名相对难以记忆。

OpenCL涵盖的领域不但包括GPU,还包括其他的多种处理器芯片。到现在为止,支持OpenCL的硬件主要局限在CPU、GPU、DSP和FPGA上,目前在桌面端和服务器端提供OpenCL开发环境的主要有Apple,NVIDIA、AMD、ARM和Intel,其中Apple提供了一个独立的OpenCL开发环境并与自家的OS X系统完整地融合在一起,NVIDIA和AMD都提供了基于自家的GPU的OpenCL在Windows和OpenCL实现。

OpenCL包含两个部分,一是OpenCL C语言(OpenCL 2.1将开始使用OpenCL C++作为内核编程语言)和主机端API。二是硬件架构的抽象,OpenCL只是给C11进行了非常小的扩展,以提供控制并行计算设备的API以及一些声明计算内核的呢管理。软件开发人员可以利用OpenCL开发并行程序,并且可获得比较好的多种设备上运行的可移植性。

为了使得OpenCL程序能够在各种硬件平台运行,OpenCL提供了一个硬件平台层,同时各种不同设备上的存储器并不相同,相应地,OpenCL提供了一个存储器抽象模型。不但包括一门编程语言,还包括一个完整的并行编程框架,通过编程语言、API以及运行时系统来支持软件在整个平台上的运行。

异构并行计算的发展

近年来,从私有的CUDA、C++AMP、Direct3D、Metal API,到开放的OpenCL、OpenACC、OpenGL。而传统的共享存储器编程环境OpenMP和分布式编程环境MPI也增加的对异构计算的支持。

OpenCL的特点如下:

  • 高性能:OpenCL是一个底层的API,能够很好的映射到更底层的硬件上,充分发挥硬件中各个层次的并行性,故能够获得很好的性能。
  • 适用性强:OpenCL是一个抽象的API,抽象了当前主流的异构并行计算硬件的不同架构的共性,同时又兼顾了不同硬件的特点。
  • 开放:OpenCL是开发组件的开发、维护的标准,能够获得最广泛的硬件支持,如AMD、Intel、NVIDIA、ARM、Qualcomm和联发科等都已经或正在硬件是哪个支持OpenCL。

MPI 3对异构并行计算的支持

经典的分布式存储并行标准MPI在其版本3中明确了允许在数据传输函数调用时使用异构平台上的指针内容,并且同时在标准中扫清了相关的数据匹配等问题。这使得MPI在进行数据传输时,能够直接传递指向GPU或其他硬件上内存地址的指针。但是MPI 3对异构的支持几乎为0。

OpenMP对异构并行计算的支持

经典共享存储器并行编程环境OpenMP在其4.0版本标准中增加了很多支持异构计算的构造,如target、target data等。

OpenCL无处不在

AMD不但在其GPU上全面支持OpenCL,还在去X86 CPU上支持OpenCL,是第一个提供CPU+GPU全面支持OpenCL的厂家。AMD转向OpenCL后,和OpenCL的编程模型紧密结合,推出了GCN系统的GPU。Intel推出了其OpenCL支持方案,但是这种支持是全面的一劳永逸的,无论是Intel的X86 CPU,MIC GPU,还是Intel X86 CPU集成的GEN架构GPU都得到了支持。

2013/2014,主流的移动处理器厂都退出了基于其移动GPU的OpenCL编译运行环境,如Imageination Technology的PowerVR系列移动GPU、高通的Adreno系列的移动GPU、ARM的Mali系统的GPU都支持了OpenCL。与此同时,主流的FPGA厂商Altera和Xilin也推出了OpenCL编译运行环境。