考慮下面的C程序:靜態分支預測/ GCC優化
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塊優化參數(包含在-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
主要是從跳轉等於到跳轉不等於。我知道,直到Pentium 4,靜態分支預測在條件前向分支上被認爲沒有被處理器採用(似乎靜態預測在更進一步的英特爾處理器上變得隨機),因此我想這種優化正在處理這個問題。
假設和指的的JNE優化的版本,這將意味着其他塊實際上是在考慮到比在程序流程如果塊更容易執行。
但是究竟是什麼意思?由於編譯器沒有對函數中的值進行假設,所以這種可能性僅依賴於程序員的着作(其實可能已經使用了if (!a)
而不是if (a)
和反轉函數調用)。
這是否意味着它應該被看作是一個很好的做法,對待如果條件塊作爲特殊情況下(而不是正常的執行流程)?
那就是:的
if (!cond) {
// exceptional code
}
else {
// normal continuation
}
代替:
if (cond) {
// normal continuation
}
else {
// exceptional code
}
(當然,人們可以更喜歡使用相關的功能塊內return語句來限制壓痕尺寸)。
是的......但不同的回報點也可以在「je」版本上實現。如果/ else塊重新排序_consciously_:在最初的程序中,將if(a)'改爲'if(!a)'完全相反:從'jne'(非優化)版本改爲'je'(分支訂單優化)版本。我不能相信GCC做這個改變只是爲了取笑我! :) – lledr
我認爲在任何情況下__builtin_expect應該幫助這個;-)。 http://blog.man7.org/2012/10/how-much-do-builtinexpect-likely-and.html –