流水线冒险概念:
导致流水线出现停顿的因素称为流水线冒险,主要分为三类:结构冒险、数据冒险、控制冒险。
一、结构冒险
概念:因处理器资源冲突,而无法实现某些指令的组合,就称该处理器有结构冒险。
例子:
以MIPS流水线为例,指令和数据都存储在存储器中,IF阶段需要访问存储器,MEM阶段也需要访问存储器,在早期的处理器中,程序和数据存储器没有分开,下图在第4个Cycle,IF和MEM同时访问存储器导致其中一个操作要等待。现在的处理器,程序存储在LIP Cache,数据存储在LID Cache,单独访问,因此不存在这个问题。
二、数据冒险
概念:
流水线使原先有先后顺序的指令同时处理,当出现某些指令的组合时,可能会导致指令使用了错误的数据。
例子:
“add R1,R2,R3”将寄存器R2和R3的和赋值给R1,“add R4,R1,R5”将寄存器R1和R5的和赋值给R4,很明显,R1在第1条指令中被更新,然后再第2条指令中被使用, 可使R1必须在第5个Cycle之后才被更新,而指令2在第4个Cycle就要访问R1,这时的R1还不是正确的值,执行结果出错。
解决方案如下:
(1)插入气泡
在两条语句之间增加两个Cycle等待。延时两个Cycle,Cycle 5将第1条指令的结果写回到寄存器R1中后,第2个add指令的EX单元就可以正常执行了。
缺点:中间增加了两个Cycle的等待,影响了执行效率。
(2)直通(Forwarding)解决
当硬件检测到当前指令的源操作数正好在EX/MEM流水线寄存器中时,就直接将EX/MEM寄存器的值传递给ALU的输入,而不是从寄存器堆中读数据。
也不是每种数据冒险都能解决,同时又不引起stall,如下面这个例子:
sub指令需要R1,但是R1最早也要在Cycle4才能到MEM/WB寄存器中,即使将MEM/WB寄存器直通到EX的输入端,也需要延时一个Cycle才能使用。
三、控制冒险
什么是控制冒险:
从微观的角度,在流水线处理器中,指令是并行处理的,在当前指令正在执行时,后面的很多条指令已经完成了取指和译码等步骤。然后,在程序中会存在很多的跳转语句,如果程序的实际执行路径是要跳转到其他的地址去执行,那么流水线中已经做好的这些取指和译码工作就白做了,这就是流水线的控制冒险。此时,处理器需要排空流水线,跳转到新的地址处重新进入流水线。跳转对程序性能的损失是巨大的,流水线越深,损失越大。
我们以DSP流水线为例子说明这个问题的危害性:
在下面这个DSP流水线中,指令2在第8个Cycle执行时,发现要跳转到其他地方,于是流水线后面的PG等工作就全白做了。
X86处理器使用硬件冲刷流水线来保证发生跳转时,流水线能正确执行,在DSP中,硬件不处理这些冒险,而改用软件来处理。DSP通过增加NOP来排空流水线,在跳转语句后增加5个NOP操作来保证流水线正确。
跳转指令的执行阶段修改程序指针的地址,加了5个NOP后,MPY的流水线正好可以顺利执行。在DSP中,编译器可以将指令乱序,用有效指令替代NOP指令,这样就可以避免了跳转带来的性能损失。
其他:
在X86 CPU上,使用分支预测用来避免跳转带来的损失。