第三章:流水线上的冒险

一、结构冒险

因为处理器资源冲突,而无法实现某些指令的组合,就称该处理器有结构冒险。我们以MIPS流水线为例,指令和数据都存储在存储器中,IF阶段需要访问存储器,MEM阶段也需要访问存储器,在早期的处理器中,程序和数据存储器没有分开,下图在第4个Cycle,IF和 MEM同时访问存储器导致其中一个操作要等待。现在的处理器,程序存储在LlD Cache中,数据存储在LID Cache中,单独访问,因此不存在这个问题。

在这里插入图片描述

二、数据冒险

流水线使原先有先后顺序的指令同时处理,当出现某些指令的组合时,可能会导致指令使用了错误的数据。看下面这个例子:
在这里插入图片描述
“add R1,R2,R3”将寄存器R2和R3的和赋给R1,“add R4,R1,R5”将寄存器R1和R5的和赋给R4,很明显,R1在第1条指令中被更新,然后在第⒉条指令中使用,可是R1必须在第5个Cycle之后才被更新,而指令2在第4个Cycle就要访问R1,这时的R1还不是正确的值,执行结果出错。
一种解决方法是在这两条语句之间增加两个Cycle的等待:
在这里插入图片描述

延时两个Cycle,Cycle 5将第1条指令的结果写回到寄存器R1中后,第2个add指令的EX单元就可以正常执行了。
这种方法有很大一个缺点就是中间增加了两个Cycle的等待,影响了执行效率。实际的处理器使用直通(Forwarding)来解决这个问题。
当硬件检测到当前指令的源操作数正好在EX/MEM 流水线寄存器中时,就直接将EX/MEM寄存器的值传递给ALU的输入,而不是从寄存器堆中读数据,如下图:
在这里插入图片描述
不只寄存器访问有这样的问题,Memory访问也有同样的问题。看下面的这个例子:
在这里插入图片描述
“Ioad R1,(R2)”将存储器中的值装载到R1中来,存储器的地址在R2中存储,“storeR1,(R3)”将R1中的值放到存储器中去。在Cycle 4,load指令的MEM阶段将存储器中的值装载到MEM/WB寄存器中,在Cycle 5,load指令的WB阶段将MEM/WB寄存器的值装载到寄存器R1中,而在Cycle 5,store指令的MEM要将寄存器R1的值放到存储器中去,R1的值还没有被load 指令更新。这个数据冒险的解决方法可以延时1个周期,也可以采用同样的Forwarding 方法,将MEM/WB寄存器直通到MEM输入端。
在这里插入图片描述
也不是每种数据冒险都能解决,同时又不引起stall,如下面这个例子:
在这里插入图片描述
sub 指令需要 R1,但是R1最早也要在Cycle 4才到MEM/WB寄存器中,即使将MEM/WB寄存器直通到EX的输入端,也需要延时一个Cycle才能使用。

控制冒险

从微观的角度,在流水线处理器中,指令是并行处理的,在当前指令正在执行时,后面的很多条指令已经完成了取指和译码等步骤。然而,在程序中会存在很多的跳转语句,如果程序的实际执行路径是要跳转到其他的地址去执行,那么流水线中已经做的这些取指和译码工作就白做了,这就是流水线的控制冒险。此时,处理器需要排空流水线,跳转到新的地址处重新进入流水线。由此可知,跳转对程序性能的损失是巨大的,流水线越深,损失越大。DSP流水线更深,我们以 DSP流水线为例说明这个问题的危害性。
在下面这个DSP流水线中,指令2在第8个Cycle执行时,发现要跳转到其他的地方,于是流水线后面的PG等工作就全白做了。
在这里插入图片描述
x86处理器使用硬件冲刷流水线来保证发生跳转时,流水线能正确执行,在DSP中,硬件不处理这些冒险,而是改由软件来处理。DSP通过增加NOP来排空流水线,在跳转语句后增加5个NOP操作来保证流水线正确。
在这里插入图片描述
跳转指令的执行阶段修改程序指针的地址,加了5个NOP后,MPY的流水线正好可以顺利执行。
在DSP中,编译器可以将指令乱序,用有效指令代替NOP指令,这样就避免了跳转带来的性能损失。
在x86 CPU 上,使用分支预测用来避免跳转带来的损失。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Strive_LiJiaLe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值