likely unlikely()详解

本文介绍GCC内置函数__builtin_expect及likely、unlikely宏的作用与用法,帮助程序员优化分支预测,提高程序性能。

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

先理清一个概念:从作用上看   if(like(v)) == if(unlikely(v))==if(v)  那么我们为什么要用likely unlikely 命令呢,我们来看下面的详解


本文转自:http://www.cnblogs.com/PyshCliem/archive/2012/09/07/2674714.html


在include/linux/compiler.h中定义如下:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)

 

__builtin_expect是gcc内建的了, 就像bash的test之类的.看来了gcc的doc
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 value of c must be a compile-time constant. The semantics of the built-in are that it is expected that exp == c. For example:
  if (__builtin_expect (x, 0))
    foo ();
would indicate that we do not expect to call foo, since we expect x to be zero. Since you are limited to integral expressions for exp, you should use constructions such as
  if (__builtin_expect (ptr != NULL, 1))
    error ();
when testing pointer or floating-point values.

大致是说, 由于大部分程序员在分支预测方面做得很糟糕, 所以GCC提供了这个内建函数来帮助程序员处理分支预测, 优化程序.其第一个参数exp为一个整型表达式, 这个内建函数的返回值也是这个exp, 而c为一个编译常量, 这个函数的语义是:你期望exp表达式的值等于常量c, 从而GCC为你优化程序, 将符合这个条件的分支放在合适的地方.
因为这个程序只提供了整型表达式, 所以如果你要优化其他类型的表达式, 可以采用指针的形式.
  likely(x) 其实就是期望x的值为1
    if(unlikely(x)){
      foo();
    }
来测试条件的话, 我们就期望foo()函数执行, 所以该宏的名字用likely也就是可能来表示.
  unlikely(x)望表达式x的值为0, 从而如果我们用
    if(unlikely(x)){
      bar();
    }
来测试条件的话, 我们就不期望bar()函数执行, 所以该宏的名字用unlikely也就是不太可能来表示.

上面这两条语句都是x为1的时候执行.其实也就一句话:if()语句你照用, 跟以前一样, 只是如果你觉得if()是1 的可能性非常大的时候, 就在表达式的外面加一个likely(), 如果可能性非常小(比如几率非常小), 就用unlikely()包裹上.你也可以完全不用likely(), unlikely().
注意: macro的定义x有括号. 这应该也是c的基础了, 不过我们一般还是会疏忽的. 这就说明x可以用表达式了, 于是likely也就可以test任意类型的东西了.

总之:
__builtin_expect() 是GCC(version >= 2.96)提供给程序员使用的, 目的是将“分支转移”的信息提供给编译器, 这样编译器可以对代码进行优化, 以减少指令跳转带来的性能下降.
__builtin_expect((x), 1) 表示 x 的值为真的可能性更大;
__builtin_expect((x), 0) 表示 x 的值为假的可能性更大.
使用 likely(), 执行if后面的语句的机会更大,使用unlikely(), 执行else后面的语句的机会更大.


<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 ; 预测不跳转 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值