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 branchprediction information. In general, you should prefer to useactual profile feedback for this (`-fprofile-arcs'), asprogrammers are notoriously bad at predicting how their programsactually perform. However, there are applications in which thisdata is hard to collect.The return value is the value of EXP, which should be an integralexpression. The value of C must be a compile-time constant. Thesemantics 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 weexpect `x' to be zero. Since you are limited to integralexpressions for EXP, you should use constructions such asif (__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
#include #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++;elsea--;printf ("%d\n", a);return 0;
}
执行 gcc -fprofile-arcs -O2 -S builtin_expect.c生成汇编
main:
// 程序入口
.LFB22:.cfi_startprocsubq $8, %rsp.cfi_def_cfa_offset 16movq 8(%rsi), %rdixorl %esi, %esimovl $10, %edxaddq $1, __gcov0.main(%rip)
// 调用 atoi() 函数call strtolcmpl $2, %eax
// --------------------------------------------------------
// 关键步骤:如果a等于2(这不太可能)则跳转,否则顺序执行,不跳转
// --------------------------------------------------------je .L6addq $1, __gcov0.main+16(%rip)leal -1(%rax), %esi
.L3:movl $.LC0, %edixorl %eax, %eaxcall printfaddq $1, __gcov0.main+24(%rip)xorl %eax, %eaxaddq $8, %rsp.cfi_remember_state.cfi_def_cfa_offset 8
// 返回退出ret
.L6:.cfi_restore_stateaddq $1, __gcov0.main+8(%rip)movl $3, %esijmp .L3
下面,将 unlikely() 替换为 likely(),重新生成汇编程序:
main:
// 程序入口
.LFB22:.cfi_startprocsubq $8, %rsp.cfi_def_cfa_offset 16movq 8(%rsi), %rdixorl %esi, %esimovl $10, %edxaddq $1, __gcov0.main(%rip)
// 调用 atoi()函数call strtolcmpl $2, %eax
// --------------------------------------------------
// 如果a等于2(这很可能)顺序执行,仅在 a != 2 时进行跳转
// ---------------------------------------------------jne .L2addq $1, __gcov0.main+8(%rip)movl $3, %esi
.L3:movl $.LC0, %edixorl %eax, %eaxcall printfaddq $1, __gcov0.main+24(%rip)xorl %eax, %eaxaddq $8, %rsp.cfi_remember_state.cfi_def_cfa_offset 8
// 返回退出ret
.L2:.cfi_restore_stateleal -1(%rax), %esiaddq $1, __gcov0.main+16(%rip)jmp .L3
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
