编译器完成的指令重新排序与cpu完成的指令之间有什么关系?

时间:2019-01-17 21:10:04

标签: cpu compiler-optimization instruction-reordering

好,因此出于性能原因,编译器可以自由地对代码片段进行重新排序。让我们假设一些代码片段在没有应用优化的情况下直接转换为机器代码,如下所示:

machine_instruction_1
machine_instruction_2
machine_instruction_3
machine_instruction_4
machine_instruction_5

但是一个聪明的编译器认为原始顺序效率很低,并对相同的代码重新排序,以使生成的机器指令的新顺序如下:

machine_instruction_5
machine_instruction_4
machine_instruction_3
machine_instruction_2
machine_instruction_1

到目前为止很好。

这是棘手的部分开始的地方。产生的机器指令将由一个cpu执行,只要保留了代码逻辑,该cpu可以根据性能原因以其认为合适的任何方式再次对其进行重新洗牌。由于我们正在处理指令“重排”的两个“层”:

  • 第一个,由于编译器的优化
  • 第二个,由于cpu顺序执行

什么使编译时指令重新排序完全相关? CPU所看到的只是一系列原始机器指令,没有迹象表明编译器会先进行任何优化。如果cpu引入了自己的“重排序层”,那么它怎么不会使编译器设置的指令顺序无效?基本上,是什么促使cpu尊重编译器优化?编译时重新排序和运行时重新排序如何“协作”,后者如何补充前者?

1 个答案:

答案 0 :(得分:2)

在考虑指令执行时,必须遵守的是程序语义。只要遵守此顺序,任何排序都是正确的。具体地,这由“依赖性”来描述,该“依赖性”指示某些指令相对于程序正确行为是否需要给定顺序。例如考虑以下程序

1 x <= y+3
2 z <= 2*x
3 w = 5*y
4 y = 2*a

指令1和2是从属的。如果修改了它们的相对顺序,则程序与程序员想要的不兼容,并且禁止重新排序。由于不同的原因,不能在1和3使用y之前不变地执行4。存在不同的依赖关系,包括在考虑控制流时。

编译器和硬件在尊重依赖的同时,尝试对程序进行重新排序以提高其效率。确实,他们的行为是互补的。

与处理器相比,编译器可以考虑进行更大的重组,并且可以使用更复杂的启发式方法来进行重组。编译器可以对程序有较大的了解,并可以对大部分代码进行重新排序。从理论上讲,如果不存在依赖冲突,并且距离编译器认为可以改善程序执行,那么距离为1000的一条指令可以被替换。它可以完全重组代码,展开循环等。相反,处理器具有相对有限的预取指令窗口,可以考虑对它们进行重新排序,并且任何重新排列都只能关注紧密的指令,并且基于可在一个周期内执行的简单方法。

但是处理器具有很大的优势。它可以进行动态重新排序并响应随机事件。在给定的时间,它会考虑可以执行哪些指令来满足相关性和数据可用性,并相应地对代码进行重新排序。这是基于动态依赖关系的,例如,如果前一个依赖指令的结果由于高速缓存未命中而无法使用,它将执行其他尊重依赖关系的指令。具有相同输入数据的同一程序的连续运行会导致不同的排序,具体取决于分支的错误预测,高速缓存未命中等。

因此,编译器和处理器之间没有竞争,但有效率的协作。