GPU如何助力高性能计算
CPU执行指令的方式就是一个接着另一个地执行。CPU中有许多能够加速串行计算的技术。高速缓存、无次序执行、超标量技术、分支预测……均为抽取指令的技术或一系列指令的串行级并行机制。CPU对片上高速缓存的设计与容量的依赖也非常大。如果程序大小与CPU高速缓存容量不匹配,那么该程序在CPU上的运行速度将会很慢。
CPU的主要运作原理,不论其外观,都是执行储存于被称为程序里的一系列指令。在此讨论的是遵循普遍的冯·诺伊曼架构设计的装置。程序以一系列数字储存在计算机记忆体中。差不多所有的冯·诺伊曼-CPU的运作原理可分为四个阶段:提取(fetch)、解码(decode)、执行(execute)和写回(writeback)。
第一阶段,提取,从程序记忆体中检索指令(为数值或一系列数值)。由程序计数器(PC)指定程序记忆体的位置,程序计数器保存供识别目前程序位置的数值。换言之,程序计数器记录了CPU在目前程序里的踪迹。提取指令之后,PC根据指令式长度增加记忆体单元[iwordlength]。指令的提取常常必须从相对较慢的记忆体查找,导致CPU等候指令的送入。这个问题主要被论及在现代处理器的高速缓存和管线化架构(见下)。
CPU根据从记忆体提取到的指令来决定其执行行为。在解码阶段,指令被拆解为有意义的片断。根据CPU的指令集架构(ISA)定义将数值解译为指令[isa]。一部分的指令数值为运算码(opcode),其指示要进行哪些运算。其它的数值通常供给指令必要的信息,诸如一个加法(addition)运算的运算目标。这样的运算目标也许提供一个常数值(即立即值),或是一个空间的寻址值:寄存器或记忆体地址,以寻址模式决定。在旧的设计中,CPU里的指令解码部分是无法改变的硬体装置。不过在众多抽象且复杂的CPU和ISA中,一个微程序时常用来帮助转换指令为各种形态的讯号。这些微程序在已成品的CPU中往往可以重写,方便变更解码指令。
在提取和解码阶段之后,接着进入执行阶段。该阶段中,连接到各种能够进行所需运算的CPU部件。例如,要求一个加法运算,算数逻辑单元(ALU,arithmetic logic unit)将会连接到一组输入和一组输出。输入提供了要相加的数值,而且在输出将含有总和结果。ALU内含电路系统,以于输出端完成简单的普通运算和逻辑运算(比如加法和比特运算)。如果加法运算产生一个对该CPU处理而言过大的结果,在标志寄存器里,运算溢出(arithmetic overflow)标志可能会被设置(参见以下的数值精度探讨)。
最终阶段,写回,以一定格式将执行阶段的结果简单的写回。运算结果极常被写进CPU内部的寄存器,以供随后指令快速访问。在其它案例中,运算结果可能写进速度较慢,但容量较大且较便宜的主记忆体。某些类型的指令会操作程序计数器,而不直接产生结果数据。这些一般称作“跳转”(jumps)并在程序中带来循环行为、条件性执行(透过条件跳转)和函数[jumps]。许多指令也会改变标志寄存器的状态比特。这些标志可用来影响程序行为,缘由于它们时常显出各种运算结果。例如,以一个“比较”指令判断两个值的大小,根据比较结果在标志寄存器上设置一个数值。这个标志可借由随后的跳转指令来决定程序动向。
在执行指令并写回结果数据之后,程序计数器的值会递增,反复整个过程,下一个指令周期正常的提取下一个顺序指令。如果完成的是跳转指令,程序计数器将会修改成跳转到的指令地址,且程序继续正常执行。GPU内部的并行计算架构围绕两个基本概念而设计。首先,程序中的数据可分成许多个部分,而为数众多的核群可以并行地处理这些数据。第二个架构方面的设想是,数据将不与高速缓存匹配。例如在图形计算或石油天然气数据处理上,数据量可能会达到兆字节甚至是太字节,用高速缓存来容纳如此巨大的数据量几乎是不切实际的。考虑到这两点设想,GPU被设计为能够使用数以千计的线程,所有线程均并行地执行,能够访问巨大容量的本地存储器。 #p#page_title#e#
GPU设计初充为高性能三维图形应用,具有强大的计算能力和很高的存储带宽,而这两点对于高性能三维图形应用是至关重要的。以往GPU计算都是专用于这些应用的,但现在新型的GPU(如:GeForce 8800)允许具备一定的用户可编程性,这就使得GPU能够面向更通用的计算。不过,通用计算功能首先必须被高效地映射到GPU上。
三维图形计算被组织进一个图形管道,该管道描述场景输入和图像输出之间的一系列计算阶段。图形管道的输入是一个场景,它由许多几何体(定义为连接顶点)以及用来从几何体计算场景的图形指令组成。
GPU对那些顶点进行处理,并将之变换为屏幕空间几何体(screen-space geometry),该几何体通过光栅化(rasterization)过程依次被划分为像素片段(pixel-sized fragments)。每一片段与屏幕上的一个像素位置相关。最后,这些片段被处理并组合到由像素组成的图像中。
该管道包含大约12级(stage),每级都在一个GPU里执行,作为在同一裸片上每级的一个单独处理器。管道的典型输入是几十个到几十万个顶点,每一个顶点都可在GPU上并行处理。
因此,复杂的图形管道可以按空间划分,GPU上的各个处理器并行运行每一级。这种体系与CPU不同,它使每一个处理器可以专用于执行自己的任务,而CPU只有一个处理器。在CPU上实现图形管道是按时间来划分复杂任务的:CPU在其处理器上处理管道的第一级,仅当第一级完成时才开始下一级。
从通用编程人员的观点来看,GPU包含两个重要的级,都是用户可编程的,即顶点级(vertex stage)和片段级(fragment stage),前者在每一个顶点输入端运行用户定义程序,后者在每一个片段上运行用户定义程序。
两个级具有类似的编程模型。在运行长串的输入(几十万个顶点或片段)时两者都很有效率。不过,每个输入都是被独立处理的,因此一个顶点或片段的计算不会影响另一个顶点或片段。用这种方法,能够并行处理许多顶点或片段。
每一个输入用相同的片段或顶点程序处理。这些程序通常在SIMD(单指令多数据)情况下运行时最有效,这意味着用控制每个计算的指令序列来并行计算每个顶点或片段。不过,新近开发的硬件已开始允许在这些程序中执行更为复杂的控制流程。
那么,片段或顶点程序是什么样呢?首先,它们都只支持IEEE单精度浮点运算。其次,二者都可以从全局存储器的任意位置读取,但不能写入任意全局存储器。顶点程序的输出是一个单独的顶点,而片段程序的输出则只是这该片段像素位置上的一个片段。
在通用编程人员看来,这些局限性意义重大,能使GPU支持许多功能性单元,这些单元能并行运算大量数据元的顶点或片段程序。例如,在16个并行片段处理器上,Nvidia GeForce 6800的片段程序的总体运算速率超过每秒500亿次浮点运算。
若顶点和片段程序具有可编程性,就可以在GPU上运行通用应用。迄今大多数努力都集中在片段级,因为它比顶点程序具有更多的处理器和更高的性能。要使这些应用有效,关键在于将必要的计算映射到片段处理硬件的SIMD并行编程模型上。
在图形硬件上构建通用程序的典型方法之一是:给定一个大的几何体,如屏幕大小的矩形。该几何体覆盖屏幕的主要部分,故产生大量片段。
由该几何体产生的每个片段都可以被并行处理。现在的GPU能用相同的指令并行处理16个片段。每一个片段计算都能从全局存储器的任一位置读取(矢量术语称为“搜集”),但不能写入任意全局存储器(“分散”)。
片段程序的输出是全局存储器中的一个图像,每一片段计算都对应于该图像中的一个单独的像素点。
一些简单的应用可在单个图形管道循环中表达出来,但许多复杂的应用却不行。这些更复杂的应用要通过利用后继循环计算中的单循环全局图像输出,在图形管道的多个循环中才能完成。
与CPU相比,GPU具有更高的计算速度和存储器带宽,在那些能很好地映射到其编程模型的应用上,它极具潜力。而编程人员的挑战则在于,如何有效地将感兴趣的应用映射到图形硬件的图形隐喻(graphics metaphors)和限制性编程模型中。 #p#page_title#e#