2013-09-01 42 views
7

考慮下面的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語句來限制壓痕尺寸)。

回答

4

我曾經在ARM(7,9)上做過大量的性能優化操作。這是簡單的C,足夠愚蠢的編譯器(SDT AFAIR)。保存一些CPU資源的方法之一是分析if分支並重寫if條件,以便正常流程不會中斷線性指令序列。這有積極的影響,因爲CPU預測模塊更有效的使用和更有效的代碼段內存緩存使用。

我認爲在這裏我們看到了非常接近的優化。在第一個代碼片段中,兩個分支都導致正常序列被破壞(對於一個分支,行6,對另一個分支行12)。在第二個片段中,一個分支指令排序爲retq,其他分支序列具有單一跳轉(不比第一個片段差)。請注意2 retq說明。

所以我可以看到這是不是重新排序,從而分支塊jejne而是討論的問題是不具有任何保存和jump充分預測塊電源進入其中的一個線性的指令序列。

關於「爲什麼GCC偏好另一個分支」......我在文檔中看到這可能是靜態分支預測的結果(基於翻譯單元內的調用?)。無論如何,我建議玩__builtin_expect有更詳細的答案。

+0

是的......但不同的回報點也可以在「je」版本上實現。如果/ else塊重新排序_consciously_:在最初的程序中,將if(a)'改爲'if(!a)'完全相反:從'jne'(非優化)版本改爲'je'(分支訂單優化)版本。我不能相信GCC做這個改變只是爲了取笑我! :) – lledr

+0

我認爲在任何情況下__builtin_expect應該幫助這個;-)。 http://blog.man7.org/2012/10/how-much-do-builtinexpect-likely-and.html –