7

次の C プログラムを考えてみましょう。

void bar();
void baz();

void foo( int a ) {
    if ( a ) {
        bar();
    }
    else {
        baz();
    }
}

私の x86-64 ベースのコンピューターでは、-O1 最適化レベルで GCC によって生成された命令は次のようになります。

 0: sub    $0x8,%rsp
 4: test   %edi,%edi
 6: je     14 <foo+0x14>
 8: mov    $0x0,%eax
 d: callq  12 <foo+0x12> # relocation to bar
12: jmp    1e <foo+0x1e>
14: mov    $0x0,%eax
19: callq  1e <foo+0x1e> # relocation to baz
1e: add    $0x8,%rsp
22: retq

-freorder-blocks 最適化パラメーター (-O2 に含まれる) を追加すると、コードは次のようになります。

 0: sub    $0x8,%rsp
 4: test   %edi,%edi
 6: jne    17 <foo+0x17>
 8: mov    $0x0,%eax
 d: callq  12 <foo+0x12> # relocation to baz
12: add    $0x8,%rsp
16: retq   
17: mov    $0x0,%eax
1c: callq  21 <foo+0x21> # relocation to bar
21: add    $0x8,%rsp
25: retq

主にjump equalsからjump not equalsへの変更です。Pentium 4 までは、条件付き前方分岐での静的分岐予測がプロセッサによって行われないと見なされていたことを知っています (それ以降の Intel プロセッサでは静的予測がランダムになったようです)。したがって、この最適化はこれに対処していると思います。

それを仮定し、jne最適化バージョンを参照すると、elseブロックは実際には、プログラム フローのifブロックよりも実行される可能性が高いと見なされることを意味します。

しかし、それは正確にはどういう意味ですか?コンパイラによるfoo関数のa値の仮定がないため、そのような確率はプログラマーの記述のみに依存します (実際には関数呼び出しの代わりに使用したり、関数呼び出しを反転したりできたはずです)。if ( !a )if ( a )

if条件付きブロックを例外的なケース (通常の実行フローではなく) として扱うことを良い習慣と見なす必要があるということですか?

あれは:

if ( !cond ) {
    // exceptional code
}
else {
    // normal continuation
}

それ以外の:

if ( cond ) {
    // normal continuation
}
else {
    // exceptional code
}

(もちろん、インデント サイズを制限するために、関連するブロック内で return ステートメントを使用することをお勧めします)。

4

1 に答える 1

4

私はかつて、ARM(7,9) でかなりの量のパフォーマンス最適化アクションを実行しました。それは単純な C であり、十分に愚かなコンパイラ (SDT AFAIR) でした。いくつかの CPU リソースを節約する方法の 1 つは、if分岐を分析して条件を書き直すifことでした。これにより、通常のフローが線形の命令シーケンスを壊さなくなりました。これは、CPU 予測ブロックの使用効率とコード セグメント メモリ キャッシュの使用効率の両方にプラスの効果がありました。

ここでは、非常に近い最適化が見られると思います。最初のコード フラグメントでは、両方の分岐で通常のシーケンスが壊れています ( 61 つの分岐と12別の分岐のラベルの行)。2番目のフラグメントでは、1つの分岐命令が順序付けられretq、他の分岐シーケンスには単一のジャンプがあります(最初のフラグメントよりも悪くありません)。retq2指示に注意してください。

私が見ることができるように、これはブロックの並べ替えの問題ではなくjejneむしろブロックの並べ替えの問題であるため、分岐は線形命令シーケンスであり、jump予測ブロックの電力が完全に節約されることなく入力されます。

「GCC が別のブランチよりも 1 つのブランチを好む理由」について... ドキュメントで、これは静的ブランチ予測の結果である可能性があることがわかります (翻訳単位内の呼び出しに基づいていますか?)。__builtin_expectとにかく、より詳細な答えを得るために遊んでみることをお勧めします。

于 2013-09-01T16:58:43.550 に答える