从多核到众核处理器( 四 )


【从多核到众核处理器】这里可能需要叉开话题来讲一下并行的基本分类了 。一般地讲,并行处理有三个分类:数据并行、指令并行和线程并行 。线程是一串串行执行的指令,每条指令操作一个或多个数据 。在此基础上,实现并行的方式有三种:
一种是多个这样的串行指令序列同时执行,就是Hydra为代表的线程并行模式;
第二种数据并行是同一条指令应用在并行的数据上 。比如本来是一条加法指令计算C=A+B,同时将加法应用到一组A和一组B上得到一组C上就是数据并行 。SIMD和即将讲到的Imagine都利用了这种并行;
第三种是指令并行,也就是说在同一时间发射多条指令,同时计算不同数据多个不同运算,VLIW就是这样一种并行方式 。但是由于实现VLIW的编译器难度太高,使得直接实现大规模可扩展的指令并行比较困难 。
回到多核处理器的学术路径上来 。Imagine是斯坦福的一个数据并行的多核处理器 。Imagine有8个ALU单元被同一个控制器所控制,同时对大量的并行数据进行同样的操作 。这种处理器的模式后来被称为流处理器 。后面我们讲到的Nvidia的Fermi就是这种数据并行流处理器的一种实现实例 。下面这个图即是Imagine的结构框图,可以看到它是多么像一个大型的SIMD单元啊 。实际上它也即是48个ALU单元分成了8个SIMD簇 。但是不可否认的是,就这样一个看似简单的设计提供了极高的数据并行度,使得它在处理一系列与多媒体有关的应用上得心应手,发挥了更多晶体管所带来的性能优势 。

从多核到众核处理器

文章插图
 
接下来我们来GPU处理器结构:Nvida的Fermi以及前一代的GT200,然后我们就可以发现他们和Imagine惊人的相似之处:每一个处理器核是一个简单的ALU阵列 。当然,在Nvidia的名词里,处理器核叫Streaming Multiprocessor(SM),每个Fermi的SM里有32个32位ALU、32个单精度的浮点运算单元还有一些特殊运算单元;每个GT200的SM里的运算单元少地多 。SM相当于Imagine里地ALU Cluster,能够执行SIMD的操作,但是绝对和Intel以及AMD里面的处理器核相去甚远 。通用处理器中的每个核里有庞大的指令池和寄存器堆,执行繁杂的指令预取,分支预测,条件跳转等操作,虽然计算单元不如SM多,但是计算精度较高(64位) 。换句话说,如果你的程序没有那么宽的单指令多数据并行,那么不要指望SM比传统处理器核快 。
片上存储是为流数据简化(也算是优化)过的 。在传统的GT200中,这种存储就叫texture cache,在Imagine里叫Stream memory 。在图形图像中,大部分的操作是流水线化的,所以这种cache不需要支持不同SM之间存储共享(即使需要,必需程序员显式指定,而不是处理器代劳),部分的缓存甚至是私有的,就连地址空间都是独立的 。这对于流处理器来说,没有任何问题 。我们把流处理想象成一个巨大的SIMD,不同的data之间没有任何共享,texture cache就够用了 。但是一旦有了分支、线程并行、数据交换、信号锁,这种cache就会让程序员头痛,于是Fermi做了一些优化,使得片上缓存至少在地址上是共享了,但是并不完全支持缓存一致性 。只有当程序员显示使用同步信号量,存储的顺序核一致性才是可以保证的 。
从多核到众核处理器

文章插图
 
这里需要澄清一个很明显的误区就是在GPU上编程能够成百倍地提高CPU的性能,这个观点在Nvidia推出CUDA的时候被狠狠吹捧了一番,不过后来大家发现GPGPU的能力其实非常有限:
  1. 首先,只有存在大量规则数据并行的应用程序,GPU才能发挥其巨大优势 。程序中的分支跳转以及线程间的数据共享都是GPU的软肋,就算能够被支持,效率也不高 。说直白一点,如果谁想在GPU上做Web Server,那基本上是痴人说梦 。
  1. 其次,GPU需要对应用程序进行大量优化,以挖掘其并行性 。这个优化过程需要对GPU结构和被优化的程序本身有着深刻地理解 。这和在通用处理器编程中打开几个优化选项的难度不可同日而语 。另一方面,通用处理器的编程工具链经过若干年来的积累已经能够自动完成很多优化功能使得程序员可以站在巨人的肩膀上,而对于GPU来说,这样的肩膀还不厚实 。直白地说,如果需要在非图形图像应用上释放GPU的潜力,花钱花时间和请高人都是必不可少的 。
  1. 最后,就算对于GPU擅长的应用,如果对CPU和GPU程序都做优化,性能的差别也仅仅在一个数量级之内 。ISCA有篇文章探讨了这个问题,一个粗浅的结论是,对于作者考察的几个例子来说,优化过的GPU程序在Nvidia GTX280上,比在Intel Core i7 960上平均快了2.5倍 。


    推荐阅读