上篇文章:ARM 嵌入式 编译系列 4 – GCC 编译属性 __read_mostly 介绍
下篇文章: ARM 嵌入式 编译系列 4.2 – GCC 链接规范 extern “C“ 介绍
GCC likely与unlikely 介绍
likely 和 unlikely 是GCC编译器提供的一种代码优化特性,这两个宏用于告诉编译器某个条件判断的结果是真还是假的可能性更大。
在编写代码时,如果我们已经知道某个条件判断的结果大部分情况下都是真或者假,就可以使用likely
和unlikely
宏来进行标记,编译器会根据这个提示进行优化,使得代码运行更高效。
以下是这两个宏的定义:
#define likely(x) __builtin_expect((x), 1)
#define unlikely(x) __builtin_expect((x), 0)
这里的__builtin_expect
是 GCC
的内建函数,用于提供编译器关于表达式结果的预期值。
以下是如何在代码中使用这两个宏的:
if (likely(x > 0)) {
// 大部分情况下,x都大于0
} else {
// 一般情况下,这里的代码不会执行
}
if (unlikely(err != 0)) {
// 一般情况下,err都等于0,所以这里的代码很少执行
}
在这两个示例中,likely
和unlikely
宏标记了条件判断的结果预期,这样编译器在生成机器代码时,可以将更可能执行的代码放在内存中的靠前位置,从而提高代码执行效率。
linux 内核中的 likely/unlikely
这两个宏在内核中的定义如下:
# define likely(x) __builtin_expect(!!(x), 1)
# define unlikely(x) __builtin_expect(!!(x), 0)
可见这里使用了 gcc 的内建函数 __builtin_expect()
。
__builtin_expect (long exp, long c)
函数:
该函数用来引导 gcc 进行条件分支预测。在一条指令执行时,由于流水线的作用,CPU可以同时完成下一条指令的取指,这样可以提高CPU的利用率。在执行条件分支指令时,CPU也会预取下一条执行,但是如果条件分支的结果为跳转到了其他指令,那 CPU 预取的下一条指令就没用了,这样就降低了流水线的效率。
另外,跳转指令相对于顺序执行的指令会多消耗 CPU 时间,如果可以尽可能不执行跳转,也可以提高 CPU 性能。
使用 __builtin_expect (long exp, long c)
函数可以帮助 gcc 优化程序编译后的指令序列,使汇编指令尽可能的顺序执行,从而提高 CPU 预取指令的正确率和执行效率。
__builtin_expect(exp, c)
接受两个 long
型的参数,用来告诉 gcc:exp==c
的可能性比较大。
例如,__builtin_expect(exp, 1)
表示程序执行过程中,exp 取到 1 的可能性比较大。该函数的返回值为 exp 自身。
内核中 likely(x)
和 unlikely(x)
宏:
知道 __builtin_expect()
函数的作用之后,我们就知道内核中 likely(x)
和 unlikely(x)
宏的作用了,通过 likely(x)
和 unlikely(x)
宏定义,我们可以得出他们的作用:
- likely(x) 等价于 x,即
if (likely(x))
等价于if (x)
,但是它告诉 gcc,x 取 1 的可能性比较大; - unlikely(x) 等价于 x,即
if (unlikely(x))
等 价于if (x)
,但是它告诉 gcc,x 取 0 的可能性比较大。
上篇文章:ARM 嵌入式 编译系列 4 – GCC 编译属性 __read_mostly 介绍
下篇文章: ARM 嵌入式 编译系列 4.2 – GCC 链接规范 extern “C“ 介绍