Linux中的likely()和unlikely()

likely()与unlikely()在2.6内核中,随处可见,那为什么要用它们?它们之间有什么区别呢?
首先明确:
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)

这里的__built_expect()函数是gcc(version >= 2.96)的内建函数,提供给程序员使用的,目的是将”分支转移”的信息提供给编译器,这样编译器对代码进行优化,以减少指令跳转带来的性能下降。
__buildin_expect((x), 1)表示x的值为真的可能性更大.
__buildin_expect((x), 0)表示x的值为假的可能性更大.
也就是说,使用likely(),执行if后面的语句的机会更大,使用unlikely(),执行else后面的语句机会更大一些。通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着后面的代码,从而减少指令跳转带来的性能上的下降。
比如 :

if (likely(a>b)) {
  fun1();
}
if (unlikely(a>b){
  fun2();
}

  这里就是程序员可以确定 a>b 在程序执行流程中出现的可能相比较大,因此运用了likely()告诉编译器将fun1()函数的二进制代码紧跟在前面程序的后面,这样就使cache在预取数据时就可以将fun1()函数的二进制代码拿到cache中。这样,也就添加了cache的命中率。
  同样的,unlikely()的作用就是告诉编译器,a>b在程序执行流程中出现的可能性较小时执行fun2()
  咱们不用对likely和unlikely感到迷惑,须要知晓的就是 if(likely(a>b))if(a>b)在功能上是等价的,同样 if(unlikely(a>b))if (a>b)在功能上是等价的。
  比如下面的代码:

#include <stdio.h>
#define unlikely(x) __builtin_exp ect(!!(x), 0)
#define likely(x) __builtin_exp ect(!!(x), 1)

int main()
{
  int a=2,b=4;
  if(unlikely(a>b)) {
      printf("in the unlikely,is not your exp ecting!\n");
  } else {
      printf("in the unlikely, is your exp ecting\n");
  }
  if(likely(a>b)) {
      printf("in the likely, is your exp ecting\n");
  }
  return 0;
}

执行结果:

in the unlikely,is not your exp ecting!
in the likely, is your exp ecting

  总之,likely和unlikely的功能就是添加 cache的命中率,提高系统执行速度

本文转自:http://blog.youkuaiyun.com/rxjhweike/archive/2011/02/22/6199751.aspx

<think>好的,用户想了解C语言中的likelyunlikely宏定义及其优化提示。首先,我得回忆一下C语言的关键字,确定这两个是否是原生关键字。根据我的知识,C标准里并没有likelyunlikely作为关键字,所以它们应该是通过宏实现的。 接下来,我需要查阅相关资料,特别是Linux内核或一些优化相关的代码,因为之前好像听说过这两个宏在内核中被广泛使用。根据用户提供的引用[1]、[2][4],likelyunlikely是使用GCC的__builtin_expect内建函数来定义的。它们的定义通常是这样的:#define likely(x) __builtin_expect(!!(x), 1),这样告诉编译器条件x更可能为真,反之unlikely则是预期为假。 然后,我需要解释这两个宏的作用,主要是优化分支预测。程序运行时,CPU的分支预测器会尝试预测条件分支的方向,如果预测正确,流水线效率高,否则会有性能损失。通过使用这两个宏,程序员可以将自己的先验知识告诉编译器,编译器会优化代码的布局,将更可能执行的分支放在前面,减少跳转带来的开销。 另外,需要提到这些宏是GCC特有的,属于编译器扩展,不属于标准C,因此可能在其他编译器如MSVC中不可用。用户需要注意可移植性问题。 还要考虑使用场景,比如在错误处理或高概率事件中应用unlikelylikely。例如,在循环中检查错误码,如果错误很少发生,可以用unlikely包裹,让编译器优化这部分代码的位置。 可能还需要举一个例子,说明如何在实际代码中使用这些宏,比如在条件判断前加上likely,帮助编译器优化。 最后,总结使用时的注意事项,比如确保条件概率的判断正确,避免滥用,以及考虑可移植性。此外,用户的问题可能涉及性能优化,可能需要提到其他优化技巧,如循环展开、内存对齐等,但根据问题范围,重点应保持在likelyunlikely上。 现在,需要将这些信息组织成结构清晰的中文回答,并生成相关问题。确保符合格式要求,如行内公式用$...$,独立公式用$$...$$,但这个问题可能不需要数学公式,所以重点在代码块引用标注。同时检查引用是否正确,每个引用标识对应到用户提供的参考资料中。</think>### C语言中`likely``unlikely`宏的定义及优化原理 #### 1. 宏定义与语法 在C语言中,`likely``unlikely`**并非原生关键字**,而是通过GCC的编译器扩展`__builtin_expect`实现的宏定义,主要用于优化分支预测。其典型定义如下[^1][^2]: ```c #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) ``` - `!!(x)`:将任意值转换为布尔值(0或1),确保表达式逻辑一致性。 - `__builtin_expect(exp, value)`:提示编译器`exp`的预期结果大概率是`value`(1表示真,0表示假)。 #### 2. 作用与优化原理 分支预测是CPU流水线技术的关键机制。若预测失败,CPU需清空流水线并重新加载指令,导致性能损失。通过`likely``unlikely`,程序员可向编译器提供分支条件的先验知识,使其生成更优的指令布局: - **优化指令顺序**:将高概率分支代码放在条件跳转指令附近,减少CPU取指延迟。 - **减少分支预测错误**:通过调整分支权重,提高CPU预测准确率。 例如,在错误处理中,失败分支通常概率极低: ```c if (unlikely(error)) { // 错误处理(低概率分支) } else { // 正常流程(高概率分支) } ``` #### 3. 使用场景与示例 **典型场景**: - 循环中的退出条件(如`unlikely(terminate)`)。 - 高频触发的状态检查(如`likely(data_ready)`)。 - 错误处理或异常路径(如`unlikely(malloc_fail)`)。 **代码示例**: ```c // 假设数据就绪是高频事件 if (likely(data_ready)) { process_data(); } else { retry_fetch(); } ``` #### 4. 注意事项 - **编译器依赖**:`__builtin_expect`是GCC/Clang扩展,其他编译器(如MSVC)可能不支持[^4]。 - **概率准确性**:错误的方向提示可能导致性能下降。 - **可读性**:过度使用可能降低代码可维护性,需权衡优化收益。 #### 5. 性能验证方法 可通过以下方式验证优化效果: 1. **反汇编**:检查分支指令的布局是否按预期调整。 2. **性能分析工具**:如`perf stat`测量分支预测失败率(Branch-misses)。 3. **基准测试**:对比使用宏前后的吞吐量或延迟。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值