免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2389 | 回复: 0
打印 上一主题 下一主题

内核中 likely(x) 和 unlikely(x) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-10-19 21:42 |只看该作者 |正序浏览
本帖最后由 tbag-tang 于 2010-10-23 21:09 编辑

在内核源码中经常出现以下两个宏:likely(x) 和 unlikely(x)
其定义如下:

# define likely(x)        __builtin_expect(!!(x), 1)
# define unlikely(x)        __builtin_expect(!!(x), 0)

我先解释下这里的gcc内建函数 __builtin_expect
在/usr/include/gmp-i386.h中有如下说明.
/* __builtin_expect is in gcc 3.0, and not in 2.95. */

__builtin_expect(!!(x), 1)
        这里表示条件为真的可能性比较大
__builtin_expect(!!(x), 0)
        这里表示条件为假的可能性比较大
注:如果x为0,则!!(x)还是0; 如果x不为0,则!!(x)为1.

真的是这样吗? 带着这个疑问接着往下看。

测试程序如下:
1) __builtin_expect(!!(x), 0)

// test.c
#include <stdio.h>

void ketty(int va)
{
        if (__builtin_expect(!!(va), 0)) {
                va = va + 8;
        }
        else {
                va = va + 98;
        }

        printf("%d\n", va);
}

int main(void)
{
        ketty(1);

        return 0;
}

编译及反汇编过程如下:
tbag@:~/test$ gcc -O2 test.c -o unlikely
tbag@:~/test$ objdump -D unlikely > unlike
tbag@:~/test$        vi unlike

08048410 <ketty>:
8048410:        55                           push   %ebp
8048411:        89 e5                        mov    %esp,%ebp
8048413:        83 ec 18                     sub    $0x18,%esp
8048416:        8b 45 08                     mov    0x8(%ebp),%eax
------------------------------------------------------------
8048419:        85 c0                        test   %eax,%eax
804841b:        75 1c                        jne    8048439 <ketty+0x29>
804841d:        b0 62                        mov    $0x62,%al

------------------------------------------------------------
804841f:        89 44 24 08                  mov    %eax,0x8(%esp)
8048423:        c7 44 24 04 20 85 04         movl   $0x8048520,0x4(%esp)
804842a:        08
804842b:        c7 04 24 01 00 00 00         movl   $0x1,(%esp)
8048432:        e8 f5 fe ff ff               call   804832c <__printf_chk@plt>
8048437:        c9                           leave  
8048438:        c3                           ret   
------------------------------------------------------------
8048439:        83 c0 08                     add    $0x8,%eax
------------------------------------------------------------
804843c:        eb e1                        jmp    804841f <ketty+0xf>
804843e:        66 90                        xchg   %ax,%ax

TEST   指令对两个操作数作按位与运算,将影响的标记记在标志位中,  
       供查询使用,而源目操作数均不改变。
note: 这里使用的是jne(知道这点很重要),紧跟其后的是else语句块里的代码


2) __builtin_expect(!!(x), 1)

#include <stdio.h>

void ketty(int va)
{
        if (__builtin_expect(!!(va), 1)) {
                va = va + 8;
        }
        else {
                va = va + 98;
        }

        printf("%d\n", va);
}

int main(void)
{
        ketty(1);

        return 0;
}

编译及反汇编过程如下:
tbag@:~/test$ gcc -O2 test.c -o likely
tbag@:~/test$ objdump -D likely > like
tbag@:~/test$        vi like

08048410 <ketty>:
8048410:        55                           push   %ebp
8048411:        89 e5                        mov    %esp,%ebp
8048413:        83 ec 18                     sub    $0x18,%esp
8048416:        8b 45 08                     mov    0x8(%ebp),%eax
------------------------------------------------------------
8048419:        85 c0                        test   %eax,%eax
804841b:        74 1d                        je     804843a <ketty+0x2a>
804841d:        83 c0 08                     add    $0x8,%eax

------------------------------------------------------------
8048420:        89 44 24 08                  mov    %eax,0x8(%esp)
8048424:        c7 44 24 04 20 85 04         movl   $0x8048520,0x4(%esp)
804842b:        08
804842c:        c7 04 24 01 00 00 00         movl   $0x1,(%esp)
8048433:        e8 f4 fe ff ff               call   804832c <__printf_chk@plt>
8048438:        c9                           leave  
8048439:        c3                           ret   
------------------------------------------------------------
804843a:        b0 62                        mov    $0x62,%al
------------------------------------------------------------
804843c:        eb e2                        jmp    8048420 <ketty+0x10>
804843e:        66 90                        xchg   %ax,%ax

note: 这里使用的是je,紧跟其后的是if语句块里的代码。

分析:
1.__builtin_expect(!!(x), 0)
        a) 使用的是jne跳转指令。
        b) 通过反汇编可以看出, 编译器将if语句块里面的代码放在else语句块之后。
也就是说,编译器将“预期"的小概率事件放在代码段的后面。

2.__builtin_expect(!!(x), 1)
        .) 跟__builtin_expect(!!(x), 0) 刚好相反。

这样做有什么优化吗?
我来说下我的个人观点:
.Cache
        根据局部性原理,CPU总是将最近访问主存的数据存储到cache中. 这样的话,大概率事件放在代码前面,命中
的机率就越大,也就是说将小概率事件放在代码段后面,就越能提高Cache的使用效率。
        以下是摘自ARM System Developer's Guide: Designing and System Software的一段对Cache的描述。

        cache is a small, fast array of memory placed between the processor and main memory. It
is a holding buffer that stores portions of recently referenced system memory. The processor
uses cache memory in preference to system memory whenever possible to increase average
system performance.

以上是我的一些认识。
  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP