ループ反転技術とは何ですか?


89

Javaのジャストインタイムコンパイラー(JIT)最適化手法について説明しているドキュメントを調べていました。それらの1つは「ループ反転」でした。そして文書は言う:

通常のwhileループをループに置き換えdo-whileます。そして、 do-whileループはif節内で設定されます。この置換により、ジャンプが2つ少なくなります。

ループ反転はどのように機能し、コードパスを最適化しますか?

注意: Javaコードの例と、JITがそれをネイティブコードに最適化する方法と、最新のプロセッサで最適化される理由を誰かが説明できればすばらしいと思います。


2
これは、ソースコードに対して行うことではありません。ネイティブコードレベルで発生します。
Marko Topolnik 2013

2
@MarkoTopolnik私は知っています。しかし、JITがこれをネイティブコードレベルでどのように実行するかを知りたいです。ありがとう。
試行

1
おお、クールなウィキペディアのページに、たくさんの例en.wikipedia.org/wiki/Loop_inversionがあります。Cの例はJavaでも同じです。
Benjamin Gruenbaum

いつか前にこの問題で短い研究を行ってきたので、私の質問の一つに触発され、多分結果はあなたに役に立つでしょう:stackoverflow.com/questions/16205843/java-loop-efficiency/...
アダム・シエミオン

これは、通常、ループ条件が最後に置かれるのと同じことです(実行されるジャンプが少なくなるかどうかに関係なく)、ジャンプ命令が少なくなる(反復ごとに1対2)ためですか?
Extremeaxe5

回答:


108
while (condition) { 
  ... 
}

ワークフロー:

  1. チェック状態;
  2. falseの場合、ループの外側にジャンプします。
  3. 1回の反復を実行します。
  4. 先頭にジャンプします。

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

ワークフロー:

  1. チェック状態;
  2. falseの場合、ループを超えてジャンプします。
  3. 1回の反復を実行します。
  4. チェック状態;
  5. trueの場合、手順3に進みます。

これら2つを比較すると、ループ全体で1つのステップしかなく、ジャンプの数が反復数より1つ少ない場合、後者はジャンプをまったく行わない可能性があることが簡単にわかります。前者は、条件をチェックするためにジャンプして戻る必要があり、条件が偽の場合にループからジャンプするだけです。

最新のパイプライン化されたCPUアーキテクチャでのジャンプは非常に高価になる可能性があります。CPUはジャンプの前にチェックの実行を終了しているため、そのジャンプを超える命令はすでにパイプラインの途中にあります。分岐予測が失敗した場合、この処理はすべて破棄する必要があります。パイプラインが再準備されている間、それ以降の実行は遅延されます。

上記の分岐予測の説明:条件付きジャンプの種類ごとに、CPUには2つの命令があり、それぞれに結果への賭けが含まれています。たとえば、ジャンプは最後のものを除くすべての反復で行われる必要があるため、ループの最後に「ゼロでない場合はジャンプ、ゼロでない場合は賭ける」という命令を入れます。これにより、CPUは、ジャンプ命令自体に続く命令ではなく、ジャンプターゲットに続く命令でパイプラインのポンプを開始します。

重要な注意点

ソースコードレベルで最適化する方法の例としてこれを使用しないでください。最初の形式から2番目の形式への変換は、JITコンパイラーがルーチンの問題として完全に独自に行うものであるため、それは完全に見当違いです。


51
最後にそのメモは、非常に、非常に重要なことです。
TJクラウダー

2
@AdamSiemion:指定されたdo-whileソースコード用に生成されたバイトコードは、実際には記述しないため、無関係です。whileループを記述し、必要に応じて、コンパイラーとJITが(ループ反転を介して)ループを改善するように共謀させます。
TJクラウダー

1
上記の@TJCrowder +1とAdamへの注意:JITコンパイラーの最適化について考えるときは、バイトコードを考慮しないでください。バイトコードは、実行されている実際のJITコンパイルコードよりもJavaソースコードにはるかに近いです。実際、現代の言語の傾向は、言語仕様の一部としてバイトコードをまったく持たないことです。
Marko Topolnik 2013

1
重要な注意がもう少し説明された余分な有益だったでしょう。なぜそれが完全に見当違いであるのでしょうか?
arsaKasra

2
@arsaKasra一般的に読みやすさと安定性がソースコードの最適化よりも優先されるため、見当違いです。特に、JITがあなたのためにこれを行うという啓示があれば、自分で(非常にミクロな)最適化を試みるべきではありません。
Radiodef 2013

24

これにより、常に少なくとも1回は実行されるループを最適化できます。

通常のwhileループは、常に少なくとも1回は最初に戻り、最後に1回は最後にジャンプします。1回実行する単純なループの例:

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

do-while一方、ループは、最初と最後のジャンプをスキップします。以下は、ジャンプなしで実行される上記のループと同等のループです。

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

正解です。最初に、コード例の追加を検討してください。何かしたいboolean b = true; while(b){ b = maybeTrue();}ことboolean b;do{ b = maybeTrue();}while(b);で十分です。
Benjamin Gruenbaum

心配ない。fwiw、答えの最初の行を無効にします。:-)
TJクラウダー

@TJさて、それでもまだ入っていないループは最適化されません。どちらの場合も1つのジャンプがあります。
ケッピル2013

ああ、そうです。申し訳ありませんが、少なくとも1回はループしなかったループに適用できなかった(というよりは役に立たなかった)という意味で読んでいました。あなたと今。:-)
TJクロウダー

@Keppilおそらく、反復Xが多数ある場合、X間のジャンプを1回だけ保存することを明示する必要があります。
Manuel Selva 2016年

3

それらを見ていきましょう:

whileバージョン:

void foo(int n) {
    while (n < 10) {
       use(n);
       ++n;
    }
    done();
}
  1. 最初に、条件が真でないかどうかをテストnしてジャンプし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. 最初に、条件が真でないかどうかをテストnしてジャンプしdone();ます。
  2. 次に、を使用して増分しますn
  3. 次に、条件をテストし、それが真の場合は戻ります。
  4. すすぎ、繰り返します。
  5. 条件がtrueでなくなったとき、(ジャンプではなく)にフローしdone()ます。

したがって、たとえば、がでn始まる場合9do-whileバージョンではまったくジャンプしませんが、バージョンでwhileは最初に戻ってテストを実行し、それが真でないとわかったときに最後に戻る必要があります。 。


3

ループ反転は、プロセッサーがより少ない命令で同じ結果を達成できるため、パフォーマンスを改善するパフォーマンス最適化手法です。これにより、境界条件でのパフォーマンスが向上します。

このリンクは、ループ反転の別の例を提供します。デクリメントと比較が単一の命令セットとして実装されているいくつかのアーキテクチャでは、デクリメントと比較の操作で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を実行する必要はありません(これは最初のケースで必要です)。これにより、パフォーマンスが向上します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.