Linux内核中的c语言:likely()unlikely()

Posted rayylee

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核中的c语言: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()宏是用来对编译器进行提示,能够优化分支、提高代码执行效率。在include/linux/compiler.h的定义如下:

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

GCC 文档中对__builtin_expect()的解释如下:0

-- 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.

__builtin_expect函数的原型是:

long __builtin_expect (long exp, long c) 

它的语义是期待(expect)这么一个事实: exp == c,它的返回值就是 exp。因此不会影响分支判断。

它通过优化生成的汇编代码的排序,来提升处理器流水线的执行。使代码最可能的分支优先执行,而不执行任何jmp指令,减少指令跳转带来的性能上的下降。并且增加cache预读取命中率,增加程序的执行速度。

为了了解它是如何工作的,下面通过gcc将下面的c程序,生成汇编程序。

#include <stdio.h>
#include <stdlib.h>

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

int main(int argc, char *argv[])

   int a;

   /* 通过这种方式获取值,防止GCC进行优化 */
   a = atoi(argv[1]);

   if (unlikely (a == 2))
      a++;
   else
      a--;

   printf ("%d\\n", a);

   return 0;

执行 gcc -fprofile-arcs -O2 -S builtin_expect.c生成汇编

main:
// 程序入口
.LFB22:
	.cfi_startproc
	subq	$8, %rsp
	.cfi_def_cfa_offset 16
	movq	8(%rsi), %rdi
	xorl	%esi, %esi
	movl	$10, %edx
	addq	$1, __gcov0.main(%rip)
// 调用 atoi() 函数
	call	strtol
	cmpl	$2, %eax
// --------------------------------------------------------
// 关键步骤:如果a等于2(这不太可能)则跳转,否则顺序执行,不跳转
// --------------------------------------------------------
	je	.L6
	addq	$1, __gcov0.main+16(%rip)
	leal	-1(%rax), %esi
.L3:
	movl	$.LC0, %edi
	xorl	%eax, %eax
	call	printf
	addq	$1, __gcov0.main+24(%rip)
	xorl	%eax, %eax
	addq	$8, %rsp
	.cfi_remember_state
	.cfi_def_cfa_offset 8
// 返回退出
	ret
.L6:
	.cfi_restore_state
	addq	$1, __gcov0.main+8(%rip)
	movl	$3, %esi
	jmp	.L3

下面,将 unlikely() 替换为 likely(),重新生成汇编程序:

main:
// 程序入口
.LFB22:
	.cfi_startproc
	subq	$8, %rsp
	.cfi_def_cfa_offset 16
	movq	8(%rsi), %rdi
	xorl	%esi, %esi
	movl	$10, %edx
	addq	$1, __gcov0.main(%rip)
// 调用 atoi()函数
	call	strtol
	cmpl	$2, %eax
// --------------------------------------------------
// 如果a等于2(这很可能)顺序执行,仅在 a != 2 时进行跳转
// ---------------------------------------------------
	jne	.L2
	addq	$1, __gcov0.main+8(%rip)
	movl	$3, %esi
.L3:
	movl	$.LC0, %edi
	xorl	%eax, %eax
	call	printf
	addq	$1, __gcov0.main+24(%rip)
	xorl	%eax, %eax
	addq	$8, %rsp
	.cfi_remember_state
	.cfi_def_cfa_offset 8
// 返回退出
	ret
.L2:
	.cfi_restore_state
	leal	-1(%rax), %esi
	addq	$1, __gcov0.main+16(%rip)
	jmp	.L3

以上是关于Linux内核中的c语言:likely()unlikely()的主要内容,如果未能解决你的问题,请参考以下文章

Linux 内核源码中likely()和unlikely()

Linux 内核源码中likely()和unlikely()

Linux 内核中 likely 与 unlikely 的宏定义解析

解析Linux中的 likely 和 unlikely

第01节:Linux 内核中的 C 语言语法扩展

小小的 likely 背后却大有玄机!