Linux 内核中 likely 与 unlikely 的宏定义解析

在 2.6 内核中,随处可以见到 likely() 和 unlikely() 的身影,那么为什么要用它们?它们之间有什么区别?

首先要明确:

     if(likely(value)) 等价于 if(value)

     if(unlikely(value)) 也等价于 if(value)

也就是说 likely() 和 unlikely() 从阅读和理解代码的角度来看,是一样的!!!

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。

__builtin_expect((x),1) 表示 x 的值为真的可能性更大;

__builtin_expect((x),0) 表示 x 的值为假的可能性更大。

也就是说,使用 likely() ,执行 if 后面的语句 的机会更大,使用unlikely(),执行else 后面的语句的机会更大。

例如下面这段代码,作者就认为 prev 不等于 next 的可能性更大,

if (likely(prev != next)) {
       next->timestamp = now;
        ...
} else {
        ...;
 }

通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。

另外内核2.6.31.5中likely和unlikely还有一种定义:
# 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

举个例子(内核版本2.6.22.6):/kernel/shed.c中有一段:

if (likely(!active_balance)) {
/* We were unbalanced, so reset the balancing interval */
sd->balance_interval = sd->min_interval;
} else {
/*
* If we‘ve begun active balancing, start to back off. This
* case may not be covered by the all_pinned logic if there
* is only 1 task on the busy runqueue (because we don‘t call
* move_tasks).
*/
if (sd->balance_interval max_interval)
sd->balance_interval *= 2;
}

编译过程中,会将if后面{}里的内容编译到前面,else 后面{}里的内容编译到后面。若将likely换成unlikely 则正好相反。

总之,likely与unlikely互换或不用都不会影响程序的正确性。但可能会影响程序的效率。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-21 13:01:41

Linux 内核中 likely 与 unlikely 的宏定义解析的相关文章

《linux 内核完全剖析》 笔记 CODE_SPACE 宏定义分析

在memory.c里面,遇到一个宏定义,如下: #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < current->start_code + current->end_code) 看的第一眼,不知道,第二眼,还是不知道,纠结了半天还是不知道. 睡了一晚,今天早上再看,嘿嘿,居然看懂了... 这个宏定义用于判断给定的addr线性地址是否位于当前进程的代码段中. 4095 = 0xFFF; addr+4095的作用是将位于0~4095

《linux 内核全然剖析》 笔记 CODE_SPACE 宏定义分析

在memory.c里面.遇到一个宏定义,例如以下: #define CODE_SPACE(addr) ((((addr)+4095)&~4095) < current->start_code + current->end_code) 看的第一眼,不知道.第二眼.还是不知道.纠结了半天还是不知道. 睡了一晚,今天早上再看,嘿嘿,竟然看懂了... 这个宏定义用于推断给定的addr线性地址是否位于当前进程的代码段中. 4095 = 0xFFF; addr+4095的作用是将位于0~40

Linux内核中_IO,_IOR,_IOW,_IOWR宏的用法

#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0) #define _IOR(type,nr,size)    _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOW(type,nr,size)    _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size))) #define _IOWR(type,nr,size)  

(六)linux内核中的offsetof与container_of宏

参考: offsetof与container_of宏[总结] #define offsetof(type, member) (size_t)&(((type*)0)->member) #define container_of(ptr, type, member) ({ const typeof( ((type *)0)->member ) *__mptr = (ptr); (type *)( (char *)__mptr - offsetof(type,member) );}) 原文地

(十)Linux内核中的常用宏container_of

Container_of在Linux内核中是一个常用的宏,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址. Container_of的定义如下: #define OffsetOf(type, member) ((unsigned long) &(((type *)0)->member)) #define container_of(p, type, member)  ((type *) ((char *)(p) - O

第01节:Linux 内核中的 C 语言语法扩展

1.1 Linux 内核驱动中的奇怪语法 大家在看一些 GNU 开源软件,或者阅读 Linux 内核.驱动源码时会发现,在 Linux 内核源码中,有大量的 C 程序看起来"怪怪的".说它是C语言吧,貌似又跟教材中的写法不太一样:说它不是 C 语言呢,但是这些程序确确实实是在一个 C 文件中.此时,你肯定怀疑你看到的是一个"假的 C 语言"! 比如,下面的宏定义: #define mult_frac(x, numer, denom)( { typeof(x) quo

linux内核中的宏ffs(x)【转】

转自:https://www.cnblogs.com/fengeryi/p/3449720.html linux内核中ffs(x)宏是平台相关的宏,在arm平台,该宏定义在 arch/arm/include/asm/bitops.h #define ffs(x) ({ unsigned long __t = (x); fls(__t & -__t); }) static inline int fls(int x) { int ret; if (__builtin_constant_p(x)) r

Linux内核中的jiffies及其作用介绍及jiffies等相关函数详解

在LINUX的时钟中断中涉及至二个全局变量一个是xtime,它是timeval数据结构变量,另一个则是jiffies,首先看timeval结构struct timeval{time_t tv_sec; /***second***/susecond_t tv_usec;/***microsecond***/}到底microsecond是毫秒还是微秒?? 1秒=1000毫秒(3个零),1秒=1000 000微秒(6个零),1秒=1000 000 000纳秒(9个零),1秒=1000 000 000

向linux内核中添加外部中断驱动模块

本文主要介绍外部中断驱动模块的编写,包括:1.linux模块的框架及混杂设备的注册.卸载.操作函数集.2.中断的申请及释放.3.等待队列的使用.4.工作队列的使用.5.定时器的使用.6.向linux内核中添加外部中断驱动模块.7.完整驱动程序代码.linux的内核版本为linux2.6.32.2. 一.linux模块的框架以及混杂设备相关知识 1.内核模块的框架如下图所示,其中module_init()(图中有误,不是modules_init)只有在使用insmod命令手动加载模块时才会被调用,