流水线

来源:互联网 发布:plsql展示数据库表 编辑:程序博客网 时间:2024/04/27 21:38

4.1 流水线的概念

关于流水线,生活中到处可见,工厂的生产流水线和食堂的打饭流水线都是流水线的经典范例。在《MIPS体系结构透视》中借用食堂打饭流水线的例子很好的阐述了流水线的概念。

所谓流水线(pipeline),就是将那些重复性的工作分解成几个串行的部分,使得工作能在工人中间移动。每个熟练工人只需要依次将他们熟悉的那部分工作做好即可。虽然每个顾客等待服务的总时间有所增加,但是却有四个顾客能同时接受服务,这样在午餐高峰期能够接待的顾客数量增加了三倍。

假如Evie的小卖部新增了一种炸鸡腿,交由Bert负责派发,这样Bert在给顾客派发炸土豆时还得派发炸鸡腿。此时,Bert这一环节相比以前耗时增加,导致流水线效率下降。如是,Evie再请了一个朋友加入食物派发流水线,专门负责派发鸡腿。我们说Evie店的流水线从四步(step)增加到了五步,流水线的级数(深度)增加了一级,相应增加了人力成本。

4.2 CPU指令执行流水线

如果将程序看成是内存中存储的一些指令的话,一个即将运行的程序看起来和排着队等待接受服务的顾客没什么相似之处。但是,在CPU看来,情况就不一样了。CPU从内存中提取每条指令,进行译码,找到所需要的操作数,执行相应操作,并存储运算产生的结果——然后又从头开始重复同样的工作。这样等待执行的程序就是一个指令的序列,该队列中一次只有一条指令经过CPU。

由于每条指令要做不同的工作,因此在CPU内部已经配有各种不同的专用的大块逻辑电路(每一流水阶段都有独立的逻辑电路来处理),所以构造一个流水线并没有使CPU复杂度增加多少,只是让CPU工作得更努力一些而已。

采用流水线技术后,并没有加速单条指令的执行,每条指令的操作步骤一个也不能少,只是多条指令的不同操作步骤同时执行,因而从总体上看加快了指令流速度,缩短了程序执行时间。

为了进一步满足普通流水线设计所不能适应的更高时钟频率的要求,高档次处理器中的流水线的深度(级数)在逐代增多。当流水线深度在5~6级以上时,通常称为超流水线结构(SuperPipeline)。显然,流水线级数越多,每级所花的时间越短,时钟周期就可以设计的越短,指令速度越快,指令平均执行时间也就越短,但是相应设计成本也会增加。

在TI C6000 DSP中,所有指令的执行大概分为Fetch(取指)、Decode(译码)、Execute(执行)三个大的步骤,每个大的步骤又可以细分为一些小的步骤,实现了更深的流水线。

4.3 MIPS经典五级流水线

(1)MIPS的五级流水线

MIPS体系架构依据流水线结构设计。只要CPU从缓存中获取数据,那么执行每条MIPS指令就被分成五个流水阶段,并且每个阶段占用固定的时间,通常是只耗费一个处理器时钟周期。RD/WB操作只占用半个时钟周期,故MIPS五段流水线只占用四个时钟周期。

MIPS处理器在设计时,将处理器的执行阶段划分为以下五个阶段:

<1> IF:Instruction Fetch,取指。从指令缓存(I-Cache)中获取下一条指令。

<2> ID(RD):InstructionDecode(Read Register),译码(读寄存器)。翻译指令,识别操作码和操作数,从寄存器堆中读取数据到ALU输入寄存器。

<3> EX(ALU):Execute,执行(算术/逻辑运算)。在一个时钟周期内,完成算术或逻辑操作。注意,浮点算术运算和整数乘除运算不能在一个时钟周期内完成。

<4> MEM:Memory Access,内存数据读或者写。在该阶段,指令可以从数据缓存(D-Cache)中读/写内存变量。平均来说,大约四分之三的指令在这一阶段没有执行任何操作,为每条指令分配这个阶段是为了保证同一时刻不会有两条指令都访问数据缓存。

<5> WB:Write Back,写回。操作完成后,将计算结果从ALU输出寄存器写回到通用寄存器中。

对于运算指令,在MEM阶段空闲。对于load指令,在EX阶段计算要访问的地址,在MEM阶段从内存中将数据读入到MEMregister(MEM和WB之间的流水线寄存器)中,在WB阶段,将MEM register的数据写回到Register File中。对于store指令,在EX阶段计算要访问的地址,在MEM阶段将寄存器中的数据写回到存储器中。

(2)流水线和缓存

高效的流水线操作要求每个阶段占用相同的时间。高效的流水线还依赖于高速缓存(Cache),可以将内存访问速度提高50倍左右。当CPU需要数据时,首先在缓存中查找,如果该数据在缓存中则命中,那么缓存很快就把数据返回给CPU。由于无法猜测CPU将使用什么数据,故缓存仅存储最近一段时间内CPU从主内存中获取的数据副本。如果缓存缺失(没有命中),则需要重填(Invalidate and Refill)。

x86的寄存器个数很少,所以同样的程序编译给x86会比MIPS使用多得多的数据存取操作。当然,x86使用堆栈来代替寄存器给这些额外存取使用,这些堆栈位置将是内存中使用非常频繁的区域,对应高速缓存的使用效率非常高。

MIPS体系架构设计时采用了独立的指令缓存和数据缓存,这样CPU就可以同时获取指令和读写内存变量。

(3)严格流水线的限制

RISC(Reduced Instruction Set Computing,精简指令集)相对CISC(Complex Instruction Set Computing,复杂指令集)对指令集作了巧妙和有效的规定,从而使得流水线可以高效和成功的实现。所有的MIPS指令都经过严格的定义,以遵循同样的流水线阶段顺序,即使这些指令在某个流水线阶段什么也不做。于是最终结果是:只要CPU保持从缓存中命中(hit)数据,就能在每个时钟周期开始一条指令。

流水线的严格要求限制了指令的某些操作能力。

首先,要求所有的指令一样长(刚好是一个机器字长32位),这样的指令可以在固定时间被读取。定长指令的要求限制了操作的复杂程度。比如,在指令中没有足够的位空间对真正复杂的寻址模式进行编码。定长指令直接导致的一个问题就是,一个典型的程序在支持变长指令的x86体系架构上,编译后其指令平均长度只有3个字节,在MIPS代码中则全部是4个字节,从而占用了更多的内存空间。通常MIPS二进制文件要比680x0或80x86二进制文件大20%~30%。

第二,MIPS中的指令操作必须符合流水线特性。指令操作只有在正确的流水线阶段才能够执行,并且必须在一个时钟周期内执行完毕。比如,寄存器写回阶段只允许一个值存储到寄存器堆,因此这些MIPS指令只能修改一个寄存器的值。出栈操作需要将两个值(栈中的数据以及递增的栈指针值)写回到寄存器中,因此它不适合流水线。所以,MIPS没有提供对栈操作的硬件支持,没有x86那样的push/pop指令。

第三,流水线的设计规则没有实现可以操作内存变量的指令。缓存或者内存的数据只有在流水线的第四阶段才能够获得。对于ALU而言,这些数据来得太迟了。内存访问仅通过简单的load/store指令来将数据导入或导出寄存器。


0 0