指令级并行
标准5段流水线
1.取指(IF):根据PC中的地址,从存储器中取出指令并放入指令寄存器IR,PC+4(假设每条指令占4字节)
2.译码(ID):对指令进行译码,并根据IR中的寄存器地址访问通用寄存器,读出所需的操作数
3.执行(EX):运算类指令执行运算;load/store指令计算存储器有效地址;转移类指令计算转移目标地址,设置条件码
4.访存(MEM):运算类指令空操作;load指令读存储器;转移类指令若条件成立,目标地址送入PC
5.写回(WB):运算指令计算结果写回寄存器;load指令读入的数据写入寄存器;store指令将数据写入内存;转移类指令空操作
例子:
fadd rd,r1,r2:取指;译码,读取寄存器r1和r2;ALU计算r1+r2;空;将结果写到寄存器rdfld rd,c(rs):取指;译码,读取寄存器rs;计算存储器地址c+rs;读取存储器数据;将数据写到寄存器rdfsd rd,c(rs):取指;译码,读取寄存器rs与rd;计算存储器地址c+rs;空;将数据写到存储器
IPC:单位时间内执行完的指令数量;CPI:平均每条指令的执行时间
注:流水线技术(现代处理器已经是深度流水线,一般有十多个功能段)并不能提高一条指令的执行效率,但是可以提高吞吐量(即IPC)或者说降低CPI。流水线的理想加速比为N(即流水线段数),不能达到的原因:pipeline开销、各阶段不平衡问题(时钟周期取决于最慢阶段所需时间)。
指令指针(Instruction Pointer,IP):存放将要执行的指令的地址,常用程序计数器PC来指代(严格讲,PC=CS:IP)
指令寄存器(Instruction Register,IR):存放将要执行的指令
相关与冒险
在程序运行中,如果必须等前一条指令执行完才能执行后一条指令,那么这两条指令是相关的。相关是程序的属性,是否会产生冒险以及冒险是否会导致停顿均属于流水线结构的性质。
结构冒险:不同的指令同时使用了相同的资源,从而产生冲突,可以通过合理的流水线设计等来解决。比如:寄存器在ID与WB阶段被访问,那么可以设置两个端口:读端口与写端口;内存在IF与MEM阶段被访问,那么可以设置分离的数据cache与指令cache。
数据冒险:流水线改变了读/写操作数的顺序,可以通过转发(forwarding)缓解,但是不够,仍需要加入停顿(stall)。
- RAW(写后读)
i:add r1,r2,r3 j:sub r4,r1,r3
指令i/j都使用寄存器r1,且存在数据流动(i将数据写到r1并传递给j用),所以为数据相关(真相关)。 - WAR(读后写)
i:sub r4,r1,r3 j:add r1,r2,r3
指令i/j的相关没有数据传递,是因为寄存器不够共用r1而造成的,所以为反相关。 - WAW(写后写)
i:sub r1,r4,r3 j:add r1,r2,r3
指令i/j的相关同样没有数据传递,是由于共用输出寄存器r1而造成的,所以为输出相关。
控制冒险:跳转指令在MEM阶段在确定下一条指令的地址,所以需要停顿3个cycle来等待。
指令调度与分支预测
简单流水线的缺陷:指令按序发射,按序执行,按序结束。一旦前面指令停顿,后面的指令必须等待,不能提前执行。
比如:
DIV.D F0,F2,F4
ADD.D F10,F0,F8
SUB.D F12,F8,F14
前两条指令存在RAW相关,它们之间要插入stall时钟周期,但是SUB.D指令与它们无关,在普通流水线中也必须跟着停顿,不能提前执行。
启发:指令不按顺序执行,只要操作数准备好并且没有结构冲突,就可以提前执行,提前完成,从而提高流水线的效率。这就是指令调度的思想。
循环展开




优点:循环展开能够增大指令调度的空间,减少循环分支指令的开销,即增大指令流水线的效率。
限制:代码长度的增加,可能引起指令cache miss。因为需要更多的寄存器,所以给寄存器带来压力,并且会增加编译器的复杂性。
分支预测
静态分支预测:永远预测跳转(taken)或不跳转(not taken),这样平均命中率为50%,更精确的办法是根据原先运行的结果进行统计从而尝试预测分支是否会跳转。
动态分支预测
性能依赖预测的正确率以及预测错误的代价。
最简单的方案:分支预测缓存(Branch Prediction Buffer,BPB)或者分支历史表(Branch History Table,BHT)

