20. 依赖环(PPro,PII和PIII)

优质
小牛编辑
128浏览
2023-12-01

一组指令,其中每一条指令依赖于前一条指令的结果,称这组指令是一个依赖环。

长的依赖环应该尽可能避免,因为它阻止了乱序执行和并行执行。

比如:

MOV EAX, [MEM1]

ADD EAX,[MEM2]

ADD EAX, [MEM3]

ADD EAX, [MEM4]

MOV [MEM5],EAX

在这个例子中,每条ADD指令产生2条微码,一条从内存中读(端口2),一条做加法(端口0或1)。

读内存的微码可以乱序执行,但各条加法微码都必须等待前面的加法微码完成。 这个依赖环执行的时间并不长,因为每个加法只需要一个时钟。

但如果你有诸如乘法这样的慢指令,甚至更坏的除法形成依赖环,那么明显你应该做一些事情来打破依赖环。 可以用多个累加器实现这个目的:

MOV EAX, [MEM1] ; 开始第一个环

MOV EBX, [MEM2] ; 用另一个累加器开始第二个环

IMUL EAX, [MEM3]

IMUL EBX,[MEM4]

IMUL EAX, EBX ; 最后结合两个环

MOV [MEM5], EAX

这里,第二个乘法指令可以在第一个结束之前开始。

因为乘法指令花费4个周期且是完全流水化的,所以你最多可设4个累加器。

除法是非流水化的,因此你不能为除法依赖环做类似的事情。当然,你可以先将所有的除数相乘,最后再做一个除法。

浮点指令的延迟比整型指令大,因此你应该明确地打破长的浮点依赖环:

FLD [MEM1] ; 开始第一个环

FLD [MEM2] ; 用另一个累加器开始第二个环

FADD [MEM3]

FXCH

FADD [MEM4]

FXCH

FADD [MEM5]

FADD ; 最后把环结合

FSTP [MEM6]

你需要很多FXCH指令,不用担心,它们很“便宜”。

尽管在RAT,ROB和引退站中FXCH指令也被计为1条微码,但在RAT中FXCH指令通过寄存器重命名被“化解”,从而不会给执行端口造成任何负载。

如果依赖环很长,你需要设3个累加器:

FLD [MEM1] ; 开始第一个环

FLD [MEM2] ; 开始第二个环

FLD [MEM3] ; 开始第三个环

FADD [MEM4] ; 第三个环

FXCH ST(1) FADD [MEM5] ; 第二个环

FXCH ST(2) FADD [MEM6] ; 第一个环

FXCH ST(1) FADD [MEM7] ; 第三个环

FXCH ST(2) FADD [MEM8] ; 第二个环

FXCH ST(1) FADD     ; 结合1、3环

FADD     ; 与第2个环结合

FSTP [MEM9]

不要把中间结果存入内存后马上读出:

MOV [TEMP], EAX

MOV EBX, [TEMP]

试图在前面的写内存操作完成之前从相同地址读会带来惩罚。 就像上面的例子那样。可以把最后一条指令改成MOV
EBX,EAX或在两条指令之间插入一些其它指令。

有一种情形使你不可避免地要把中间结果存入内存,即从一个整型寄存器传输数据到一个浮点寄存器,反之亦然。 比如:

MOV EAX, [MEM1]

ADD EAX, [MEM2]

MOV [TEMP],EAX

FILD [TEMP]

如果在TEMP的写操作和TEMP的读操作之间你没有其它指令可插入,那么你可以考虑用浮点寄存器取代EAX:

FILD [MEM1]

FIADD [MEM2]

连续的跳转、调用和返回也可以看作是依赖环。 对于这些指令,吞吐量是每2个时钟周期1个转移指令。

因此推荐你在这些转移指令中间给处理器一些其它的事情做。