2017-04-03 17 views
3

GCC(和鏘也),提供這種__builtin_expect人類輔助的分支預測,如所解釋here。非正式地,人們用以下方式解釋它的語義:「編譯器只是無條件地處理指定的分支,如果條件應該與所示不同,則會發生昂貴的回滾」。可以使用`__builtin_expect`來影響程序語義嗎?

但是,如果我有一段代碼如下所示:

if (__builtin_expect(p != 0, 1)) // line 1 
    p->access_object();   // line 2 

如果我處理的非正式解釋上述字面上,編譯器可以只執行第2行,而不在第1行等待條件的計算,因此如果指針碰巧爲null,則會導致未定義的行爲(空指針取消引用)。

我的問題是,如果我使用__builtin_expect我仍然得到了保證,我的防守檢查工作?如果是這樣,如果我使用__builtin_expect作爲上述防禦性檢查,是否可以獲得任何運行時間的好處?

(注:我使用__builtin_expect這樣的目標是獲得的情況下,p非空的最高性能,在放緩(甚至數量級)的情況下,p是空的費用;即便後一種情況下會出現相當頻繁。)

+3

IMO這是一個不好的非正式描述,據我所知,它只是優化了哪個分支,你告訴它。而這種信念似乎是由[回答一個相關的問題]備份(http://stackoverflow.com/q/7346929/583833) – Borgleader

+0

@Borgleader:所以這個說,首先檢查執行反正。唯一的問題是執行檢查後可以立即執行,還是檢查後的一段時間。我是否正確? – Andrzej

+3

我想說編譯器不會「無條件地處理指定的分支」。它*開始*無條件地處理它,但如果選擇了錯誤的分支則丟棄任何結果。這種丟棄發生在非常低​​的水平上。在條件被檢查之前,這些無條件計算的結果甚至不會在實際RAM中結束。另外,就我所知,即使沒有'__builtin_expect',在檢查條件之前總會選擇其中一個分支,但是它會被分支預測變量而不是你選擇。 – HolyBlackCat

回答

4

不,builtin_expect不會影響無種族程序的語義。

特別是,如果該代碼具有無法撤銷的副作用,編譯器不得發出執行if塊主體的代碼。除了性能,代碼必須是「好像」builtin_expect未使用。

爲您具體的例子:

if (__builtin_expect(p != 0, 1)) // line 1 
    p->access_object();   // line 2 

p,如果它是空無法提領。那麼在這種情況下builtin_expect有什麼意義呢?它能做的最多的是告訴編譯器「p可能不爲空,所以access_object()可能會被調用。」如果access_object()的定義是inline,編譯器可能會嘗試內嵌它,而如果你說:「p可能是空」的編譯器可能會決定它最好不要在此調用點內聯爲access_object()的代碼,因爲它是不太可能被使用。

事實上,這會導致在實踐中非直觀地使用builtin_expect:無論「可能」如何,您都可以使用它來表示「此代碼是慢速路徑」。作爲一個簡單的例子,一個服務器程序可能需要這樣做:

if (__builtin_expect(is_allowed(user, request), 1)) 
    process(request); 
else 
    reject(request); 

即使我們發現,請求的50%是非法的,將被拒絕,我們還是可能會決定,以紀念「幸福路徑」爲可能採取的,因爲我們不關心減緩拒收。

+0

是的,實際上這是我的動機:不是顯示哪個分支更頻繁地發生,而是顯示我想優化哪一個分支。 – Andrzej

相關問題