linux內核中的likely()和unlikely()宏的作用

在看linux內核代碼的時候,經常會看到likely(x)和unlikely(x)宏的使用。那這兩個宏有什麼作用呢?
這兩個宏在內核中的定義如下:
# define likely(x)  __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)

可見這裏使用了gcc的內建函數__builtin_expect()。

__builtin_expect (long exp, long c)函數:
該函數用來引導gcc進行條件分支預測。在一條指令執行時,由於流水線的作用,CPU可以同時完成下一條指令的取指,這樣可以提高CPU的利用率。在執行條件分支指令時,CPU也會預取下一條執行,但是如果條件分支的結果爲跳轉到了其他指令,那CPU預取的下一條指令就沒用了,這樣就降低了流水線的效率。
另外,跳轉指令相對於順序執行的指令會多消耗CPU時間,如果可以儘可能不執行跳轉,也可以提高CPU性能。

使用__builtin_expect (long exp, long c)函數可以幫助gcc優化程序編譯後的指令序列,使彙編指令儘可能的順序執行,從而提高CPU預取指令的正確率和執行效率。


__builtin_expect(exp, c)接受兩個long型的參數,用來告訴gcc:exp==c的可能性比較大。
例如,__builtin_expect(exp, 1) 表示程序執行過程中,exp取到1的可能性比較大。該函數的返回值爲exp自身。


內核中likely(x)和unlikely(x)宏:

知道__builtin_expect()函數的作用之後,我們就知道內核中likely(x)和unlikely(x)宏的作用了,通過likely(x)和unlikely(x)宏定義,我們可以得出他們的作用:
likely(x)等價於x,即if(likely(x))等價於if(x),但是它告訴gcc,x取1的可能性比較大。
unlikely(x)等價於x,即if(unlikely(x))等價於if(x),但是它告訴gcc,x取0的可能性比較大。


程序示例:
我們可以寫一個簡單的應用程序,看看__builtin_expect()函數的作用:
寫一個函數testfunc():
[cpp] view plain copy
  1. int testfunc(int x)  
  2. {  
  3.     if (__builtin_expect(!!(x), 1)) ///  
  4.     {  
  5.         x = 123;  
  6.     }  
  7.     else  
  8.     {  
  9.         x = 456;  
  10.     }  
  11.       
  12.     return x;  
  13. }  
這個函數中指明瞭if條件成立的可能性比較大,gcc可以據此優化程序指令序列。
在main()函數中調用該函數,編譯程序(使用-O3選項),查看反彙編後的彙編代碼:
[plain] view plain copy
  1. 00400780 <testfunc>:  
  2.   400780: 10800003 beqz a0,400790 <testfunc+0x10>  
  3.   400784: 2402007b li v0,123  
  4.   400788: 03e00008 jr ra  
  5.   40078c: 00000000 nop  
  6.   400790: 03e00008 jr ra  
  7.   400794: 240201c8 li v0,456  

從彙編代碼中可以看出,如果if成立,即x==1,則第一條跳轉指令不成功,指令得以順序執行。而如果else成立,則需要多執行一次跳轉

如果testfunc()如下實現:
[cpp] view plain copy
  1. int testfunc(int x)  
  2. {  
  3.     if (__builtin_expect(!!(x), 0)) ///  
  4.     {  
  5.         x = 123;  
  6.     }  
  7.     else  
  8.     {  
  9.         x = 456;  
  10.     }  
  11.       
  12.     return x;  
  13. }  
這個函數中指明瞭else條件成立的可能性比較大,gcc可以據此優化程序指令序列。
在main()函數中調用該函數,編譯程序(使用-O3選項),查看反彙編後的彙編代碼:
[plain] view plain copy
  1. 00400780 <testfunc>:  
  2.   400780: 14800002 bnez a0,40078c <testfunc+0xc>  
  3.   400784: 2402007b li v0,123  
  4.   400788: 240201c8 li v0,456  
  5.   40078c: 03e00008 jr ra  
  6.   400790: 00000000 nop  
從彙編代碼中可以看出,如果else成立,即x==0,則第一條跳轉指令不成功,指令得以順序執行。而如果if成立,則需要多執行一次跳轉


其他說明:

likely(x)宏傳入__builtin_expect(!!(x), 0)的第一個參數爲!!x,這樣寫是因爲__builtin_expect的第一個參數需要爲long型,而我們如果想傳入指針或字符串類型,則需要使用!!x將x變成long型,例如,如果一個指針ptr==NULL,則!ptr=1,而!!ptr=0。

注意有些gcc版本沒有實現__builtin_expect內建函數,所以這個函數不會起實際作用,相應的,likely(x)和unlikely(x)也就沒有效果了。



原文:http://blog.csdn.net/jasonchen_gbd/article/details/44968395

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章