linux內(nèi)核中的likely和unlikely
CPU architecture:ARM920T
本文引用地址:http://www.ex-cimer.com/article/201611/320003.htmAuthor:ce123(http://blog.csdn.net/ce123)
GCCversion:arm-linux-gcc-3.4.1
看內(nèi)核時經(jīng)常遇到if(likely( )){}或是if(unlikely( ))這樣的語句,不甚了解,例如(選自kernel/fork.c中copy_process):
- SET_LINKS(p);
- if(unlikely(p->ptrace&PT_PTRACED))
- __ptrace_link(p,current->parent);
下面詳細分析一下。
likely() 與 unlikely()是內(nèi)核中定義的兩個宏。位于/include/linux/compiler.h中,具體定義如下:
- #definelikely(x)__builtin_expect(!!(x),1)
- #defineunlikely(x)__builtin_expect(!!(x),0)
__builtin_expect是GCC(version>=2.9)引進的內(nèi)建函數(shù),其作用就是幫助編譯器判斷條件跳轉(zhuǎn)的預期值,避免跳轉(zhuǎn)造成時間亂費,有利于代碼優(yōu)化。查閱GCC手冊,發(fā)現(xiàn)其定義如下(http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html):
-- 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 value of C must be a compile-time constant. The
semantics of the built-in are that it is expected that EXP == C.
For example:
if (__builtin_expect (x, 0))
foo ();
would indicate 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))
error ();
when testing pointer or floating-point values.
大致意思是:可以使用。由于大部分程序員在分支預測方面做得很糟糕,所以GCC提供了__builtin_expect這個內(nèi)建函數(shù),給編譯器提供分支預測信息,以幫助程序員處理分支預測,優(yōu)化程序。其第一個參數(shù)EXP為一個整型表達式,這個內(nèi)建函數(shù)的返回值也是這個EXP,而C為一個編譯期常量,這個函數(shù)的語義是:你期望EXP表達式的值等于常量C,從而GCC為你優(yōu)化程序,將符合這個條件的分支放在合適的地方。由于該內(nèi)建函數(shù)只提供了整型表達式,所以如果你要優(yōu)化其他類型的表達式,可以采用指針的形式。
當GCC的版本較低時(_GNUC_MINOR__ < 96),__builtin_expect直接返回EXP。下面的代碼摘自/include/linux/compiler-gcc2.h。
- /*ThesedefinitionsareforGCCv2.x.*/
- /*SomewhereinthemiddleoftheGCC2.96developmentcycle,weimplemented
- amechanismbywhichtheusercanannotatelikelybranchdirectionsand
- expecttheblockstobereorderedappropriately.Define__builtin_expect
- tonothingforearliercompilers.*/
- #include
- #if__GNUC_MINOR__<96
- #define__builtin_expect(x,expected_value)(x)
- #endif
總結(jié)一下:if() 語句照常用, 和以前一樣, 只是 如果你覺得if()是1 的可能性非常大的時候, 就在表達式的外面加一個likely(),如果可能性非常小(比如幾率非常小),就用unlikely()包裹上。下面我們看一個例子。
- //test_builtin_expect.c
- #definelikely(x)__builtin_expect(!!(x),1)
- #defineunlikely(x)__builtin_expect(!!(x),0)
- inttest_likely(intx)
- {
- if(likely(x))
- x=5;
- else
- x=6;
- returnx;
- }
- inttest_unlikely(intx)
- {
- if(unlikely(x))
- x=5;
- else
- x=6;
- returnx;
- }
root@czu:~/桌面/socket# arm-linux-gcc -fprofile-arcs -O2 -c test.c
root@czu:~/桌面/socket# arm-linux-gcc -fprofile-arcs -O2 -o test test.croot@czu:~/桌面/socket# arm-linux-objdump -D test > test.dis
- 000088cc
: - 88cc:e3500000cmpr0,#0;0x0
- 88d0:e92d4010stmdbsp!,{r4,lr}
- 88d4:e59fc044ldrip,[pc,#68];8920<.text+0x148>
- 88d8:e59fe044ldrlr,[pc,#68];8924<.text+0x14c>
- 88dc:e3a00005movr0,#5;0x5
- 88e0:0a000006beq8900
//前面通過cmp將r0和0進行比較,因為x=1的概率很大,優(yōu)先執(zhí)行不等于0的分支 - 88e4:e89c0018ldmiaip,{r3,r4}
- 88e8:e3a02000movr2,#0;0x0
- 88ec:e3a01001movr1,#1;0x1
- 88f0:e0933001addsr3,r3,r1
- 88f4:e0a44002adcr4,r4,r2
- 88f8:e88c0018stmiaip,{r3,r4}
- 88fc:e8bd8010ldmiasp!,{r4,pc}
- 8900:e89e0006ldmialr,{r1,r2}
- 8904:e3a04000movr4,#0;0x0
- 8908:e3a03001movr3,#1;0x1
- 890c:e0911003addsr1,r1,r3
- 8910:e0a22004adcr2,r2,r4
- 8914:e3a00006movr0,#6;0x6
- 8918:e88e0006stmialr,{r1,r2}
- 891c:e8bd8010ldmiasp!,{r4,pc}
- 8920:000121e0andeqr2,r1,r0,ror#3
- 8924:000121e8andeqr2,r1,r8,ror#3
- 00008928
: - 8928:e3500000cmpr0,#0;0x0
- 892c:e92d4010stmdbsp!,{r4,lr}
- 8930:e59fc044ldrip,[pc,#68];897c<.text+0x1a4>
- 8934:e59fe044ldrlr,[pc,#68];8980<.text+0x1a8>
- 8938:e3a00005movr0,#5;0x5
- 893c:1a000007bne8960
//前面通過cmp將r0和0進行比較,因為x=0的概率很大,優(yōu)先執(zhí)行等于0的分支 - 8940:e89c0018ldmiaip,{r3,r4}
- 8944:e3a02000movr2,#0;0x0
- 8948:e3a01001movr1,#1;0x1
- 894c:e0933001addsr3,r3,r1
- 8950:e0a44002adcr4,r4,r2
- 8954:e3a00006movr0,#6;0x6
- 8958:e88c0018stmiaip,{r3,r4}
- 895c:e8bd8010ldmiasp!,{r4,pc}
- 8960:e89e0006ldmialr,{r1,r2}
- 8964:e3a04000movr4,#0;0x0
- 8968:e3a03001movr3,#1;0x1
- 896c:e0911003addsr1,r1,r3
- 8970:e0a22004adcr2,r2,r4
- 8974:e88e0006stmialr,{r1,r2}
- 8978:e8bd8010ldmiasp!,{r4,pc}
- 897c:000121f8streqdr2,[r1],-r8
- 8980:000121f0streqdr2,[r1],-r0
- //test_builtin_expect.c
- inttest_likely(intx)
- {
- if(x)
- x=5;
- else
- x=6;
- returnx;
- }
- inttest_unlikely(intx)
- {
- if(x)
- x=5;
- else
- x=6;
- returnx;
- }
- 00008460
: - 8460:e3500000cmpr0,#0;0x0
- 8464:03a00006moveqr0,#6;0x6
- 8468:13a00005movner0,#5;0x5
- 846c:e1a0f00emovpc,lr
- 00008470
: - 8470:e3500000cmpr0,#0;0x0
- 8474:03a00006moveqr0,#6;0x6
- 8478:13a00005movner0,#5;0x5
- 847c:e1a0f00emovpc,lr
如上述例子分析所示,兩個函數(shù)編譯生成的匯編語句所使用到的跳轉(zhuǎn)指令不一樣,仔細分析下會發(fā)現(xiàn)__builtin_expect實際上是為了滿足在大多數(shù)情況不執(zhí)行跳轉(zhuǎn)指令,__builtin_expect僅僅是告訴編譯器優(yōu)化,并沒有改變其對真值的判斷。宏likely和宏unlikely唯一的作用就是選擇”將if分支還是else分支放在跳轉(zhuǎn)指令之后,從而優(yōu)化程序的執(zhí)行效率”。 因為likely(EXP)代表條件表達式EXP很可能成立,而unlikely(EXP)代表條件表達式EXP很可能不成立,當程序員清楚EXP表達式 多數(shù)情況成立(不成立)時,就可使用likely(unlikely),使if分支(else分支)緊跟跳轉(zhuǎn)指令其后,從而在大多數(shù)情況下不用執(zhí)行跳轉(zhuǎn)指令,避開跳轉(zhuǎn)指令所帶來的開銷,從而達到優(yōu)化的目的。
還有一點需要注意的是,在生成匯編時用的是arm-linux-gcc -fprofile-arcs -O2 -c test_builtin_expect.c,而不是arm-linux-gcc-O2 -c test_builtin_expect.c。
評論