(linux)likely和unlikely函数
在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\n", a);
- else
- printf("else: a = %d\n", 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\n", a);
else
printf("else: a = %d\n", 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\n", num);
- free(ptr);
- count = __builtin_constant_p(VALUE) ? 20 + VALUE : 10;
- printf("count = %d\n", 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\n", num);
free(ptr); count = __builtin_constant_p(VALUE) ? 20 + VALUE : 10;
printf("count = %d\n", 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的值。
(linux)likely和unlikely函数的更多相关文章
- Linux下利用ioctl函数获取网卡信息
linux下的ioctl函数原型如下: #include <sys/ioctl.h> int ioctl(int handle, int cmd, [int *argc, int argv ...
- module_init宏解析 linux驱动的入口函数module_init的加载和释放
linux驱动的入口函数module_init的加载和释放 http://blog.csdn.net/zhandoushi1982/article/details/4927579 void free_ ...
- 【C/C++】Linux下使用system()函数一定要谨慎
[C/C++]Linux下使用system()函数一定要谨慎 http://my.oschina.net/renhc/blog/53580 曾经的曾经,被system()函数折磨过,之所以这样,是因为 ...
- linux C之access函数 (20
http://blog.sina.com.cn/s/blog_6a1837e90100uh5d.html linux C之access函数 (20access():判断是否具有存取文件的权限 相关函数 ...
- linux c语言 select函数用法
linux c语言 select函数用法 表头文件 #i nclude<sys/time.h> #i nclude<sys/types.h> #i nclude<unis ...
- c/c++ linux 进程 fork wait函数
linux 进程 fork wait函数 fork:创建子进程 wait:父进程等待子进程结束,并销毁子进程,如果父进程不调用wait函数,子进程就会一直留在linux内核中,变成了僵尸进程. for ...
- Linux编程之fork函数
在Linux中,fork函数的功能就是在一个进程中创建一个新的进程,当前调用fork函数的进程就是产生的新进程的父进程,新进程在以下也称为子进程.在新进程生成之后就会在系统中开始执行. 函数原型:pi ...
- linux i2c 的通信函数i2c_transfer在什么情况下出现错误
问题: linux i2c 的通信函数i2c_transfer在什么情况下出现错误描述: linux i2c设备驱动 本人在写i2c设备驱动的时候使用i2c transfer函数进行通信的时候无法进行 ...
- Linux下系统时间函数、DST等相关问题总结(转)
Linux下系统时间函数.DST等相关问题总结 下面这个结构体存储了跟时区相关的位移量(offset)以及是否存在DST等信息,根据所在的时区信息,很容易找到系统时间与UTC时间之间的时区偏移,另外根 ...
- 关于linux中的system函数
Linux下使用system()函数一定要谨慎 https://blog.csdn.net/senen_wakk/article/details/51496322 system()正确应用 https ...
随机推荐
- [HDU-4825] Xor-Sum (01字典树)
Problem Description Zeus 和 Prometheus 做了一个游戏,Prometheus 给 Zeus 一个集合,集合中包含了N个正整数,随后 Prometheus 将向 Zeu ...
- Spring的@Configuration来代替xml配置
一. Xml配置法 下面是一个典型的spring配置文件(application-config.xml): [xml] view plain copy <beans> <bean i ...
- LA 3263 平面划分
Little Joey invented a scrabble machine that he called Euler, after the great mathematician. In his ...
- 济南学习 Day 5 T2 pm
逆欧拉函数(arc)题目描述:已知phi(N),求N.输入说明:两个正整数,分别表示phi(N)和K.输出说明:按升序输出满足条件的最小的K个N.样例输入:8 4杨丽输出:15 16 20 24数据范 ...
- 球形空间产生器 BZOJ 1013
球形空间产生器 [问题描述] 有一个球形空间产生器能够在n维空间中产生一个坚硬的球体.现在,你被困在了这个n维球体中,你只知道球面上n+1个点的坐标,你需要以最快的速度确定这个n维球体的球心坐标,以便 ...
- CKeditor如何实现图片上传功能
http://makaiyuan.blog.51cto.com/5819595/1049521 如何在数据库中导入excel文件内的数据:http://jingyan.baidu.com/album/ ...
- centos升级glibc(升级到 2.17版)
https://blog.csdn.net/wyl9527/article/details/78256066
- 2018 ICPC 沈阳网络赛预赛 Supreme Number(找规律)
[传送门]https://nanti.jisuanke.com/t/31452 [题目大意]:给定一个数字(最大可达10100),现在要求不超过它的最大超级质数.超级质数定义:对于一个数,把它看成数字 ...
- Weblogic调优
优化说明: 一.Weblogic服务程序设置: 1.设置JDK内存: 修改weblogic\user_projects\domains\base_domain\bin下的setDomainEnv.cm ...
- ubuntu harddisk uuid already exists
就是virtualbox下先用u盘启动的虚拟机,把U盘的vhdk文件拷贝到本机,然后再启动,就有问题,提示什么uuid already exist 找了半天,网上基本都是说windows下如何用的.. ...