1. ホーム
  2. c

[解決済み] コンパイラはdo-whileループと他のタイプのループの間でより良いコードを生成するか?

2023-04-02 13:13:29

質問

の中にコメントがあります。 zlib 圧縮ライブラリ (Chromium プロジェクトや他の多くのプロジェクトで使用されている) にコメントがあり、C 言語の do-while ループはほとんどのコンパイラで "より良いコードを生成することを暗に示しています。以下は、そのスニペットが表示されるコードです。

do {
} while (*(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         *(ushf*)(scan+=2) == *(ushf*)(match+=2) &&
         scan < strend);
/* The funny "do {}" generates better code on most compilers */

https://code.google.com/p/chromium/codesearch#chromium/src/third_party/zlib/deflate.c&l=1225

ほとんどの(あるいはすべての)コンパイラがより良い(例えばより効率的な)コードを生成するという証拠はあるのでしょうか?

更新してください。 マーク・アドラー は、原著者の一人である は、その背景を少し述べています。 をコメントで述べています。

どのように解決するのですか?

まず最初に

A do-while ループは while -ループまたは for -ループになります。

  • while そして for ループはループ本体を全く実行しないかもしれません。
  • A do-while ループは常にループ本体を少なくとも一度は実行します。つまり、初期状態のチェックをスキップします。

というわけで、論理的な違いです。とはいえ、誰もがこれを厳密に守っているわけではありません。よくあるのが while または for のループは、必ず一度はループすることが保証されている場合であっても、使用されることがあります。(特に foreach ループがある言語では特に)。

というわけで、リンゴとオレンジを比較しないように、ループは必ず一度は実行されるものと仮定して話を進めることにします。さらに、ここでは for ループは本質的に while ループであり、ループカウンターのための構文が少しあるためです。

というわけで、質問に答えます。

もし while ループが少なくとも一度はループすることが保証されているのであれば、ループの中に do-while ループを使用することでパフォーマンス上の利点はありますか?


A do-while は最初の条件チェックをスキップします。そのため、分岐が1つ減り、評価する条件も1つ減ります。

条件のチェックにコストがかかり、少なくとも1回はループすることが保証されているのであれば do-while ループの方が速いかもしれません。

そして、これはせいぜいマイクロ最適化と考えられていますが、コンパイラが常に行うことができないものです。特に、コンパイラがループが常に少なくとも一度は入るということを証明できない場合です。


言い換えれば、while-loopです。

while (condition){
    body
}

は事実上これと同じです。

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

必ず一度はループすることが分かっていれば、if文は余計なお世話です。


同様にアセンブリレベルでも、異なるループがどのようにコンパイルされるかは大体このような感じです。

do-whileループ。

start:
    body
    test
    conditional jump to start

while-loopです。

    test
    conditional jump to end
start:
    body
    test
    conditional jump to start
end:

条件が重複していることに注意してください。別のアプローチとして

    unconditional jump to end
start:
    body
end:
    test
    conditional jump to start

... これは、重複するコードと追加のジャンプをトレードオフするものです。

いずれにせよ、これは通常の do-while ループよりも悪いことに変わりはありません。

とはいえ、コンパイラは好きなようにできる。そして、ループが常に一度だけ入ることを証明できれば、それはあなたのために仕事をしたことになります。


しかし、質問の特定の例では、ループのボディが空であるため、少し奇妙なことがあります。ボディがないので whiledo-while .

参考までに、Visual Studio 2012でテストしてみました。

  • ボディが空の場合、実際に同じコードを生成して whiledo-while . だからその部分は、コンパイラがそれほど優れていなかった昔の名残りだろう。

  • しかし、空でないボディで、VS2012 は条件コードの重複を避けることに成功しますが、それでも余分な条件ジャンプを生成します。

ですから、質問の例では、なぜ do-while ループが一般的なケースでより速くなり得る理由を強調している一方で、その例自体は最新のコンパイラでは何の利点も与えていないように見えるのは皮肉なことです。

このコメントの古さを考慮すると、なぜそれが問題になるのか推測することができます。当時のコンパイラーは、ボディが空であることを認識する能力がなかった可能性が非常に高いです。(または、認識できたとしても、その情報を使用しなかった)。