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

          新聞中心

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

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

          作者: 時間:2016-11-22 來源:網(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)核時經(jīng)常遇到if(likely( )){}或是if(unlikely( ))這樣的語句,不甚了解,例如(選自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);

          下面詳細分析一下。

          likely() 與 unlikely()是內(nèi)核中定義的兩個宏。位于/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)引進的內(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。

          [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() 語句照常用, 和以前一樣, 只是 如果你覺得if()是1 的可能性非常大的時候, 就在表達式的外面加一個likely(),如果可能性非常小(比如幾率非常小),就用unlikely()包裹上。下面我們看一個例子。

          [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//前面通過cmp將r0和0進行比較,因為x=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//前面通過cmp將r0和0進行比較,因為x=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

          如果我們將代碼修改一下,不用這兩個宏結(jié)果會怎樣呢?
          [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

          如上述例子分析所示,兩個函數(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。



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

          評論


          技術(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); })();