在Linux内核中likely和unlikely函数有两种(只能两者选一)实现方式,它们的实现原理稍有不同,但作用是相同的,下面将结合linux-2.6.38.8版本的内核代码来进行讲解。

1、对__builtin_expect的封装

它们的源代码如下:

  1. /* linux-2.6.38.8/include/linux/compiler.h */
  2. # define likely(x)  __builtin_expect(!!(x), 1)
  3. # 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,如下面的例子:

  1. #include <stdio.h>
  2. int main(void)
  3. {
  4. int a;
  5. scanf("%d", &a);
  6. if(__builtin_expect(a, 4))
  7. printf("if: a = %d\n", a);
  8. else
  9. printf("else: a = %d\n", a);
  10. return 0;
  11. }
<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\n", a);
else
printf("else: a = %d\n", a); return 0;
}
</span>

分别输入整数0到4来进行5次测试,它们的输出分别为:

  1. else: a = 0
  2. if: a = 1
  3. if: a = 2
  4. if: a = 3
  5. 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__函数

它们的源代码如下:

  1. /* linux-2.6.38.8/include/linux/compiler.h */
  2. # ifndef likely
  3. #  define likely(x) (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
  4. # endif
  5. # ifndef unlikely
  6. #  define unlikely(x)   (__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0))
  7. # 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,如下面的例子:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define VALUE 5
  4. int main(void)
  5. {
  6. char *ptr = NULL;
  7. int num, count;
  8. ptr = malloc(20);
  9. num = __builtin_constant_p(ptr) ? 20 : 20 + 10;
  10. printf("num = %d\n", num);
  11. free(ptr);
  12. count = __builtin_constant_p(VALUE) ? 20 + VALUE : 10;
  13. printf("count = %d\n", count);
  14. return 0;
  15. }
<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\n", num);
free(ptr); count = __builtin_constant_p(VALUE) ? 20 + VALUE : 10;
printf("count = %d\n", count); return 0;
}
</span>

例子的输出结果:

  1. num = 30
  2. 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__的实现

  1. /* linux-2.6.38.8/include/linux/compiler.h */
  2. #define __branch_check__(x, expect) ({                  \
  3. int ______r;                    \
  4. static struct ftrace_branch_data        \
  5. __attribute__((__aligned__(4)))     \
  6. __attribute__((section("_ftrace_annotated_branch"))) \
  7. ______f = {             \
  8. .func = __func__,           \
  9. .file = __FILE__,           \
  10. .line = __LINE__,           \
  11. };                      \
  12. ______r = likely_notrace(x);            \
  13. ftrace_likely_update(&______f, ______r, expect); \
  14. ______r;                    \
  15. })
<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的值。

(linux)likely和unlikely函数的更多相关文章

  1. Linux下利用ioctl函数获取网卡信息

    linux下的ioctl函数原型如下: #include <sys/ioctl.h> int ioctl(int handle, int cmd, [int *argc, int argv ...

  2. module_init宏解析 linux驱动的入口函数module_init的加载和释放

    linux驱动的入口函数module_init的加载和释放 http://blog.csdn.net/zhandoushi1982/article/details/4927579 void free_ ...

  3. 【C/C++】Linux下使用system()函数一定要谨慎

    [C/C++]Linux下使用system()函数一定要谨慎 http://my.oschina.net/renhc/blog/53580 曾经的曾经,被system()函数折磨过,之所以这样,是因为 ...

  4. linux C之access函数 (20

    http://blog.sina.com.cn/s/blog_6a1837e90100uh5d.html linux C之access函数 (20access():判断是否具有存取文件的权限 相关函数 ...

  5. linux c语言 select函数用法

    linux c语言 select函数用法 表头文件 #i nclude<sys/time.h> #i nclude<sys/types.h> #i nclude<unis ...

  6. c/c++ linux 进程 fork wait函数

    linux 进程 fork wait函数 fork:创建子进程 wait:父进程等待子进程结束,并销毁子进程,如果父进程不调用wait函数,子进程就会一直留在linux内核中,变成了僵尸进程. for ...

  7. Linux编程之fork函数

    在Linux中,fork函数的功能就是在一个进程中创建一个新的进程,当前调用fork函数的进程就是产生的新进程的父进程,新进程在以下也称为子进程.在新进程生成之后就会在系统中开始执行. 函数原型:pi ...

  8. linux i2c 的通信函数i2c_transfer在什么情况下出现错误

    问题: linux i2c 的通信函数i2c_transfer在什么情况下出现错误描述: linux i2c设备驱动 本人在写i2c设备驱动的时候使用i2c transfer函数进行通信的时候无法进行 ...

  9. Linux下系统时间函数、DST等相关问题总结(转)

    Linux下系统时间函数.DST等相关问题总结 下面这个结构体存储了跟时区相关的位移量(offset)以及是否存在DST等信息,根据所在的时区信息,很容易找到系统时间与UTC时间之间的时区偏移,另外根 ...

  10. 关于linux中的system函数

    Linux下使用system()函数一定要谨慎 https://blog.csdn.net/senen_wakk/article/details/51496322 system()正确应用 https ...

随机推荐

  1. 【DFS求树的最大二分匹配+输入外挂】HDU 6178 Monkeys

    http://acm.hdu.edu.cn/showproblem.php?pid=6178 [题意] 给定一棵有n个结点的树,现在有k个猴子分布在k个结点上,我们可以删去树上的一些边,使得k个猴子每 ...

  2. Spoj-DRUIDEOI Fata7y Ya Warda!

    Fata7y Ya Warda! Druid (AKA Amr Alaa El-Deen) and little EOIers have finished their training and the ...

  3. day1之校花网小试牛刀

    一 利用生成器来完成爬去校花网视频 import requests import re import os import hashlib import time DOWLOAD_PATH=r'D:\D ...

  4. 玩转css样式选择器----当父元素有多个子元素时选中第一个

  5. Redis对象的设计与实现

    一.Redis对象结构Redis中的每个对象都由一个redisObject结构表示: typedef struct redisObject { unsigned type;//类型 unsigned ...

  6. 2018 ICPC 徐州网络预赛 Features Track (STL map pair)

    [传送门]https://nanti.jisuanke.com/t/31458 [题目大意]有N个帧,每帧有K个动作特征,每个特征用一个向量表示(x,y).两个特征相同当且仅当他们在不同的帧中出现且向 ...

  7. P1067 多项式输出 (模拟)

    题目描述 一元nn次多项式可用如下的表达式表示: 其中,a_i x^i 称为i次项,ai​ 称为i次项的系数.给出一个一元多项式各项的次数和系数,请按照如下规定的格式要求输出该多项式: 多项式中自变量 ...

  8. PLsql/Oracle数据库中没有scott账户,如何创建并解锁

    当然首先要装好Oracle 11g 然后还要有sqlplus,这个在Oracle11g的时候应该都会配上的 进入正题,如果oracle/plsql没scott账户,如何创建 先找到Oracle安装目录 ...

  9. [转] windows下Svn服务器之必须提交修改注释篇

    1. 强制添加注释信息 找到Respositories目录下对应项目里的hooks目录下建立pre-commit.bat文件,复制如下内容: @echo off set SVNLOOK="C ...

  10. validation set以及cross validation的常见做法

    如果给定的样本充足,进行模型选择的一种简单方法是随机地将数据集切分成三部分,分为训练集(training set).验证集(validation set)和测试集(testing set).训练集用来 ...