查看linux內核源碼,你會發現有很多if (likely(""))...及if (unlikely(""))...語句,這些語句其實是編譯器的一種優化方式,具體分析如下:
likely及unlikely是一個宏定義:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
likely()的 意思是認為這個分支最有可能發生,如if (likely(x == 0)){...},這個語句表示x等于0最有可能發生,其實語意就相當于if (x == 0){...},只不過likely針
對程序指令運行做了優化,不去做一些無謂的指令跳轉;unlikely()意思相反,就是最不可能發生,注意if (unlikely(x == 0))還是相當于if (x==0)的邏輯。
如果需要更進一步了解likely()就必須要深入了解__bulitin_expect(!!(x), 1)函數。
— Built-in Function: long __builtin_expect (long exp, long c) You may use __builtin_expect to PRovide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (-fprofile-arcs), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect. The return value is the value of exp, which should be an integral expression. The semantics of the built-in are that it is expected that exp == c. For example: if (__builtin_expect (x, 0)) foo (); indicates that we do not expect to call foo, since we expect x to be zero. Since you are limited to integral expressions for exp, you should use constructions such as if (__builtin_expect (ptr != NULL, 1)) foo (*ptr); when testing pointer or floating-point values.
文檔鏈接:https://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html#Other-Builtins
從gcc官方文檔來看,內建函數long __builtin_expect (long exp, long c)主要用于優化程序的分支預測,減少程序的指令跳轉,現代處理器一般都是流水線架構,
很多芯片級的優化是靠流水線預取完成的,所以我們的程序優化也是需要盡量減少跳轉。
文檔也提到了由于大部分程序員根本就不了解自己程序的運行情況,所以推薦我們在編譯時加上-fprofile-arcs選項去評估我們的程序分支運行情況;-fprofile-arcs選
項是代碼覆蓋率測試工具gcov使用時需要增加的編譯選項,gcov能夠去統計及分析我們的程序的分支運行情況,關于gcov的使用這里不做介紹,只需要知道gcov是
一個測試統計工具,配合-fprofile-arcs工具使用,__builtin_expect 根據gcov的分析結果來做實際的分支預測優化。
這里可以大家還會有疑問,為什么#define likely(x) __builtin_expect(!!(x), 1)中要使用!!(x),這其實是因為函數__builtin_expect (long exp, long c)期望是
exp == c,這時的1相當于bool值true,所以exp需要是一個bool表達式,通過!!可以變成bool表達式而不改變原有函數,這樣才能夠正確的與1或0(bool值)做匹配
判斷;試想如果沒有!!,即#define likely(x) __builtin_expect((x), 1),那么likely(10)原本是希望表達式是true,但是根據函數的處理邏輯10 != 1,那么優化會
以false的結果來優化,這樣就陰差陽錯了!!!
最后講述一下__attribute__(section(""))屬性,這個屬性比較好理解,就是為某個函數或變量指定section,比如:
int __attribute__(section(".test.data")) value = 0;
這樣的話變量value將會被放在.test.data段中;
void __attribute__((section(".test.text"))) func(void){}
這樣函數func會被放入.test.text段中。
查看section信息可以通過如下命令:readelf -S xxx,可以查看可執行文件也可以是目標文件.o,關于section這里不過多介紹,只要大概知道一般我們的代碼都是
放在.text段,全局變量一般放在.data段,我們通過__attribute__((""))定義的符號就放在我們特定的section里面。
新聞熱點
疑難解答