什么是环路反转技术?

2022-08-31 11:30:53

我正在浏览一个文档,其中讨论了Java的即时编译器(JIT)优化技术。其中之一是“循环反转”。该文件说:

将常规循环替换为循环。循环是在子句中设置的。这种替换导致跳转次数减少两次。whiledo-whiledo-whileif

循环反转如何工作,如何优化我们的代码路径?

注意:如果有人能用Java代码的例子来解释,以及JIT如何将其优化为本机代码,以及为什么它在现代处理器中是最佳的,那就太好了。


答案 1
while (condition) { 
  ... 
}

工作流程:

  1. 检查条件;
  2. 如果为假,请跳到循环外;
  3. 运行一次迭代;
  4. 跳转到顶部。

if (condition) do {
  ...
} while (condition);

工作流程:

  1. 检查条件;
  2. 如果为假,请跳到循环之外;
  3. 运行一次迭代;
  4. 检查条件;
  5. 如果为 true,请跳至步骤 3。

比较这两者,你可以很容易地看到,后者可能根本不做任何跳跃,只要循环中只有一个步骤,并且通常跳跃的次数将比迭代次数少一个。前者将不得不跳回去检查条件,只有当条件为假时才能跳出循环。

在现代流水线 CPU 架构上跳转可能非常昂贵:由于 CPU 在跳转之前完成了检查的执行,因此跳转之外的指令已经在流水线的中间。如果分支预测失败,则必须放弃所有这些处理。在重新请求管道时,进一步执行将延迟。

解释上述分支预测:对于每种条件跳转,CPU都有条指令,每条指令包括对结果的押注。例如,您可以在循环结束时放置一条指令,说“如果不是零则跳转,则押注不是零”,因为除了最后一次迭代之外,必须在所有迭代中进行跳转。这样,CPU 开始使用跳转目标后面的指令泵送其管道,而不是跳转指令本身后面的指令。

重要提示

请不要将此作为如何在源代码级别进行优化的示例。这将是完全错误的,因为从你的问题中已经很清楚,从第一种形式到第二种形式的转换是JIT编译器作为例行公事做的事情,完全是它自己完成的。


答案 2

这可以优化始终至少执行一次的循环。

然后,常规循环将始终至少跳回到开头一次,并在结束时跳到末尾一次。运行一次的简单循环的示例:while

int i = 0;
while (i++ < 1) {
    //do something
}  

另一方面,循环将跳过第一个和最后一个跳转。下面是与上面的循环等效的循环,它将在没有跳转的情况下运行:do-while

int i = 0;
if (i++ < 1) {
    do {
        //do something
    } while (i++ < 1); 
}

推荐