likely && unlikely

likely和unlikely是gcc扩展的跟处理器相关的宏:

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


现在处理器都是流水线的,有些里面有多个逻辑运算单元,系统可以提前取多条指令进行并行处理,但遇到跳转时,则需要重新取指令,这相对于不用重新去指令就降低了速度。所以就引入了likely和unlikely,目的是增加条件分支预测的准确性,cpu会提前装载后面的指令,遇到条件转移指令时会提前预测并装载某个分支的指令。unlikely 表示你可以确认该条件是极少发生的,相反likely表示该条件多数情况下会发生。编译器会产生相应的代码来优化cpu执行效率。
因此程序员在编写代码时可以根据判断条件发生的概率来优化处理器的取指操作。

 从表面上看if(likely(value)) == if(value),if(unlikely(value)) == if(value)

 内核2.2.26的头文件include/linux/compiler.h中有宏定义

/*
 * Generic compiler-dependent macros required for kernel
 * build go below this comment. Actual compiler/compiler version
 * specific implementations come from the above header files
 */

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


 __builtin_expect是gcc的一个预处理命令,其解释如下:

long __builtin_expect (long exp, long c)
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.

<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、付费专栏及课程。

余额充值