扩展阅读
  • linux c/c++ IP字符串转换成可比较大小的数字
  • 中文Linux与西文Linus分别哪一个版是权威?I认为是:中科软的白旗Linux与西文的绿帽子Linux!大家的看法呢? iis7站长之家
  • linux哪个版本好?linux操作系统版本详细介绍及选择方案推荐
  • 在虚拟机上安装的linux上,能像真的linux系统一样开发linux程序么?
  • secureCRT下Linux终端汉字乱码解决方法
  • 我重装window后,把linux的引导区覆盖了,进不了linux怎么办?急啊,望热心的人帮助 (现在有linux的盘)
  • Linux c字符串中不可打印字符转换成16进制
  • 红旗Linux主机可以通过127.0.0.1访问,但如何是连网的Win2000机器通过Linux的IP去访问Linux
  • Linux常用命令介绍:更改所属用户群组或档案属性
  • 安装vmware软件,不用再安装linux系统,就可以模拟linux系统了,然后可以在其上学习一下LINUX下的基本操作 了?
  • linux命令大全详细分类介绍及常用linux命令文档手册下载
  • 我重装window后,把linux的引导区覆盖了,进不了linux怎么办?急啊,望热心的人帮助 (现在没有linux的盘,只有DOS启动盘)
  • Linux Kernel 'sctp_v6_xmit()'函数信息泄露漏洞
  • 如何让win2000和linux共存。我装好WIN2000,再装LINUX7.0,但LILO只能找到LINUX,不能引导WIN2000
  • linux c下利用srand和rand函数生成随机字符串
  • 在windows中的VMware装了个linux,主板有两个串口,能做windows和linux的串口通信测试么,怎么测试这两个串口在linux是有效
  • Docker官方镜像将会使用Alpine Linux替换Ubuntu
  • 我们网站的服务器从windows2000迁往linux,ASP程序继续使用,可是我连LINUX的皮毛都不了解,大家告诉我LINUX下怎么建网站??
  • Linux下c基于openssl生成MD5的函数
  • 中文Linux与西文Linus分别哪一个版是权威?I认为是:中科软的白旗Linux与西文的绿帽子Linux!大家的看法呢?
  • linux僵尸(zombie)进程介绍及清除
  • Windows2000和Linux双操作系统,Linux系统有问题,我直接把Linux分区删除后,Windows2000进不去了,怎么办???
  •  
    当前位置:  操作系统>Linux

    linux内核中的likely宏和unlikely宏介绍及用法

     
        发布时间:2014-5-11  


        本文导语:  linux内核中的likely宏和unlikely宏介绍 在内核代码中时常 会看到unlikely和likely的踪影。他们实际上是解释在 linux/compiler.h 中的两个宏。#define likely(x) __builtin_exp ect(!!(x), 1) #define unlikely(x) __builtin_exp ect(!!(x), 0) ...

    linux内核中的likely和unlikely宏介绍

       在内核代码中时常 会看到unlikely和likely的踪影。他们实际上是解释在 linux/compiler.h 中的两个宏。

    #define likely(x)    __builtin_exp ect(!!(x), 1)
    #define unlikely(x)  __builtin_exp ect(!!(x), 0)

       这里的__built_exp ect()函数gcc的内建函数。

       ____builtin_expect是gcc编译器版本>=2.96)提供给程序员使用,目的是使得程序员可以把分支预测的信息提供给编译器,以降低因为指令跳转带来的分支下降,它的返回值就是它的第一个参数(这个参数必须是整数)传给它的值。

       所以在linux2.6.38中,____builtin_expect的返回值就是x的值,所以:

        if(likely(value))  等价于 if(value)

         if(unlikely(value))等价于 if(value)

    这样我们在阅读代码时,就可以把if(likely(value)),if(unlikely(value))看做if(value),便于我们阅读代码。至于为什么要在内核代码中运用 这两个宏,首要 的目标 是为了实行 代码的优化,提高系统执行速度


    __builtin_expect()的原理分析

      __builtin_expect()的机制,在gcc使用手册中有下面一则例子:

    if (__builtin_expect (x, 0))
                 foo ();

    这段程序暗示我们,既然我们期望__builtin_expect (x, 0)返回值为0,那么我们不希望调用foo()函数。

       __builtin_expect()在GCC的官方文档中解释如下:

    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的内建方法判断 EXP == C 是否成立,成立则将if分支中的执行语句紧跟放在汇编跳转指令之后,否则将else分支中的执行语句紧跟汇编跳转指令之后。如下例子所示:

    //test.c
        #define likely(x) __builtin_expect(!!(x),1)
        #define unlikely(x) __builtin_expect(!!(x),0)
        int test_unlikely(int x)
        {
            if(unlikely(x == 2))
            {
                x++;
            }
            else
            {
                x--;
            }
            return x;
        }
        int test_likely(int x)
        {
            if(likely(x == 2))
            {
                x++;
            }
            else
            {
                x--;
            }
            return x;
        }


    编译导出目标文件的汇编表示:

    gcc -fprofile-arcs -O2 -c test.c

    objdump -d test.o

    得到如下汇编:

    test.o:     file format elf32-i386
    Disassembly of section .text:
    00000000 <test_likely>:
       0: 55                                 push   %ebp
       1: 83 05 00 00 00 00 01       addl   $0x1,0x0
       8: 89 e5                            mov    %esp,%ebp
       a: 83 15 04 00 00 00 00       adcl   $0x0,0x4
      11: 83 7d 08 02                   cmpl   $0x2,0x8(%ebp)                   //留意这里!!!判断 x == 2
      15: 75 15                            jne    2c <test_likely+0x2c>               //跳转指令jne!!!x != 2 时才跳转!
      17: 83 05 08 00 00 00 01       addl   $0x1,0x8                          //if分支代码 x++
      1e: b8 03 00 00 00               mov    $0x3,%eax
      23: 83 15 0c 00 00 00 00       adcl   $0x0,0xc
      2a: 5d                                pop    %ebp
      2b: c3                                ret   
      2c: 8b 45 08                       mov    0x8(%ebp),%eax                   //跳转到这里,else分支代码 x--
      2f: 5d                                pop    %ebp
      30: 83 e8 01                       sub    $0x1,%eax
      33: 83 05 10 00 00 00 01      addl   $0x1,0x10
      3a: 83 15 14 00 00 00 00      adcl   $0x0,0x14
      41: c3                               ret   
      42: 8d b4 26 00 00 00 00      lea    0x0(%esi),%esi
      49: 8d bc 27 00 00 00 00      lea    0x0(%edi),%edi
    00000050 <test_unlikely>:
      50: 55                               push   %ebp
      51: 89 e5                           mov    %esp,%ebp
      53: 8b 45 08                       mov    0x8(%ebp),%eax
      56: 83 05 18 00 00 00 01      addl   $0x1,0x18
      5d: 83 15 1c 00 00 00 00      adcl   $0x0,0x1c
      64: 83 f8 02                       cmp    $0x2,%eax                          //留意这里!!!判断 x == 2
      67: 74 13                           je     7c <test_unlikely+0x2c>           //跳转指令je!!!x == 2 时就跳转!
      69: 83 e8 01                       sub    $0x1,%eax                          //else分支代码 x--
      6c: 83 05 28 00 00 00 01      addl   $0x1,0x28
      73: 83 15 2c 00 00 00 00      adcl   $0x0,0x2c
      7a: 5d                               pop    %ebp
      7b: c3                               ret   
      7c: 83 05 20 00 00 00 01      addl   $0x1,0x20                       //跳转到这里,if分支代码 x++
      83: b0 03                           mov    $0x3,%al
      85: 83 15 24 00 00 00 00      adcl   $0x0,0x24
      8c: 5d                               pop    %ebp
      8d: c3                               ret   
      8e: 66 90                           xchg   %ax,%ax
    00000090 <_GLOBAL__I_0_test_unlikely>:
      90: 55                    push   %ebp
      91: 89 e5                 mov    %esp,%ebp
      93: 83 ec 08              sub    $0x8,%esp
      96: c7 04 24 00 00 00 00  movl   $0x0,(%esp)
      9d: e8 fc ff ff ff        call   9e <_GLOBAL__I_0_test_unlikely+0xe>
      a2: c9                    leave 
      a3: c3                    ret

      注意:likely和unlikely所生成的跳转指令是不同的,分别是jne和je.

       如上述例子分析所示,宏likely和宏unlikely唯一的作用就是选择“将if分支还是else分支放在跳转指令之后,从而优化程序的执行效率”。 因为likely(EXP)代表条件表达式EXP很可能成立,而unlikely(EXP)代表条件表达式EXP很可能不成立,当程序员清楚EXP表达式 多数情况成立(不成立)时,就可使用likely(unlikely),使if分支(else分支)紧跟跳转指令其后,从而在大多数情况下不用执行跳转指 令,避开跳转指令所带来的开销,从而达到优化的目的。

       linux内核中的likely宏和unlikely宏使用举例

      举例分析 :  

    if (likely(a>b)) {
      fun1();
      }
      if (unlikely(a<b)) {
      fun2();
      }

      这里就是程序员可以确定 a>b 在程序执行流程 中出现的可能相比较大,因此运用 了likely()告诉编译器将fun1()函数的二进制代码紧跟在前面程序的后面,这样就cache在预取数据时就可以将fun1()函数的二进制代码 拿到cache中。这样,也就添加 了cache的命中率。

      同样的,unlikely()的作用就是告诉编译器,a<b 的可能性很小所以这里在编译时,将fun2()的二进制代码尽量不要和前边的编译在一块。

      咱们 不用对likely和unlikely感到迷惑,须要 知晓 的就是 if(likely(a>b)) 和 if(a>b)在功能上是等价的,同样 if(unlikely(a<b)) 和 if(a<b) 的功能也是一样的。不一样的只是他们声称的二进制代码有所不一样 ,这一点咱们 也可以从他们的汇编代码中看到。

      比如下面的代码: 

    #include <stdio.h>
    #define unlikely(x) __builtin_exp ect(!!(x), 0)
    #define likely(x) __builtin_exp ect(!!(x), 1)
    int main()
    {
    int a=2,b=4;
    if(unlikely(a<b)) {
    printf("in the unlikely,is not your exp ecting!n");
    } else {
    printf("in the unlikely, is your exp ectingn");
    }
    if(likely(a<b)) {
    printf("in the likely, is your exp ectingn");
    }
    return 0;
    }

      执行结果:

      in the unlikely,is not your exp ecting!

      in the likely, is your exp ecting

      总之,likely和unlikely的功能就是添加 cache的命中率,提高系统执行速度。

    • 本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
      本站(WWW.)站内文章除注明原创外,均为转载,整理或搜集自网络.欢迎任何形式的转载,转载请注明出处.
      转载请注明:文章转载自:[169IT-IT技术资讯]
      本文标题:linux内核中的likely宏和unlikely宏介绍及用法
    相关文章推荐:


    站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3