什么是延迟分支?
当分支由流水线简单处理时,在每个执行的分支之后,至少有一个周期未被利用。这是由于流水线的类似装配线的惰性。分支之后的指令槽称为分支延迟槽。
延迟槽也可能出现在加载指令之后;这些被定义为加载延迟槽。在传统执行期间,分支延迟槽会被浪费。但是,当使用延迟分支时,这些槽至少可以部分使用。
延迟分支原理
ti | ti+1 | ti+2 | ti+3 | ti+4 | |||
---|---|---|---|---|---|---|---|
B | b | F | D | E | WB | ||
A | add | F | D | E | WB | ||
C | sub | F | D | ||||
BTA | F |
在图中,它可以将我们程序段中最初位于分支之前的 add 指令转移到分支延迟槽中。使用延迟分支,处理器首先执行 add 指令,但分支只会稍后生效。因此,在此示例中,延迟分支保持初始执行顺序 -
add r1, r2, r3; b anywhere; anywhere: sub
它定义了一个无条件分支。条件分支在简单的流水线执行期间会导致相同或更高的延迟。这是因为需要额外检查特定条件的操作。
因此,未执行分支的延迟槽中的指令将始终被执行。分支到目标指令 (sub) 的执行延迟了一个流水线周期。此周期用于执行延迟槽中的指令 (add)。因此,延迟分支导致以下执行顺序 -
a, add b, b c, sub
延迟分支最早在 1952 年的 MANIAC I 中引入,后来在微程序设计中得到了广泛使用(Patterson 和 Sequin,1981)。在 20 世纪 80 年代初,这种方案在 RISC-I 中“重新发明”(Patterson 和 Sequin 1981),并随后用于当时出现的几种 RISC 架构,例如 MIPS(1982p)、RISC-II(1983)、MIPS-R-line(从 1987 年开始)和 AMD 29000(1987)。
延迟分支的缺点
延迟分支有各种缺点,如下所示 -
延迟分支需要重新定义体系结构。
由于需要插入 NOP,延迟分支会导致代码略微膨胀。例如,它必须插入 100∗fb∗(1−ff)=100∗ 0.2∗(1−0.6)=8 个 NOP 到每 100 条指令中,因此代码长度将比没有延迟分支的情况长 8%。
中断处理变得更加困难。这是因为由延迟槽中的指令引起的中断请求必须与来自“正常”指令的中断请求以不同的方式处理。当延迟槽指令引发中断时,前面的指令(即条件分支)已经获取但尚未处理。这种情况与传统指令处理中发生的情况截然不同,在传统指令处理中,导致中断的指令之前的所有指令都已完成。
需要额外的硬件来实现延迟分支。