linux中 likely与unlikely

本文介绍了Linux内核中likely与unlikely宏的作用及实现原理,详细解释了这些宏如何帮助编译器进行分支预测优化,从而提高代码执行效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

看内核时总遇到if(likely( )){}或是if(unlikely( ))这样的语句,最初不解其意,现在有所了解,所以也想介绍一下。

likely() 与 unlikely()是内核(我看的是2.6.22.6版本,2.6的版本应该都有)中定义的两个宏。位于/include/linux/compiler.h中,具体定义如下:

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

__builtin_expect是gcc(版本>=2.96,网上写的,我没验证过)中提供的一个预处理命令(这个名词也是网上写的,我想叫函数更好些),有利于代码优化。gcc(version 4.4.0)具体定义如下:
long __builtin_expect (long exp, long c) [Built-in Function]
注解为:
You may use __builtin_expect to provide the compiler with branch prediction information. In general, you should prefer to use actual profile feedback for this (‘-fprofile-arcs’), as programmers are notoriously bad at predicting how their programs actually perform. However, there are applications in which this data is hard to collect.The return value is the value of exp, which should be an integral expression. The semantics of the built-in are that it is expected that exp == c.

它的意思是:我们可以使用这个函数人为告诉编绎器一些分支预测信息“exp==c” 是“很可能发生的”。

#define likely(x) __builtin_expect(!!(x), 1)

也就是说明x==1是“经常发生的”或是“很可能发生的”。

使用likely ,执行if后面语句的可能性大些,编译器将if{}是的内容编译到前面, 使用unlikely ,执行else后面语句的可能性大些,编译器将else{}里的内容编译到前面。这样有利于cpu预取,提高预取指令的正确率,因而可提高效率。

举个例子(内核版本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互换或不用都不会影响程序的正确性。但可能会影响程序的效率。


if(likely(foo))  //认为foo通常为1
if(unlikely(foo)) //认为foo通常为0

感谢各位光顾!

不知道有没有写清楚,望指正!

疑惑:

为什么likely或是unlikely要定义成__builtin_expect(!!(x), 1),而不直接用__builtin_expect(x, 1)?" !!(x) "与" x "有什么不同?


另外内核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

转:http://blog.youkuaiyun.com/tommy_wxie/article/details/7384641
<think>好的,我现在要解决用户关于likelyunlikely在编程中的含义及用法的问题。首先,我需要回忆自己之前学过的相关知识,并结合提供的引用内容来整理答案。 用户提到的这几个引用中,引用[1]提到了`__builtin_expect(EXP, N)`的用法,说明likelyunlikely是基于这个内置函数的宏,用来告诉编译器条件判断的结果更可能是什么。引用[2]指出这是Linux内核中的优化手段,用于条件分支的性能优化。引用[3]则具体解释了宏的用法,分别标记条件为真或假的可能性,帮助编译器优化代码布局。 首先,我需要解释likelyunlikely的基本含义,它们的作用是给编译器提供分支预测的信息,从而生成更高效的机器码。比如,使用likely告诉编译器这个条件很可能为真,那么编译器可能会将更可能执行的代码放在前面,减少跳转带来的性能损耗。 接下来,应该详细说明它们的实现方式,即通过GCC的`__builtin_expect`函数,将条件的期望值传递给编译器。例如,`likely(x)`实际上就是`__builtin_expect(!!(x), 1)`,而unlikely则是期望值为0。 然后,需要举例说明在代码中如何使用这两个宏,比如在条件判断语句中包裹条件表达式,帮助编译器优化分支预测。例如,在错误处理或者罕见情况的条件判断时使用unlikely,而在正常流程的条件判断时使用likely。 同时,应该提到使用这两个宏的注意事项,比如不要滥用,只在确实有明确倾向性的分支中使用,否则可能导致性能下降。另外,需要指出它们主要适用于GCC编译器,其他编译器可能有不同的实现方式。 最后,根据用户提供的示例中的结构,生成相关问题,比如优化原理、适用场景、其他优化方法的对比等。 检查是否有遗漏的部分,比如是否涵盖了定义、实现原理、代码示例、注意事项,以及相关问题是否合理。确保引用的内容被正确标注,比如引用[^2]对应Linux内核的应用,引用[^3]对应具体用法示例。 现在需要将这些内容组织成结构清晰的回答,符合用户要求的格式,使用中文,正确使用LaTeX格式,比如内联公式用$符号,独立公式用$$。不过当前问题可能不涉及数学公式,所以可能不需要使用数学表达式,但需要保持结构清晰,分点说明。 总结,回答的结构应该是: 1. 定义和作用 2. 实现原理(基于GCC内置函数) 3. 具体用法示例 4. 使用注意事项 5. 相关问题 确保每个部分都参考了提供的引用内容,并正确标注引用标识。同时,生成的代码示例需要用反引号包裹,正确显示语法高亮。</think>### likelyunlikely的含义及用法 #### 1. 定义作用 `likely`和`unlikely`是编程中用于优化条件分支性能的宏。它们通过向编译器提供分支条件的概率信息,帮助编译器生成更高效的机器码。例如: - `likely(condition)`:表示`condition`为真的概率很大。 - `unlikely(condition)`:表示`condition`为假的概率很大。 #### 2. 实现原理 这两个宏基于GCC的`__builtin_expect`内置函数实现,语法为: ```c #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) ``` - `__builtin_expect(EXP, N)`:提示编译器`EXP == N`的概率很高[^1]。 - `!!(x)`:将任意表达式转换为布尔值(0或1),确保逻辑一致性。 #### 3. 代码示例 **场景1**:处理常见路径(使用`likely`) ```c if (likely(data_valid)) { // 数据有效时的高频操作 } else { // 错误处理的低频路径 } ``` **场景2**:处理罕见错误(使用`unlikely`) ```c if (unlikely(file_open_failed)) { // 文件打开失败的异常处理 } else { // 正常文件操作 } ``` #### 4. 注意事项 1. **适用场景**:明确分支概率时使用(如错误处理、核心循环)。 2. **编译器依赖**:主要针对GCC/Clang,其他编译器需适配(如MSVC的`__assume`)。 3. **避免滥用**:错误预测可能降低CPU流水线效率。 4. **内核应用**:广泛用于Linux内核(如网络栈、文件系统)[^2]。 #### 5. 性能优化机制 编译器会根据提示调整: - **代码布局**:高频路径紧邻无跳转指令,减少流水线冲刷。 - **分支预测**:生成`cmov`等无分支指令(概率极高时)。 - 汇编对比示例: ```asm ; 使用likely时 test %eax, %eax jne .Lhigh_prob_path ; 预测不跳转 ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值