笔记——流水线基础概念

什么是流水线

流水线是利用执行指令所需的操作之间的并行性,实现多条指令重叠执行的一种技术。流水线是一种在连续指令流中开发指令级并行性的技术。流水线的明显长处是:它对编程者是透明的。
就像装配线那样,不同的步骤并行完成流水线中不同指令的不同部分,每一个步骤称为一个流水节拍或一个流水段。指令沿流水线移动一次的时间间隔是一个时钟周期,因为所有节拍同时工作,所以,机器周期取决于最慢的流水段。在理想情况下,流水线的加速比等于流水线的段数,但是通常情况下加速比与流水段之间不会有那么好的平衡,流水线需要一些附加的时间开销。
流水线可以减少指令的平均执行时间。当从不同的角度看流水线时,这个减少量的含义也相应有所不同。可以认为是减少了每条指令的平均时钟周期数(CPI),也可以认为是减少了时钟周期的长度,还可以认为在这两个方面都减少了。如果研究对象是一台每条指令分为多个时钟周期的机器,那么流水线可以看做是减少了CPI——这是我们将采纳的基本观点。如果研究对象为每条指令执行一个长时钟周期,那么流水线减少的是机器的时钟周期。

背景—RISC指令系统基础

RISC机采用流水线技术,大部分指令在一个时钟周期内完成。
RISC系统结构有以下几个关键特点,这些特点明显简化了RISC的实现:
1)所有运算使用的数据都来自寄存器,运算结果也都写入寄存器,每个寄存器的典型长度是32位或64位。
2)能够访问存储器的操作只有两种指令:从存储器中读取数据到寄存器的load指令和从寄存器向存储器中写数据的store指令。load和Istore指令可以对一个寄存器的一部分进行操作( 例如,一个字节,16 位或者32位)。
3)指令的数量比较少,所有指令的长度均相同。
这种结构使流水线的实现得到显著的简化。
大多数RISC指令系统都包括有三种类型的指令:ALU指令、load和store指令、转移和跳转。

非流水线情况下RISC指令系统的简单实现

在此方案中,假设每条RISC指令的执行最多需要5个时钟周期:
1)取指令周期(IF):
PC->MAR,1->R,M(MAR)->MDR,MDR->IR,(PC)+1->PC
2)指令译码/读寄存器周期(ID):
对指令进行译码并访问寄存器堆以读出寄存器中的内容。
固定字段译码:因为RISC指令系统中指定的寄存器位置是固定的。
可以对寄存器中的内容进行比较,判断是否为转移指令,若是则修改PC值。
需要扩展立即数操作也在此进行。
3)执行/有效地址周期(EX):
ALU指令对上一个时钟周期准备好的操作数进行运算。执行访问存储器、寄存器-寄存器ALU指令、寄存器-立即数ALU指令三种功能中的一个。
4)访问存储器(MEM)
如果是load指令,将根据前一个周期计算得到的有效地址从存储器中读取数据如果是store指令,则根据有效地址将第二个寄存器中的数据写人存储器中。
5)写回周期(WB)
寄存器-寄存器ALU指令或load指令。
将结果写人寄存器堆。结果可能来自存储器(对于load指令)或者来自ALU(对于ALU指令)。
在这种实现下,转移指令需要2个周期,store指令需要4个周期,其他指令需要5个周期。

经典的5段流水线RISC处理器(非完善)

在这里插入图片描述
只需要简单的在每一个时钟周期启动一条新的指令。
开始,我们需要确定处理器在每一个时钟周期都进行什么样的动作,并保证在同一个时钟周期没有两条指令使用相同的数据通路资源。例如,一个ALU不能同时用于计算有效地址和做减法运算。因此,我们必须保证流水线中指令的重叠不会导致这样的冲突。
多条指令的重叠引起的冲突很少:使用分开的指令和数据存储器(Cache);为处理同一时钟周期对同一个寄存器的读和写,我们在一个时钟周期的前半部分对寄存器写,该时钟周期的后半部分进行读寄存器操作;在IF段完成PC自加并写回结果,在ID段为可能转移的目标地址的计算提供一个加法器。
在流水线中引入流水线寄存器(用于在一个流水段和下一个流水段之间进行数据传递和信息控制)保证流水线的不同段中的指令不会互相影响。
在此阶段,只是简单实现流水线,其中还有很多问题,后面进行优化处理。

流水线的基本性能问题

流水线增大了CPU的指令吞吐量——即单位时间完成的指令条数,但是它未减少指令各自的执行时间。实际上,流水线技术经常要对流水线附加一些控制,因而增加了开销,使单条指令执行时间略有增加
除了流水线时延引起的限制,流水段的不平衡和流水线的附加开销也引入了某些限制。流水段的不平衡引起的限制,是因为时钟不能快于最慢的流水段。流水线的附加开销引起的限制是因为流水线寄存器的延迟和时钟偏移。流水线寄存器增加了时钟周期的启动时间和传输延迟,其中启动时间是指从寄存器输入稳定开始,直到触发写操作的时钟信号到达为止的时间之差。时钟偏移是指在时钟到达时任何两个寄存器之间的延迟,而时钟偏移的存在也导致了对时钟周期的限制。一旦时钟周期很小,以至于与时钟偏移和锁存器附加开销相当时,流水就没有用处了。

