<meter id="pryje"><nav id="pryje"><delect id="pryje"></delect></nav></meter>
          <label id="pryje"></label>

          新聞中心

          EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > linux內(nèi)核中的likely和unlikely

          linux內(nèi)核中的likely和unlikely

          作者: 時(shí)間:2016-11-22 來(lái)源:網(wǎng)絡(luò) 收藏
          Kernel version:2.6.14

          CPU architecture:ARM920T

          本文引用地址:http://www.ex-cimer.com/article/201611/320003.htm

          Author:ce123(http://blog.csdn.net/ce123)

          GCCversion:arm-linux-gcc-3.4.1

          看內(nèi)核時(shí)經(jīng)常遇到if(likely( )){}或是if(unlikely( ))這樣的語(yǔ)句,不甚了解,例如(選自kernel/fork.c中copy_process):

          [plain]view plaincopy
          print?
          1. SET_LINKS(p);
          2. if(unlikely(p->ptrace&PT_PTRACED))
          3. __ptrace_link(p,current->parent);

          下面詳細(xì)分析一下。

          likely() 與 unlikely()是內(nèi)核中定義的兩個(gè)宏。位于/include/linux/compiler.h中,具體定義如下:

          [plain]view plaincopy
          print?
          1. #definelikely(x)__builtin_expect(!!(x),1)
          2. #defineunlikely(x)__builtin_expect(!!(x),0)

          __builtin_expect是GCC(version>=2.9)引進(jìn)的內(nèi)建函數(shù),其作用就是幫助編譯器判斷條件跳轉(zhuǎn)的預(yù)期值,避免跳轉(zhuǎn)造成時(shí)間亂費(fèi),有利于代碼優(yōu)化。查閱GCC手冊(cè),發(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.

          大致意思是:可以使用。由于大部分程序員在分支預(yù)測(cè)方面做得很糟糕,所以GCC提供了__builtin_expect這個(gè)內(nèi)建函數(shù),給編譯器提供分支預(yù)測(cè)信息,以幫助程序員處理分支預(yù)測(cè),優(yōu)化程序。其第一個(gè)參數(shù)EXP為一個(gè)整型表達(dá)式,這個(gè)內(nèi)建函數(shù)的返回值也是這個(gè)EXP,而C為一個(gè)編譯期常量,這個(gè)函數(shù)的語(yǔ)義是:你期望EXP表達(dá)式的值等于常量C,從而GCC為你優(yōu)化程序,將符合這個(gè)條件的分支放在合適的地方。由于該內(nèi)建函數(shù)只提供了整型表達(dá)式,所以如果你要優(yōu)化其他類型的表達(dá)式,可以采用指針的形式。

          當(dāng)GCC的版本較低時(shí)(_GNUC_MINOR__ < 96),__builtin_expect直接返回EXP。下面的代碼摘自/include/linux/compiler-gcc2.h。

          [plain]view plaincopy
          print?
          1. /*ThesedefinitionsareforGCCv2.x.*/
          2. /*SomewhereinthemiddleoftheGCC2.96developmentcycle,weimplemented
          3. amechanismbywhichtheusercanannotatelikelybranchdirectionsand
          4. expecttheblockstobereorderedappropriately.Define__builtin_expect
          5. tonothingforearliercompilers.*/
          6. #include
          7. #if__GNUC_MINOR__<96
          8. #define__builtin_expect(x,expected_value)(x)
          9. #endif

          總結(jié)一下:if() 語(yǔ)句照常用, 和以前一樣, 只是 如果你覺(jué)得if()是1 的可能性非常大的時(shí)候, 就在表達(dá)式的外面加一個(gè)likely(),如果可能性非常小(比如幾率非常小),就用unlikely()包裹上。下面我們看一個(gè)例子。

          [plain]view plaincopy
          print?
          1. //test_builtin_expect.c
          2. #definelikely(x)__builtin_expect(!!(x),1)
          3. #defineunlikely(x)__builtin_expect(!!(x),0)
          4. inttest_likely(intx)
          5. {
          6. if(likely(x))
          7. x=5;
          8. else
          9. x=6;
          10. returnx;
          11. }
          12. inttest_unlikely(intx)
          13. {
          14. if(unlikely(x))
          15. x=5;
          16. else
          17. x=6;
          18. returnx;
          19. }

          root@czu:~/桌面/socket# arm-linux-gcc -fprofile-arcs -O2 -c test.c

          root@czu:~/桌面/socket# arm-linux-gcc -fprofile-arcs -O2 -o test test.c

          root@czu:~/桌面/socket# arm-linux-objdump -D test > test.dis

          [plain]view plaincopy
          print?
          1. 000088cc:
          2. 88cc:e3500000cmpr0,#0;0x0
          3. 88d0:e92d4010stmdbsp!,{r4,lr}
          4. 88d4:e59fc044ldrip,[pc,#68];8920<.text+0x148>
          5. 88d8:e59fe044ldrlr,[pc,#68];8924<.text+0x14c>
          6. 88dc:e3a00005movr0,#5;0x5
          7. 88e0:0a000006beq8900//前面通過(guò)cmp將r0和0進(jìn)行比較,因?yàn)閤=1的概率很大,優(yōu)先執(zhí)行不等于0的分支
          8. 88e4:e89c0018ldmiaip,{r3,r4}
          9. 88e8:e3a02000movr2,#0;0x0
          10. 88ec:e3a01001movr1,#1;0x1
          11. 88f0:e0933001addsr3,r3,r1
          12. 88f4:e0a44002adcr4,r4,r2
          13. 88f8:e88c0018stmiaip,{r3,r4}
          14. 88fc:e8bd8010ldmiasp!,{r4,pc}
          15. 8900:e89e0006ldmialr,{r1,r2}
          16. 8904:e3a04000movr4,#0;0x0
          17. 8908:e3a03001movr3,#1;0x1
          18. 890c:e0911003addsr1,r1,r3
          19. 8910:e0a22004adcr2,r2,r4
          20. 8914:e3a00006movr0,#6;0x6
          21. 8918:e88e0006stmialr,{r1,r2}
          22. 891c:e8bd8010ldmiasp!,{r4,pc}
          23. 8920:000121e0andeqr2,r1,r0,ror#3
          24. 8924:000121e8andeqr2,r1,r8,ror#3
          25. 00008928:
          26. 8928:e3500000cmpr0,#0;0x0
          27. 892c:e92d4010stmdbsp!,{r4,lr}
          28. 8930:e59fc044ldrip,[pc,#68];897c<.text+0x1a4>
          29. 8934:e59fe044ldrlr,[pc,#68];8980<.text+0x1a8>
          30. 8938:e3a00005movr0,#5;0x5
          31. 893c:1a000007bne8960//前面通過(guò)cmp將r0和0進(jìn)行比較,因?yàn)閤=0的概率很大,優(yōu)先執(zhí)行等于0的分支
          32. 8940:e89c0018ldmiaip,{r3,r4}
          33. 8944:e3a02000movr2,#0;0x0
          34. 8948:e3a01001movr1,#1;0x1
          35. 894c:e0933001addsr3,r3,r1
          36. 8950:e0a44002adcr4,r4,r2
          37. 8954:e3a00006movr0,#6;0x6
          38. 8958:e88c0018stmiaip,{r3,r4}
          39. 895c:e8bd8010ldmiasp!,{r4,pc}
          40. 8960:e89e0006ldmialr,{r1,r2}
          41. 8964:e3a04000movr4,#0;0x0
          42. 8968:e3a03001movr3,#1;0x1
          43. 896c:e0911003addsr1,r1,r3
          44. 8970:e0a22004adcr2,r2,r4
          45. 8974:e88e0006stmialr,{r1,r2}
          46. 8978:e8bd8010ldmiasp!,{r4,pc}
          47. 897c:000121f8streqdr2,[r1],-r8
          48. 8980:000121f0streqdr2,[r1],-r0

          如果我們將代碼修改一下,不用這兩個(gè)宏結(jié)果會(huì)怎樣呢?
          [plain]view plaincopy
          print?
          1. //test_builtin_expect.c
          2. inttest_likely(intx)
          3. {
          4. if(x)
          5. x=5;
          6. else
          7. x=6;
          8. returnx;
          9. }
          10. inttest_unlikely(intx)
          11. {
          12. if(x)
          13. x=5;
          14. else
          15. x=6;
          16. returnx;
          17. }
          反匯編代碼如下:

          [plain]view plaincopy
          print?
          1. 00008460:
          2. 8460:e3500000cmpr0,#0;0x0
          3. 8464:03a00006moveqr0,#6;0x6
          4. 8468:13a00005movner0,#5;0x5
          5. 846c:e1a0f00emovpc,lr
          6. 00008470:
          7. 8470:e3500000cmpr0,#0;0x0
          8. 8474:03a00006moveqr0,#6;0x6
          9. 8478:13a00005movner0,#5;0x5
          10. 847c:e1a0f00emovpc,lr

          如上述例子分析所示,兩個(gè)函數(shù)編譯生成的匯編語(yǔ)句所使用到的跳轉(zhuǎn)指令不一樣,仔細(xì)分析下會(huì)發(fā)現(xiàn)__builtin_expect實(shí)際上是為了滿足在大多數(shù)情況不執(zhí)行跳轉(zhuǎn)指令,__builtin_expect僅僅是告訴編譯器優(yōu)化,并沒(méi)有改變其對(duì)真值的判斷。宏likely和宏unlikely唯一的作用就是選擇”將if分支還是else分支放在跳轉(zhuǎn)指令之后,從而優(yōu)化程序的執(zhí)行效率”。 因?yàn)閘ikely(EXP)代表?xiàng)l件表達(dá)式EXP很可能成立,而unlikely(EXP)代表?xiàng)l件表達(dá)式EXP很可能不成立,當(dāng)程序員清楚EXP表達(dá)式 多數(shù)情況成立(不成立)時(shí),就可使用likely(unlikely),使if分支(else分支)緊跟跳轉(zhuǎn)指令其后,從而在大多數(shù)情況下不用執(zhí)行跳轉(zhuǎn)指令,避開跳轉(zhuǎn)指令所帶來(lái)的開銷,從而達(dá)到優(yōu)化的目的。

          還有一點(diǎn)需要注意的是,在生成匯編時(shí)用的是arm-linux-gcc -fprofile-arcs -O2 -c test_builtin_expect.c,而不是arm-linux-gcc-O2 -c test_builtin_expect.c。



          關(guān)鍵詞: linux內(nèi)核likelyunlikel

          評(píng)論


          技術(shù)專區(qū)

          關(guān)閉
          看屁屁www成人影院,亚洲人妻成人图片,亚洲精品成人午夜在线,日韩在线 欧美成人 (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();