结构冲突
由于硬件资源有限导致的,多个指令执行阶段对同一硬件提出访问请求,导致的冲突。
插入暂停周期
比如在运行时同时有两个指令对资源提出访问请求,一个指令在取指阶段提出取指令的请求访问内存,一个指令在运算阶段提出对数据的访存,这时会产生访存冲突,这时可以暂停一个周期,等待上一条指令的访存结束,之后进行下一条指令的执行,进行取指操作,这样去除了结构相关,但是对应暂停的那一周期内无指令完成执行。
设置备用硬件资源
同样,对于上面情况中,有同时对内存的访问来获取数据和指令,我们可以将数据与指令进行分开存储,设置相互独立的指令存储器与数据存储器。推广一下就是对于其他的硬件资源都可以通过设置多个备用的硬件资源来防止结构冲突,但是这样会导致成本的提升。
数据冲突
暂停等待
如果出现指令的运行依赖于上一条指令的结果的情况,就暂停指令的执行,直到上一条指令的写回周期结束,再继续运行当前指令的读数据操作,也就是第二阶段,这一阶段一共浪费了三个时间周期。
这样的效率就大大降低了。
定向技术(数据旁路)
在某条指令产生计算结果之前,其他指令并不真正立即需要该计算结果,如果能够将该计算结果从其产生的地方直接送到其他指令需要它的地方,那么就可以避免停顿。如图:
在具体运算的时候,操作的数据来源不是全来自于在第二阶段取出的数据,有一部分的数据来源于其他流水段直接传过来的,在实际的流水线中,每一个流水段之间都有寄存器保存上一段运行产生的数据,即图中的灰色的矩形框。
分时访问
如上图所示,在对reg的访问中有的是前半段为虚线有的是后半段为虚线,由于对寄存器的访问时间很短,所以可以分为前半周期和后半周期分开访问。
互锁机制
定向技术并不能解决所有的数据冲突,有的时候,上一条指令还没有执行完,下一条指令就需要其结果来进行数据运算,这时定向无法解决,数据根本就没有产生,定向也无法获取,这时就需要流水线暂停之后指令的运行来等待上一条数据的运行结果。
互锁机制是一个功能部件,首先这一部件检测数据冲突,发现后将流水线暂停,等待数据的产生,直至冲突消失,停顿从等待相关数据的指令开始,到相应指令产生数据为止。
编译器指令调度
让编译器重新组织指令顺序来消除冲突,这种技术称为指令调度或流水线调度,具体的实现是将原先放置气泡的位置运行没有冲突的其他指令,等到等待的周期结束后再将之前有冲突的指令运行。即充分的利用原先的停顿周期。
控制冲突
控制冲突的关键是在流水线的并行处理中,我们无法在一个流水阶段中迅速的得出分支指令的跳转情况,也就是无法确定并行进行的下一条指令究竟是哪一条。
排空流水线
与之前的暂停类似,这里的相关在于控制相关,所以我们需要等到分支指令计算出具体的跳转地址后在进行下一条指令的运行。
由分支指令引起的延迟称为分支延迟。
减少分支延迟:尽早计算出分支目标地址。流水线中尽早判断出分支转移是否成功。
编译器处理
对分支的处理方法在程序的执行过程中始终是不变的,是静态的。要么总是预测分支成功,要么总是预测分支失败。
前提保证
分支结果出来之前不会改变处理机的状态,以便一旦猜错时,处理机能够回退到原先的状态。
预测分支失败
允许分支指令后的指令继续在流水线中流动,若确定分支失败,将分支指令看作是一条普通指令,流水线正常流动。
预测分支成功
若确定分支成功,流水线就把在分支指令之后取出的所有指令转化为空操作,并按分支目地重新取指令执行。
延迟分支
从逻辑上“延长”分支指令的执行时间。把延迟分支看成是由原来的分支指令和若干个延迟槽构成,不管分支是否成功,都要按顺序执行延迟槽中的指令。
分支延迟槽中的指令“掩盖”了流水线原来必须插入的暂停周期。
指令的调度
在延迟槽中放入有用的指令。由编译器完成。能否带来好处取决于编译器能否把有用
的指令调度到延迟槽中。
调度方法:从前调度,从目标处调度,从失败处调度。
分支取消
当分支的实际执行方向和事先所预测的一样时,执行分支延迟槽中的指令,否则就将分支延迟槽中的指令转化成一个空操作。
预测分支成功的情况下,分支取消机制的执行情况,保证运行的正确性。