转移的历史情况记录在BHT中,其中分支指令地址作为BHT的索引(只是真实地址的部分低位)。预测位最简单为1位,通常为2位。2位预测器的有限状态机包含4个状态:强or弱跳转执行(taken)、强or弱顺序执行(not taken)。优点是,分支指令必须连续选择某条分支两次,才能发生强状态翻转,从而改变预测的分支。如下图所示:

相关预测器(两级预测器)
简单的2位预测器仅仅根据自己的历史做预测,但是一个分支是否转移成功往往跟最近的几个分支相关。利用其他分支行为来进行预测的分支预测器称为相关预测或两级预测器。一般情况下,\((m,n)\)预测器利用最近\(m\)个分支的行为从\(2^m\)个分支预测器中进行选择,其中每个预测器都是单个分支的\(n\)位预测器。(0,2)预测器即是2位预测器。\((m,n)\)预测器位数:\(2^m\times n\times 需要预测的分支数目\)。理解:第一级是\(m\)位的移位寄存器,映射到共有\(2^m\)个表项的Branch History Pattern中,其中每个表项都是一个\(n\)位预测器(每个预测器有\(2^12=4k\)项,所以共有\(4k\times n位\)),所有位数即\(2^m\times 4k\times n\)

分支目标缓存(Branch Target Buffer,BTB)
一个高速缓存,必要字段包括:(1)分支指令的PC;(2)一个taken的分支指令的转移地址。目的:在取指阶段(译码之前)就能知道:本指令是否为分支指令;如果是分支指令,是否发生转移;如果发生转移,转移目标是哪里。工作原理:在取指阶段,将PC与BTB中的条目比较:如果命中,则返回所保存的转移目标地址,并用于下一条指令取指;如果BTB不命中,则根据PC+4取指。


一点改进:不仅存转移目标指令的地址,同时存储指令。取的时候将地址与指令全取出来,从而可以少一次访存。
计分板算法
简单流水线中,写回发生在最后一拍,因此按序执行时不会出现WAR和WAW冲突,但是在乱序执行时就可能出现。计分板是一个集中控制部件,其功能是控制数据寄存器与FU之间的数据传送,记录数据寄存器和多个FU的状态变化情况,从而判断指令的相关性。
将ID流水级分为独立的两级:issue级和read operands级,对于运算类指令忽略访存阶段,所以计分板算法四个阶段为:
- 发射(issue):顺序发射,译码,检查结构冒险与WAW冒险(write的目的寄存器不会被其他指令的write所写)
- 读操作数(read operands):RAW冒险消除后读取操作数
- 执行(execution):进行运算,并向计分板通知指令执行完成
- 写回(write result):WAR冒险消除后写回结果
计分板运作机制:维护了三个状态表,根据状态表来判断当前执行某一指令是否会存在相关
- 指令状态表:每条已发射指令四个阶段所在的时钟周期
- 寄存器状态表:记录每个寄存器是否会被某个功能单元写入,如果是,记录将会被哪个功能单元写入
- 功能单元状态表:使用9个字段的标记记录各个功能单元的使用和等待情况
通过指令状态表,可以看到计分板算法的效果:按序发射、乱序执行、乱序写回。计分板算法的局限:
- 计分板算法可以消除RAW相关,是因为乱序执行,实际出现RAW时,也是需要停顿的。
- 计分板算法无法消除WAR和WAW,这一点对性能影响较大。
Tomasulo算法
引入保留站,三个阶段(issue、execution、write result)
- 分布式的保留栈:每个保留栈可以自主判断冒险;CDB的使用:一个结果被多个功能单元等待,这些功能单元能同时得到该结果的广播消息。如果结果保存在寄存器中,寄存器只能由一个功能单元访问。
能够消除WAW、WAR冒险:利用保留栈可实现寄存器的重命名
- WAR:一旦数据从寄存器读到保留栈,则不再依赖该寄存器;WAW:两条指令的目标寄存器相同,但他们的结果由不同的功能单元等待,仍然不会发生WAW

浙公网安备 33010602011771号