Linux内核中的likely()和unlikely()宏

本文深入探讨了Linux内核中likely()与unlikely()宏的使用与原理,解释了这两个宏如何帮助GCC编译器优化条件分支,提高代码执行效率。通过实例展示了如何标记频繁或罕见发生的条件,以实现更高效的编译优化。

在Linux内核代码中经常看到likely()和unlikely()这两个宏,它们都是对bool变量进行判定,其定义如下:

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

其中__builtin_expect()函数是gcc提供的用于对分支语句进行优化,其原型如下:

long __builtin_expect (long exp, long c)

当exp == c时,该函数返回非零值;

gcc内建这个函数用于条件选择语句的优化,在一个条件经常出现,或者该条件很少出现的时候,编译器可以对条件分支选择进行优化;likely(x)即表示x很可能或者绝大多数情况下为真;而unlikely则刚好相反。(宏定义中的!!(x),对x两次取反,只是确保将x转化成bool型

如对下面的条件选择语句:

if (error) {
    ......
}

如果想把这个标记成绝少发生的分支,即认为error绝大多数情况下都为0:

if (unlikely(error)) {
    ......
}

相反,如果认为某个分支发生的可能性很大:

if (likely(success)) {
    ......
}

即认为success在绝大多数情况下为真。

上述说那么多都是废话!!!细节,又比不上https://cosysn.github.io/2017/01/14/introduction-to-linux-kernel-2-likey-and-unlieky/

对于likely(x)和unlikely(x)到底返回什么值,简单得很,一句话:

likely(x)和unlikely(x)都是说“x是真的?”,x如果是true,则两者均返回true;否则为false。

likely和unlikely本身并不会改变x的逻辑,它只是用于编译优化,所以,后面再碰到likely和unlikely,直接把它们去掉,只看后面的表达式就行了。

unlikely(0)返回false,unlikely(1)返回true;

likely(1)返回true;likely(2)返回true;likely(0)返回false。
 

 

<think> 嗯,用户想了解编程中"unlikely"的用法含义。从对话历史来看,用户可能是个有一定编程基础的学习者,正在研究代码优化技术。用户提供的四个引用中,前三个都直接与unlikely相关,但第四个关于Python readalllines的内容明显是干扰项,需要忽略。 用户的核心诉求很明确:需要系统解释unlikely的作用机制应用场景。引用[1]给出了观定义,引用[2]提供了C语言的示例,引用[3]则补充了C++20的标准实现。这些材料正好构成完整的技术图谱:从底层原理到具体语法。 注意到引用[1]提到"作用机理"分理论实践两部分,这个结构值得借鉴。可以把响应分为原理层面(分支预测、流水线优化)实操层面(语法差异、使用禁忌)。用户可能特别关心性能提升效果,需要准备具体数据对比。 有趣的是用户上次询问过快速排序,或许可以暗示unlikely在算法优化中的价值(比如处理递归边界条件)。不过当前问题更偏向语言特性,暂不需要展开算法案例。 需要警惕的陷阱:用户可能混淆不同语言的实现。必须明确区分C/C++的编译器扩展属性Python等语言的异常处理机制(后者通常不用unlikely概念)。在注意事项里要特别强调语言依赖性。 最后考虑用户可能想动手测试,准备添加简单的性能对比示例。引用[2]的代码片段稍显单薄,考虑补充带时间统计的完整demo会更直观。同时提示编译器依赖性问题(如GCC/Clang的__builtin_expect)。 </think> 在编程中,`unlikely` 是一种优化技术,用于向编译器提示某个条件分支**极不可能被执行**。其核心原理是通过调整代码布局减少 CPU 流水线中断,从而提升程序性能。以下是详细解析: --- ### **核心原理** 1. **分支预测优化** CPU 采用流水线机制顺序执行指令。当遇到条件分支(如 `if`)时,若预测失败会导致流水线清空,产生性能损耗。`unlikely` 告知编译器:该条件大概率不成立,应优先优化 `else` 分支的指令顺序[^2]。 2. **代码布局调整** 编译器会将标记为 `unlikely` 的代码块放置在内存中较远的位置(如 `.text.unlikely` 段),确保高频执行的代码更紧凑,提升 CPU 缓存命中率[^1]。 --- ### **使用方法** #### C语言(编译器扩展) ```c #include <linux/compiler.h> // GCC/Clang 需包含此头文件 if (unlikely(error_condition)) { // < 1% 概率发生的错误检查 handle_error(); } else { // 主逻辑(高频执行) } ``` #### C++20(标准属性) ```cpp if (error_condition) [[unlikely]] { // C++20 标准语法 handle_error(); } else { // 主逻辑 } ``` --- ### **适用场景** [^2] 1. **错误处理** 如文件打开失败、内存分配异常等罕见情况。 ```c FILE *fp = fopen("data.txt", "r"); if (unlikely(fp == NULL)) { perror("File open failed"); exit(1); } ``` 2. **边界条件检查** 循环中的极端值检测(例如循环次数超过阈值)。 3. **硬件中断处理** 设备驱动中异常信号的处理。 --- ### **关键注意事项** 1. **性能影响需实测** 误用(如对高频分支标记 `unlikely`)可能导致性能下降约 **10~15%**。应通过性能分析工具(如 `perf`)验证优化效果[^1]。 2. **编译器依赖性** - C/C++ 非标准:`unlikely` 是 GCC/Clang 的扩展属性,MSVC 不支持。 - 替代方案:C++20 的 `[[unlikely]]` 具有跨平台性。 3. **避免过度优化** 在实时性要求极高的场景(如内核、嵌入式),需谨慎平衡可读性与性能。 --- ### **底层机制示例** 编译前后对比(x86 汇编): ```nasm # 未使用 unlikely(默认分支布局) test %eax, %eax jne .Lerror ; 默认预测 je(条件成立)更可能跳转 # 使用 unlikely 后 test %eax, %eax je .Lmain ; 强制预测 jne(条件不成立)更可能执行主逻辑 .Lerror: ... ``` > 📌 **经验法则**:仅在分支执行概率 **< 5%** 时使用 `unlikely`,且优先优化高频代码路径[^3]。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值