什么是循环反演技术?

我正在阅读一篇关于Java的即时编译器 (JIT)优化技术的文档。 其中之一是“循环倒置”。 文件说:

用一个do-while循环replace一个常规do-while循环。 do-while循环在if子句中设置。 这种替代导致两个较less的跳跃。

循环反转是如何工作的?它如何优化我们的代码path?

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

 while (condition) { ... } 

工作stream程:

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

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

工作stream程:

  1. 检查条件;
  2. 如果为false,跳转到循环之外;
  3. 运行一个迭代;
  4. 检查条件;
  5. 如果为true,则跳转到步骤3。

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

现代stream水线CPU体系结构的跳转可能非常昂贵:由于CPU在跳转之前完成了检查的执行,超出该跳转的指令已经在stream水线的中间。 如果分支预测失败,所有这些处理必须被丢弃。 进一步的执行被延迟,而pipe道被重新谴责。

解释提到的分支预测 :对于每一种条件跳转,CPU都有两条指令,每条指令都包含对结果的下注 。 例如,你可以在循环结尾放一条指令,说“ 如果不是零跳,下注不为零 ”,因为除了最后一个迭代外,必须进行跳转。 这样,CPU就开始用跳转目标之后的指令来抽取其stream水线,而不是跟随跳转指令本身。

重要的提示

请不要以此为例来说明如何在源代码级进行优化。 这是完全错误的,因为从你的问题中已经清楚了,从第一种forms到第二种forms的转变是JIT编纂者作为日常事务完全依靠自己的事情做的事情。

这可以优化总是至less执行一次的循环。

一个常规的while循环将总是跳回到起始处,最后一次跳到结尾。 一个简单的循环运行一次的例子:

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

另一方面, do-while循环会跳过第一个和最后一个跳转。 这里是一个等价的循环,上面那个,将运行没有跳转:

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

我们来看看他们:

while版本:

 void foo(int n) { while (n < 10) { use(n); ++n; } done(); } 
  1. 首先我们testingn并跳转到done(); 如果条件不正确。
  2. 然后我们使用并增加n
  3. 现在我们跳回到这个条件。
  4. 冲洗,重复。
  5. 当条件不成立时,我们跳到done()

do-while版本:

(请记住,我们实际上并没有在源代码中这样做(这会引入维护问题),编译器/ JIT为我们做。

 void foo(int n) { if (n < 10) { do { use(n); ++n; } while (n < 10); } done(); } 
  1. 首先我们testingn并跳转到done(); 如果条件不正确。
  2. 然后我们使用并增加n
  3. 现在我们testing这个条件,如果它是真的就跳回来。
  4. 冲洗,重复。
  5. 当条件不再是真的,我们stream(不跳) done()

例如,如果n开始是9 ,我们绝不会在do-while版本中跳跃,而在while版本中我们必须跳回到开头,做testing,然后跳回到最后。看到这是不正确的。

循环反转是一种性能优化技术,可以提高性能,因为处理器可以用较less的指令完成相同的结果。 这应该大大改善边界条件下的性能。

这个链接为循环反转提供了另一个例子。 在很less的体系结构中,递减和比较是作为一个单独的指令集来实现的,用一个递减和比较操作来将一个for循环转换成一个while是合理的。

维基百科有一个很好的例子,我在这里再次解释它。

  int i, a[100]; i = 0; while (i < 100) { a[i] = 0; i++; } 

将由编译器转换为

  int i, a[100]; i = 0; if (i < 100) { do { a[i] = 0; i++; } while (i < 100); } 

这如何转化为性能? 当i的值是99时,处理器不需要执行GOTO(在第一种情况下是必需的)。 这提高了性能。