次の 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 ステートメントを使用することをお勧めします)。