抽象的すぎる質問で申し訳ありませんが、私にとっては非常に実用的です + 一部の専門家は同様の経験があり、説明できる可能性があります。
約 10000 行の大きなコードがあります。
ある場所に置くと気づきます
if ( expression ) continue;
ここで、expression は常に false (コードと cout のロジックで二重にチェックされます) ですが、不明なパラメーターに依存します (そのため、コンパイラーはコンパイル中にこの行を単純に取り除くことはできません)。プログラムの速度は25% 向上します (計算の結果同じだ)。ループ自体の速度を測定すると、速度アップ係数は 3 より大きくなります。
なぜこれが起こるのか、また、そのようなトリックなしでこのスピードアップの可能性を利用する方法は何ですか?
PS私はgcc 4.7.3、-O3最適化を使用しています。
より詳しい情報:
2 つの異なる表現を試しましたが、どちらも機能します。
行を次のように変更すると:
if ( expression ) { cout << " HELLO " << endl; continue; };
スピードアップはなくなりました。
行を次のように変更すると:
expression;
スピードアップはなくなりました。
行を囲むコードは次のようになります。
for ( int i = a; ; ) { do { i += d; if ( d*i > d*ilast ) break; // small amount of calculations, and conditional calls of continue; } while ( expression0 ); if ( d*i > dir*ilast ) break; if ( expression ) continue; // very big amount calculations, and conditional calls of continue; }
for ループが奇妙に見えます。これは、このボトルネックをキャッチするためにループを修正したためです。当初、expression は expression0 に等しく、do-loop の代わりにこれだけを継続しました。
分岐予測を理解するために __builtin_expect を使ってみました。と
// the expression (= false) is supposed to be true by branch prediction. if ( __builtin_expect( !!(expression), 1) ) continue;
スピードアップは 25% です。
// the expression (= false) is supposed to be false by branch prediction. if ( __builtin_expect( !!(expression), 0) ) continue;
スピードアップはなくなりました。
-O3 の代わりに -O2 を使用すると、効果がなくなります。このコードは、False 条件を使用した高速 O3 バージョンよりもわずかに (~3%) 遅くなります。
「-O2 -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -ftree-vectorize」も同様です。もう 1 つのオプション: "-O2 -finline-functions -funswitch-loops -fpredictive-commoning -fgcse-after-reload -ftree-vectorize -fipa-cp-clone" を使用すると、効果が増幅されます。「ライン」を使用しても速度は同じですが、「ライン」を使用しない場合、コードは 75% 遅くなります。
その理由は、次の条件演算子にあります。したがって、コードは次のようになります。
for ( int i = a; ; ) { // small amount of calculations, and conditional calls of continue; if ( expression ) continue; // calculations1 if ( expression2 ) { // calculations2 } // very big amount calculations, and conditional calls of continue; }
ほとんどの場合、expression2 の値は false です。だから私はそれを次のように変更しました:
for ( int i = a; ; ) { // small amount of calculations, and conditional calls of continue; // if ( expression ) continue; // don't need this anymore // calculations1 if ( __builtin_expect( !!(expression2), 0 ) ) { // suppose expression2 == false // calculations2 } // very big amount calculations, and conditional calls of continue; }
そして、望ましい25%のスピードアップを手に入れました。もう少しでも。そして、行動はクリティカルラインに依存しなくなりました。
推測なしでこの動作を説明できる資料を誰かが知っている場合、私は彼らの答えを読んで受け入れることを非常に嬉しく思います.