流水线的主要障碍——流水线冒险

三类冒险:结构冒险、数据冒险、控制冒险
■有停顿的流水线的CPI为∶
CPI=理想流水线CPI+(结构相关停顿 +数据相关停顿+控制相关停顿)/指令数
■有停顿的流水线的加速比为∶
加速比=流水线深度/(1+每条指令的流水线停顿周期)
消除相关、减少停顿是提高已有流水线性能最重要的手段。
结构冒险:
概念:如果因为资源冲突而无法使用某种指今的组合,那么该处理器就被称为是有结构冒险的。(现有硬件达不到指令流水的需求)
情况一:最常见的结构冒险出现在部分功能单元没有充分流水的时候,此时一系列使用该单元的指令不能按照每个时钟周期前进一拍的速率流水。
解决办法:将流水线设计的更合理。
情况二:另一种常见的结构冒险是因为某些资源没有足够多的副本,不能满足让流水线中的若干条指令同时执行。
解决方法:
1)增加资源副本:
存储器冲突:哈佛结构
两个ALU∶取指令一地址加法器
2)改变资源以便它们能并发的使用:
不相关的数据尽量使用不同的寄存器
寄存器重命名
3)通过延迟(或暂停)流水线的冲突段或在冲突段插入流水线气泡(气泡在流水线中只占资源不做实际操作),使各段"轮流"使用资源。
在某些时候,设计人员允许结构冒险存在,原因是可以降低成本或者减少单元延迟。
数据冒险:
概念:在同时执行的几条指令中,一条指令依赖于前一条指令的数据却得不到时,就会发生的冒险。
数据冒险产生的原因是流水线改变了非流水线情况下读/写操作数的顺序,使其呈现出与在非流水线处理器上的执行时不一致的指令顺序。
解决方法一:使用直通技术数据冒险引起的停顿
直通思想可以一般化,即把结果直接送到需要它的功能单元,一个结果能够从一个单元输出对应的流水线寄存器直接送到另一个单元的输入,而不限制在同一单元的输出到输入。使用硬件技术解决,也称旁路技术。
举例:
在这里插入图片描述
解决方式二:需要停顿的数据冒险
并非所有的数据冒险都可以采用旁路的方法来解决。
举例:load操作可以把结果直通给AND和OR操作,却不能直通给SUB操作。
在这里插入图片描述
load操作有一种不能只用直通就能消除的延迟。这时,需要加入一种称为流水线锁定器的新硬件来保证正确运行。通常,流水线锁定器发现冒险后就停顿流水线,直至冒险被消除。流水线锁定器停顿流水线,让需要某个结果的操作一直等到该结果产生时为止。
控制冒险
概念:流水线中的转移指令或其他改写PC的指令造成的冒险。损失比数据冒险更大。
如果一条转移指令把PC改写成它的目标地址,那么该转移称为选中转移,否则称为未选中转移。如果某条指令i是选中转移,那么,通常要到ID段的末尾在已经完成了地址计算和比较之后才能改变 PC。
有许多减少因为转移延迟造成流水线停顿的方法,这里,我们介绍4种简单的编译时调度方法。在这4种方法中,对转移的处理都是静态的,即在整个程序执行过程中对每个转移的处理都一样。
1)冻结或冲刷流水线,即在转移的目标地址确定之前保存或者删除所有紧随转移的指令。这种方法的优点是硬件和软件两方面都比较简单。转移的开销固定。
2)对所有转移都按未选中处理。因此必须注意在转移结果产生前不要改变机器的状态。因为不知道指令何时改变机器状态,以及怎样把变化改回去。在简单的5段流水线中,采用预测未选中调度策略,实现方法就是直接取下一条指令,好像转移指令只是一条普通的指令那样,流水线看起来也没有特殊之处。但是只要转移指令被选中,就需要用空操作代替取来的指令(只需清除IF/ID寄存器),并到目标地址重新取指令。相较于第一种好处是在未选中时不必再重新取下一条指令。
3)预测转移被选中,一旦完成转移指令的译码并计算出目标地址后,就假设转移被选中,到目标地址取指令。因为在我们的5段流水线中总是先得到转移的结果(知道是转移指令时就已经确定要转移了),后得到目标地址,所以这种方法不适用于我们的5段流水线(没有任何好处)。在某些机器中,尤其是隐含条件码和更强功能(于是也更慢)转移条件的机器中,转移的目标地址比其结果事早产生,这时,采用预测转移被选中的方法比较合适。
4)提供利用编译器进行优化的机会。在有些处理器中称为转移延迟,
后续指令放在转移延迟槽内。不管转移是否被选中,这条指今都要被流水。尽管延迟槽长度大于1是可能的,但实际上,所有带转移延迟的处理器一般只延迟一条指令,并使用其他的技术来处理有更多转移开销的情况。
流水线遇到分支指令时,按正常方式处理,同时执行延迟槽中的指令。
编译器的任务就是在延迟槽中放入有用的指令,称为延迟槽调度。有三种调度方法∶
从分支前(from before)调入
从目标处(from target)调入
从失败处(from fall-through)调入
在这里插入图片描述
带转移延迟的机器通常不允许在延迟槽内有转移指令。
延迟槽内可选指令:填入延迟槽的是转移前的一条不相关指令;将转移目标指令填入延迟槽;用延迟槽后的指令填入延迟槽等。

