并行计算指的在同一时刻存在多于一个计算任务被执行。由于CPU主频提高的上限,使用多核心处理器进行并行计算早已成为主流。GPU也是一个多核心的处理器,但它的并行计算模型与多核的CPU有很大区別。我们有必要了解GPU的并计算模型。
对并行计算模式进行分类是了解CPU和GPU并行计算区别的有效方式。一种分类的方法是按照实现并行的层次分,即底层的并行或是高层的并行。主要可以分为以下4种:
- 位并行(bit-level parallelism)是一种相对较老的并行方式,它通过增加处理器的字节长度来提高并行率。从4位的处理器到20世纪80年代末的32位的处理器,位并行技术在近二十 间没有再向更高的并行程度发展。目前广泛使用的32位和64位的处理器。
- 指令并行(instruction-level parallelism)是20世纪9 0年代中期之前关注的重点。现代CPU都 有一个多级指令流水线。举例来说,一个五级指令流水线可以是:读取、解码、执行、访问内存、写入。如果流水线中的每级都可以同时执行,流水线中的各级就可以同时进行。这样并不顺次地执行不相关的指令起到了指令并行的效果。
- 数据并行(data parallelism)指多个不同的数据同时被相同的指令、指令集或者算法处理。这与GPU的并行概念是相同的。当每个线程处理一个数据所需的时间相等时,数据并行处理的速度是用单线程循环语句的速度的N_threads倍;当线程处理不同数据所需的时间不相等时,数据并行处理所需的时间由花费最长计算时间的线程决定。 值得一提的是,这里的数据 并行模型与下面的另一种分类方式中的SIMD模型是完全相同的。
- 任务并行(task parallelism)是在多个不同的数据上执行不同的指令、指令集或者算法。这与多核CPU的计算模型是相同的。一个典型的任务并行例如如下:
begin if CPU="a" then do task "A" //任务并行(即MIMD) else if CPU="b" then do task "B" //任务并行(即MIMD) end if end
在所有的CPU中,当CPU“a”从第一句if语句里读到了true,他就会开始执行任务“A”;当CPU“b”从第二句读到了true,他就会开始执行任务“B”。由于判断if-else语句的速度非常快,跳转的时间可以忽略,因此可以认为任务“A”和任务“B”是同时执行的。需要助兴的是,从外部执行这段代码时,为了给不同的数据分配不同的处理器,采用的是数据并行模型(SIMD),即每个数据都经由这段相同的代码处理,只有在被加注的两行体现了任务并行的效果。任务并行的模型和我们下面要介绍的MIMD模型是完全相同的。
另一种流行的分类法是从数据流和指令的角度把计算模型分为4类,即费林分类法。
- 单指令单数据流(SISD)是非并行计算的模型。典型的例子就是单核的CPU,所有数据都被一个处理器顺次处理,某一时刻只能处理一种指令。
- 单指令多数据流(SIMD)是GPU的计算模型,与上述数据并行模型相同。
- 多指令单数据流(MISD)指在同一个数据流上执行不同的指令。流水线模型可以被认为一种MISD。
- 多指令多数据流(MIMD)是多核CPU的计算模型,与上述的任务并行模型相同。
在CUDA中,程序在整体上完全不必遵循SIMD的要求,只需要那些必须在GPU上执行的部分局部地符合SIMD模型。值得一提的是,CUDA提出了SIMT(Single Instruction Multiple Treads)模型,按照费林分类法,SIMT也属于SIMD的范畴,因为它也是在多个数据上执行相同的指令,但在程序实现的过程中,SIMT允许由用户来分配线程,而并行计算也是以线程为单位的。CUDA为每个线程指定了标识符(编号)。在已知数量的数据上,SIMT模型可以指定自定义数目的线程,并根据线程标识符设计线程与数据关联的映射法则。
总结一下SIMD(包括SIMT)的两大特点。也就是说,要使用GPU做并行计算,要保证并行算法满足一下两点:
- 每个线程的任务互不相关。
- 每个线程执行相同的指令。
与之对应,具有一下特点的算法能够在GPU上达到最高的执行效率:
- 每个数据都需要经过相同的流程来处理。
- 数据之间没有相干性。
- 数据量庞大。