请参见设置1的执行,阶段和性能(吞吐量),以及设置3的管道和停转类型。流水线处理器中的依赖关系
流水线处理器中可能主要存在三种类型的依赖关系。这些都是 :
1)结构依赖性
2)控制依赖
3)数据依赖
这些依赖性可能会在管道中引入停顿。
停顿:停顿是管道中没有新输入的周期。结构依赖性
这种依赖关系是由于管道中的资源冲突而产生的。资源冲突是指多个指令试图在同一周期中访问同一资源的情况。资源可以是寄存器,内存或ALU。
例子:
Instruction / Cycle | 1 | 2 | 3 | 4 | 5 |
---|---|---|---|---|---|
I1 | IF(Mem) | ID | EX | Mem | |
I2 | IF(Mem) | ID | EX | ||
I3 | IF(Mem) | ID | EX | ||
I4 | IF(Mem) | ID |
在上述情况下,在周期4中,指令I 1和I 4试图访问引入资源冲突的同一资源(内存)。
为避免此问题,我们必须保持指令等待,直到所需的资源(在本例中为内存)可用为止。这种等待将在管道中引入停顿,如下所示:
Cycle | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
I1 | IF(Mem) | ID | EX | Mem | WB | |||
I2 | IF(Mem) | ID | EX | Mem | WB | |||
I3 | IF(Mem) | ID | EX | Mem | WB | |||
I4 | – | – | – | IF(Mem) |
解决结构依赖性的方法
为了最大程度地减少管道中的结构依赖性停顿,我们使用一种称为重命名的硬件机制。
重命名:根据重命名,我们将存储器分为两个独立的模块,分别用于存储指令和数据,分别称为代码存储器(CM)和数据存储器(DM)。 CM将包含所有指令,而DM将包含指令所需的所有操作数。
Instruction/ Cycle | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|
I1 | IF(CM) | ID | EX | DM | WB | ||
I2 | IF(CM) | ID | EX | DM | WB | ||
I3 | IF(CM) | ID | EX | DM | WB | ||
I4 | IF(CM) | ID | EX | DM | |||
I5 | IF(CM) | ID | EX | ||||
I6 | IF(CM) | ID | |||||
I7 | IF(CM) |
控制依赖性(分支危险)
这种依赖关系发生在诸如BRANCH,CALL,JMP等控制指令的传输过程中。在许多指令体系结构上,当处理器需要将新指令插入流水线时,它们将不知道这些指令的目标地址。因此,不需要的指令被馈送到流水线。
考虑程序中的以下指令序列:
100:我1
101:我2 (JMP 250)
102:我3
。
。
250:BI 1
预期输出:I 1- > I 2- > BI 1
注意:通常,仅在ID阶段之后才知道JMP指令的目标地址。
Instruction/ Cycle | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
I1 | IF | ID | EX | MEM | WB | |
I2 | IF | ID (PC:250) | EX | Mem | WB | |
I3 | IF | ID | EX | Mem | ||
BI1 | IF | ID | EX |
输出序列:I 1- > I 2- > I 3- > BI 1
因此,输出顺序不等于预期的输出,这意味着未正确实现管道。
要解决上述问题,我们需要停止指令提取,直到获得分支指令的目标地址为止。这可以通过引入延迟槽直到我们获得目标地址来实现。
Instruction/ Cycle | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|
I1 | IF | ID | EX | MEM | WB | |
I2 | IF | ID (PC:250) | EX | Mem | WB | |
Delay | – | – | – | – | – | – |
BI1 | IF | ID | EX |
输出序列:I 1- > I 2- >延迟(失速)-> BI 1
由于延迟时隙不执行任何操作,因此此输出序列等于预期的输出序列。但是此插槽会在管道中造成停顿。
控制依赖性的解决方案分支预测是一种可以消除由于控制依赖性导致的停顿的方法。在第一阶段中,将进行关于将采取哪个分支的预测。对于分支预测,分支惩罚为零。
分支惩罚:在分支操作中在流水线处理器中引入的停顿数称为分支惩罚。
注意:正如我们看到的那样,目标地址在ID阶段之后可用,因此在管道中引入的停顿数为1。假设在ALU阶段之后将存在分支目标地址,那么将有2个停顿。通常,如果在第k个阶段之后存在目标地址,则流水线中将有(k – 1)个停顿。
由于分支指令而在管道中引入的停顿总数=分支频率*分支罚款
数据依赖性(数据危险)
让我们考虑一条ADD指令S,这样
S:添加R1,R2,R3
S读取的地址= I(S)= {R2,R3}
S = O(S)= {R1}写入的地址
现在,我们说指令S2取决于指令S1,当
该条件称为伯恩斯坦条件。
存在三种情况:
- 流(数据)相关性:O(S1)∩I(S2),S1→S2,S1在被S2读取后写入
- 反依赖性:I(S1)∩O(S2),S1→S2,S1在S2覆盖之前先读取内容
- 输出依存关系:O(S1)∩O(S2),S1→S2,并且都写入相同的存储位置。
示例:假设有两条指令I1和I2:
I1:添加R1,R2,R3
I2:SUB R4,R1,R2
当上述指令在流水线处理器中执行时,将发生数据依赖条件,这意味着I 2会在I 1写入数据之前尝试读取数据,因此,I 2错误地从I 1获取了旧值。
Instruction / Cycle | 1 | 2 | 3 | 4 |
---|---|---|---|---|
I1 | IF | ID | EX | DM |
I2 | IF | ID(Old value) | EX |
为了最大程度地减少流水线中的数据依赖性停顿,使用了操作数转发。
操作数转发:在操作数转发中,我们使用各阶段之间存在的接口寄存器来保存中间输出,以便从属指令可以直接从接口寄存器访问新值。
考虑相同的示例:
I1:添加R1,R2,R3
I2:SUB R4,R1,R2
Instruction / Cycle | 1 | 2 | 3 | 4 |
---|---|---|---|---|
I1 | IF | ID | EX | DM |
I2 | IF | ID | EX |
数据危害
当表现出数据依赖性的指令在流水线的不同阶段修改数据时,就会发生数据危害。危险会导致管道延迟。主要有三种类型的数据危害:
1)RAW(写后读)[流/真数据相关性]
2)WAR(读取后写入)[反数据依赖性]
3)WAW(写后写)[输出数据相关性]
假设有两个指令I和J,以使J跟随I。然后,
- 当指令J在我写入数据之前尝试读取数据时,会发生RAW危险。
例如:
I:R2 <-R1 + R3
J:R4 <-R2 + R3 - 当指令J在我读取数据之前尝试写入数据时,会发生WAR危险。
例如:
I:R2 <-R1 + R3
J:R3 <-R4 + R5 - 当指令J在我写输出之前尝试写输出时,会发生WAW危险。
例如:
I:R2 <-R1 + R3
J:R2 <-R4 + R5
在乱序执行指令期间会发生WAR和WAW危险。
资料来源:goo.gl/J9KVNt
https://zh.wikipedia.org/wiki/危险区(计算机架构)
https://zh.wikipedia.org/wiki/数据依赖本文由Saurabh Sharma提供。