likely(x)とunlikely(x)関数、すなわち__builting.expectの使用

15210 ワード

転載元:http://velep.com/archives/795.html
 
本論文で述べたlikely()とunlikely()の二つのマクロは、linuxカーネルコードといくつかのアプリケーションでよく見られます.実は、この二つのマクロはGCCコンパイラ内蔵のマクロです.builting.expectの使用.名前の通り、likelyは「可能性が高い」という意味で、unlikelyは「不可能」という意味です.実際の応用では、何を表していますか?またどのように使いますか?以下は外国語の翻訳です.Likely()とunlikely()は、Linuxカーネルコードに対して、条件判定文ではしばしばlikely()とunlikely()の呼び出しを見ます.下記のコードで示します.
bvl = bvec_alloc(gfp_mask, nr_iovecs, &idx);
if (unlikely(!bvl)) {
  mempool_free(bio, bio_pool);
  bio = NULL;
  goto out;
}
ここでは、likely()またはunlikely()を呼び出してコンパイラにこの条件が発生する可能性がありますか、あるいはあまりあり得ない可能性があると伝えて、コンパイラにこの条件の判断を正確に最適化するようにします.この2つのマクロはinclude/linux/comppiler.hファイルで見つけることができます.
#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)
GCCドキュメントで上記のコードの中の__u uを見つけることができます.builting.expectの説明は以下の通り抜粋されています.
    -- 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.
同前builting.expectの説明には、if(u builtinguexpect(x,0)foo()の二例があります.期待x==0を表します.つまりfoo()関数を実行しないことを期待しません.同じように、if(_builtinguexpect(ptr!=NULL、1)error()希望ポインタprtが空ではないこと、つまりerror()関数の実行が見られないことを示します.コンパイラの最適化作業はGCCの説明から分かります.builting.expectの主な役割は、条件ジャンプの予想値をコンパイラが判断し、jmpジャンプ命令の実行による時間の無駄を避けることです.それでは、コンパイラの最適化をどのように助けますか?コンパイラ最適化時には、条件ジャンプの予想値に応じて、正確な順にコンパイルコードを生成し、「起こり得る」条件分岐を順番にコマンドセグメントを実行します.jmp指令段ではありません.次に例を挙げて説明します.以下の簡単なCプログラムはgcc-O 2を使ってコンパイルします.
#define likely(x)    __builtin_expect(!!(x), 1)
#define unlikely(x)  __builtin_expect(!!(x), 0)
 
int main(char *argv[], int argc)
{
   int a;
 
   /*        (         ) */
   a = atoi (argv[1]);
 
   if (unlikely (a == 2))
      a++;
   else
      a--;
 
   printf ("%d
", a); return 0; }
Objdump-S逆アセンブリを使って、そのアセンブリコードを確認します.
80483b0 <main>:
//               
80483b0:       55                      push   %ebp
80483b1:       89 e5                   mov    %esp,%ebp
80483b3:       50                      push   %eax
80483b4:       50                      push   %eax
80483b5:       83 e4 f0                and    $0xfffffff0,%esp
//               atoi()
80483b8:       8b 45 08                mov    0x8(%ebp),%eax
80483bb:       83 ec 1c                sub    $0x1c,%esp
80483be:       8b 48 04                mov    0x4(%eax),%ecx
80483c1:       51                      push   %ecx
80483c2:       e8 1d ff ff ff          call   80482e4 <atoi@plt>
80483c7:       83 c4 10                add    $0x10,%esp
//                  2    ,   :“a == 2”
80483ca:       83 f8 02                cmp    $0x2,%eax
//             --------------------------------------------------------
//               'a'    2 (          ),    ,
//                   ,      CPU       .
//             --------------------------------------------------------
80483cd:       74 12                   je     80483e1 <main+0x31>
80483cf:       48                      dec    %eax
//               printf
80483d0:       52                      push   %edx
80483d1:       52                      push   %edx
80483d2:       50                      push   %eax
80483d3:       68 c8 84 04 08          push   $0x80484c8
80483d8:       e8 f7 fe ff ff          call   80482d4 <printf@plt>
//               0   .
80483dd:       31 c0                   xor    %eax,%eax
80483df:       c9                      leave
80483e0:       c3                      ret
上のプログラムでは、その中のunlikely()の代わりにlikely()を使ってコンパイルし直して、そのコードを見てください.
80483b0 <main>:
//               
80483b0:       55                      push   %ebp
80483b1:       89 e5                   mov    %esp,%ebp
80483b3:       50                      push   %eax
80483b4:       50                      push   %eax
80483b5:       83 e4 f0                and    $0xfffffff0,%esp
//               atoi()
80483b8:       8b 45 08                mov    0x8(%ebp),%eax
80483bb:       83 ec 1c                sub    $0x1c,%esp
80483be:       8b 48 04                mov    0x4(%eax),%ecx
80483c1:       51                      push   %ecx
80483c2:       e8 1d ff ff ff          call   80482e4 <atoi@plt>
80483c7:       83 c4 10                add    $0x10,%esp
//             --------------------------------------------------
//               'a'    2 (        ),     ,    ,
//                   CPU       .
//                 a != 2        ,      ,          .
//             ---------------------------------------------------
80483ca:       83 f8 02                cmp    $0x2,%eax
80483cd:       75 13                   jne    80483e2 <main+0x32>
//             a++      
80483cf:       b0 03                   mov    $0x3,%al
//               printf()
80483d1:       52                      push   %edx
80483d2:       52                      push   %edx
80483d3:       50                      push   %eax
80483d4:       68 c8 84 04 08          push   $0x80484c8
80483d9:       e8 f6 fe ff ff          call   80482d4 <printf@plt>
//               0   .
80483de:       31 c0                   xor    %eax,%eax
80483e0:       c9                      leave
80483e1:       c3                      ret
どう使いますか?条件判定文では、この条件が非常に満足可能であると考えられている場合には、likely()マクロを使用し、そうでないと、条件が非常に不可能または満足しにくい場合には、unlikely()マクロを使用する.参考資料本文英文原文:http://kernelnewbies.org/FAQ/LikelyUnlikelyより多くのGCCはマクロまたは関数を内蔵しています.詳細は以下の通りです.http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html