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()