轉載 __builtin_expect — 分支預測優化
1.引言
在很多原始碼如Linux核心、Glib等,我們都能看到likely()和unlikely()這兩個巨集,通常這兩個巨集定義是下面這樣的形式。
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0)
可以看出這2個巨集都是使用函式__builtin_expect()實現的,__builtin_expect()函式是GCC的一個內建函式(build-in function).
2.函式宣告
函式__builtin_expect()是GCC v2.96版本引入的, 其宣告如下:
long __builtin_expect(long exp, long c);
2.1. 功能描述
由於大部分程式設計師在分支預測方面做得很糟糕,所以GCC 提供了這個內建函式來幫助程式設計師處理分支預測.
你期望 exp 表示式的值等於常量 c,看 c 的值, 如果 c 的值為0(即期望的函式返回值), 那麼 執行 if 分支的的可能性小, 否則執行 else 分支的可能性小(函式的返回值等於第一個引數exp).
GCC在編譯過程中,會將可能性更大的程式碼緊跟著前面的程式碼,從而減少指令跳轉帶來的效能上的下降, 達到優化程式的目的.
通常,你也許會更喜歡使用 gcc 的一個引數 '-fprofile-arcs' 來收集程式執行的關於執行流程和分支走向的實際反饋資訊,但是對於很多程式來說,資料是很難收集的。
2.2. 引數詳解
①exp
exp 為一個整型表示式, 例如: (ptr != NULL)
② c
c 必須是一個編譯期常量, 不能使用變數
2.3. 返回值
返回值等於 第一個引數 exp
2.4. 使用方法
與關鍵字if一起使用.首先要明確一點就是 if (value) 等價於 if (__builtin_expert(value, x)), 與x的值無關.
例子如下:
例子1: 期望 x == 0, 所以執行func()的可能性小
if (__builtin_expect(x, 0)) { func(); }
else
{
//do someting
}
例子2 : 期望 ptr !=NULL這個條件成立(1), 所以執行func()的可能性小
if (__builtin_expect(ptr != NULL, 1)) {
//do something
}
else
{
func();
}
例子3: 引言中的likely()和unlikely()巨集
首先,看第一個引數!!(x), 他的作用是把(x)轉變成"布林值", 無論(x)的值是多少 !(x)得到的是true或false, !!(x)就得到了原值的"布林值"
使用 likely() ,執行 if 後面的語句 的機會更大,使用 unlikely(),執行 else 後面的語句的機會更大。
#define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) int main(char *argv[], int argc) { int a; /* Get the value from somewhere GCC can't optimize */ a = atoi (argv[1]); if (unlikely (a == 2))
{ a++;
} else {
a--; } printf ("%d\n", a); return 0; }
3.RATIONALE(原理)
if else 句型編譯後, 一個分支的彙編程式碼緊隨前面的程式碼,而另一個分支的彙編程式碼需要使用JMP指令才能訪問到.
很明顯通過JMP訪問需要更多的時間, 在複雜的程式中,有很多的if else句型,又或者是一個有if else句型的庫函式,每秒鐘被呼叫幾萬次,
通常程式設計師在分支預測方面做得很糟糕, 編譯器又不能精準的預測每一個分支,這時JMP產生的時間浪費就會很大,
函式__builtin_expert()就是用來解決這個問題的.
具體從彙編角度來分析其原理的例子,大家可以參照http://kernelnewbies.org/FAQ/LikelyUnlikely,
其對應的中文翻譯版見http://velep.com/archives/795.html
轉自:https://www.cnblogs.com/LubinLew/p/GCC-__builtin_expect.html