如何实现流水线

非流水线下指令沿数据通路的流动:
在这里插入图片描述
MIPS数据通路的实现,它允许在4个或5个时钟周期内完成一条指令。除了上述多周期的设计,也可以让每一条指令占用一个长的时钟周期,在这种情况下,所有的临时寄存器可以抛弃不用,因为在一条指令的内部时钟周期之间无需通信。指令都在一个长的时钟周期内执行,并在该时钟结束时将结果写入存储器、寄存器或者PC。对于这种处理器来说,CPI将始终是1,但是其时钟周期大致等于多周期处理器的5倍,因为每一条指令都要通过所有的功能单元。设计者不会使用这种单周期的实现方法,因为有两方面的原因∶首先,对于大多数CPU来说,不同的指令需要的时钟周期时间不同,单周期的实现方法效率很低。其次、单周期的实现方法需要重复的功能单元,而在多周期的实现中功能单元可以共享。不过,这个单周期的数据路径可以用来说明流水线技术是如何改善处理器的时钟周期而不是CPI的。

MIPS的基本流水线:
在这里插入图片描述
每个时刻每条指令都只在一个流水段上是活动的,因此任何指令所做的动作都发生在一对流水线寄存器之间。
在这里插入图片描述
MIPS流水线每一个流水段发生的事件。在IF段,除了取指令和计算新的PC之外,还将增加 PC存入流水线寄存器(NPC),以供后面计算转移目标地址时使用。PC在IF段被一个或者两个数据源更新。在ID段,执行读寄存器操作,对IR的低16位进行符号扩展,并沿IR和NPC传递。在EX段,执行ALU操作或者进行地址计算,并沿IR 和B寄存器(如果是store指令)传递。同时,当指令选中转移时,将条件码的值设置为1。在 MEM段,更新存储器,进行转移决策,在需要时写入 PC,并传送在最后段所需的数据。最后,在WB段,用ALU的输出或者装入的值来更新寄存器。为了简化起见,我们在流水段之间传送整个IR,尽管随着指令沿着流水线前进过程中IR有用的部分越来越少。

MIPS流水线控制的实现:

让一条指令从译码段(ID)流动到执行段(EX)的操作通常称为发射指令,经过了这一步的指今称为已经被发射的指令。对 MIPS定点流水线,所有的数据冒险都可以在流水线的ID段检查到。如果存在一个数据冒险,相应指令就在它发射前被停顿。同理,可以在ID段确定需要什么直通,从而再次添加适当的控制。通过较早检测锁定关系可以减少硬件的复杂性,因为除了整个流水线被停顿的情况外,硬件不需要停顿一条已经更新了机器状态的指令。另一种方法是,在一个使用操作数(在流水线的EX和MEM段)的时钟周期开始时检测到冒险或直通。流水线冒险检测硬件比较相邻指令的源操作数和目的操作数即可,检测的范围仅限于一条写指令之后的两条指令的源操作数和目的操作数。
举例:由load指令产生的数据冒险可以用检查ID段的方法来消除,而对ALU输入端的直通通路可以在EX段实行。
在这里插入图片描述
对于load指令产生的冒险:控制单元必须在流水线中插入停顿,当预测到一个冒险之后,只需要把ID/EX流水线寄存器的控制位清为0。
对于直通来说:所有旁路通常都是从ALU或数据存储器的输出端到ALU与数据存储器的输入端.或者是零检测单元。于是,实现旁路时只需要比较IR中处于EX/MEM 与MEM/WB段的目标寄存器和处于ID/EX与EX/MEM段的源寄存器。

处理流水线中的转移

在这里插入图片描述
通过把零检测和地址计算移到ID段来缩短转移冒险引起的停顿,第一个变化是将转移目标地址的计算和转移条件的判断都移入ID段来完成。第二个变化是在IF阶段写指令的PC,这个PC值可能是ID段计算的转移目标地址,也可能是IF阶段计算的PC增加值。作为比较,原图示为从EX/MEM寄存器得到转移目标地址,在MEM阶段写入结果。
在改动之后,如果在ALU指令后面跟着一条使用这个ALU指令结果的转移,会只有一个数据冒险的停顿。在某些处理器中,转移冒险要比所举的例子使用更多的时钟周期,例如,译码和读寄存器分成不同段的处理器可能要增加转移延迟,该延迟长度至少为一个时钟周期。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>