在Linux内核中likely和unlikely函数有两种(只能两者选一)实现方式,它们的实现原理稍有不同,但作用是相同的,下面将结合linux-2.6.38.8版本的内核代码来进行讲解。
1、对__builtin_expect的封装
它们的源代码如下:
- /* linux-2.6.38.8/include/linux/compiler.h */
- # define likely(x) __builtin_expect(!!(x), 1)
- # define unlikely(x) __builtin_expect(!!(x), 0)
<span style="font-family:SimHei;font-size:18px;">/* linux-2.6.38.8/include/linux/compiler.h */ # define likely(x) __builtin_expect(!!(x), 1) # define unlikely(x) __builtin_expect(!!(x), 0)</span>
__builtin_expect 是GCC的内置函数,用来对选择语句的判断条件进行优化,常用于一个判断条件经常成立(如likely)或经常不成立(如unlikely)的情况。
__builtin_expect的函数原型为long __builtin_expect (long exp, long c),返回值为完整表达式exp的值,它的作用是期望表达式exp的值等于c(注意,如果exp == c条件成立的机会占绝大多数,那么性能将会得到提升,否则性能反而会下降)。
在普通的应用程序中也可以使用__builtin_expect,如下面的例子:
- #include <stdio.h>
- int main(void)
- {
- int a;
- scanf("%d", &a);
- if(__builtin_expect(a, 4))
- printf("if: a = %d ", a);
- else
- printf("else: a = %d ", a);
- return 0;
- }
<span style="font-family:SimHei;font-size:18px;">#include <stdio.h> int main(void) { int a; scanf("%d", &a); if(__builtin_expect(a, 4)) printf("if: a = %d ", a); else printf("else: a = %d ", a); return 0; } </span>
分别输入整数0到4来进行5次测试,它们的输出分别为:
- else: a = 0
- if: a = 1
- if: a = 2
- if: a = 3
- if: a = 4
<span style="font-family:SimHei;font-size:18px;">else: a = 0 if: a = 1 if: a = 2 if: a = 3 if: a = 4 </span>
注意,在上例中只有输入整数0的时候才执行else后的打印语句,也就是说__builtin_expect(a, 4)函数的值就是表达式a的值。
记住,它们只是用来提升性能的优化手段,并不会改变原来表达式的值。
2、使用__branch_check__函数
它们的源代码如下:
- /* linux-2.6.38.8/include/linux/compiler.h */
- # ifndef likely
- # define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
- # endif
- # ifndef unlikely
- # define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0))
- # endif
<span style="font-family:SimHei;font-size:18px;">/* linux-2.6.38.8/include/linux/compiler.h */ # ifndef likely # define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1)) # endif # ifndef unlikely # define unlikely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0)) # endif</span>
(1)、先使用内置函数__builtin_constant_p忽略表达式x为常量的情况 (这而的!!(x)为什么不写成x呢?)
__builtin_constant_p也是GCC的内置函数,函数原型为int __builtin_constant_p(exp),用于判断表达式exp在编译时是否是一个常量,如果是则函数的值为整数1,否则为0,如下面的例子:
- #include <stdio.h>
- #include <stdlib.h>
- #define VALUE 5
- int main(void)
- {
- char *ptr = NULL;
- int num, count;
- ptr = malloc(20);
- num = __builtin_constant_p(ptr) ? 20 : 20 + 10;
- printf("num = %d ", num);
- free(ptr);
- count = __builtin_constant_p(VALUE) ? 20 + VALUE : 10;
- printf("count = %d ", count);
- return 0;
- }
<span style="font-family:SimHei;font-size:18px;">#include <stdio.h> #include <stdlib.h> #define VALUE 5 int main(void) { char *ptr = NULL; int num, count; ptr = malloc(20); num = __builtin_constant_p(ptr) ? 20 : 20 + 10; printf("num = %d ", num); free(ptr); count = __builtin_constant_p(VALUE) ? 20 + VALUE : 10; printf("count = %d ", count); return 0; } </span>
例子的输出结果:
- num = 30
- count = 25
<span style="font-family:SimHei;font-size:18px;">num = 30 count = 25 </span>
例子中的ptr为指针变量,所以__builtin_constant_p(ptr)的值为0,num的值为30。
(2)、函数__branch_check__的实现
- /* linux-2.6.38.8/include/linux/compiler.h */
- #define __branch_check__(x, expect) ({
- int ______r;
- static struct ftrace_branch_data
- __attribute__((__aligned__(4)))
- __attribute__((section("_ftrace_annotated_branch")))
- ______f = {
- .func = __func__,
- .file = __FILE__,
- .line = __LINE__,
- };
- ______r = likely_notrace(x);
- ftrace_likely_update(&______f, ______r, expect);
- ______r;
- })
<span style="font-family:SimHei;font-size:18px;">/* linux-2.6.38.8/include/linux/compiler.h */ #define __branch_check__(x, expect) ({ int ______r; static struct ftrace_branch_data __attribute__((__aligned__(4))) __attribute__((section("_ftrace_annotated_branch"))) ______f = { .func = __func__, .file = __FILE__, .line = __LINE__, }; ______r = likely_notrace(x); ftrace_likely_update(&______f, ______r, expect); ______r; })</span>
使用它来检查判断条件并记录likely判断的预测信息,之后根据预测信息进行相应的优化以提升性能。
函数__branch_check__的返回值为______r的值,也就是参数x的值。