Android 本地代码中的LIKELY和UNLIKELY宏

本文介绍了LIKELY和UNLIKELY宏在Android Frameworks C++代码中的使用方法及其对程序执行效率的影响。通过具体示例说明了如何利用这两个宏指导编译器进行更高效的分支预测。

阅读Android Frameworks中的C++代码时,经常会碰到在条件判断语句中使用了LIKELY和UNLIKELY这两个宏,找到这两个宏的定义如下:

  

 

long __builtin_expect (long exp, long c) 是GCC的内建函数,解析如下:

你可以使用__builtin_expect给编译器提供分支预测的信息,通常,你因该明确使用这个编译选项(‘-fprofile-arcs’),因为很多程序员在如何预测他们编写的代码实际如何执行方面都很糟糕,使用这个宏可以很方面地让编译器优化分支跳转的代码。

这个函数的返回值就是exp:一个整形表达式,c必须是一个常量,该内建函数从语义上是表明:我们期望exp == c。

 

所以,如果你不考虑程序执行的效率,加不加LIKELY和UNLIKELY宏,执行的结果是一样的:

执行的结果是一样的。

那为什么还要使用这两个宏定义? 以汽车的速度为例子,如果速度超过200公里/小时表示有异常发生,代码可以这样写:

也可以这样写:

这两个方案执行后都是正确的,但是显然效率是不一样的,因为大多数情况下,汽车的速度不会超过200公里/小时,当采用第一个方案时,大多数情况下,代码执行到这里时CPU都要执行分支跳转的操作,这破坏了CPU的指令执行流水线,对性能的影响是显而易见的。而第二个方案则避免了这一问题,因为大多数时候都是顺序执行的。

对于第一种方案,我们可以加上UNLIKELY宏来让编译器来优化:

加上UNLIKELY宏后,相当于告诉编译器:速度大于200是很少出现的。这样编译器在编译代码时,会适当地调整条件判断的方式,让CPU的指令执行顺序尽可能不被打乱,已达到优化性能的效果。

所以,对于第二种方案,我们同样可以加上LIKELY宏:

 

在 C/C++ 编程中,`likely` `unlikely` 是用于**优化分支预测**的,它们并不是语言内置关键字,而是通过编译器内置函数(如 GCC 或 Clang 的 `__builtin_expect`)实现的。它们帮助编译器生成更高效的机器码,特别是在条件判断中指明某个分支是“大概率发生”还是“小概率发生”。 --- ### ✅ `likely` `unlikely` 的等价实现 ```c #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) ``` #### 解释: - `__builtin_expect(expr, expected_value)`: - 告诉编译器 `expr` 的值**预期为** `expected_value`。 - 这只是一个提示,不影响逻辑,但会影响生成的汇编代码中分支的布局(热路径优化)。 - `!!(x)`: - 将任意表达式 `x` 转换为布尔值(0 或 1),确保传给 `__builtin_expect` 的是明确的布尔上下文。 - `likely(x)`:表示 `x` 很可能为真(即期望为 1) - `unlikely(x)`:表示 `x` 很不可能为真(即期望为 0) --- ### 📌 使用示例(C语言) ```c #include <stdio.h> #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) void test_function(int flag) { if (likely(flag == 1)) { printf("Flag is 1 (common case)\n"); } else if (unlikely(flag == 0)) { printf("Flag is 0 (rare case)\n"); } } ``` #### 汇编层面的影响(以 x86 为例): - `likely` 分支会被编译器放在“直通路径”(fall-through),减少跳转开销。 - `unlikely` 分支会生成跳转指令,避免污染指令缓存。 --- ### 💡 等价替代方式 虽然没有标准库直接提供 `likely/unlikely`,但在不同环境中有等价或近似机制: #### 1. **C++20 及以后:`[[likely]]` `[[unlikely]]` 属性(官方标准)** ```cpp void func(int flag) { if (flag == 1) [[likely]] { printf("Common case\n"); } else [[unlikely]] { printf("Rare case\n"); } } ``` > 这是现代 C++ 的标准写法,语义清晰且由编译器优化支持。 #### 2. **无提示(普通写法)——功能等价但无性能提示** ```c if (flag == 1) { // ... } else { // ... } ``` > 功能上完全一样,但编译器无法做最佳分支预测优化。 --- ### 🔍 实际应用场景 - 内核开发(Linux Kernel 大量使用 `likely/unlikely`) - 高性能服务中的错误处理(如 `if (unlikely(err)) handle_error();`) - 热路径优化 --- ### ❗注意事项 - 这些仅在 GCC/Clang 等支持 `__builtin_expect` 的编译器上有意义。 - 在 MSVC 中不支持 `__builtin_expect`,可使用条件编译规避: ```c #ifdef __GNUC__ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else #define likely(x) (x) #define unlikely(x) (x) #endif ``` --- ### 总结 | 写法 | 是否等价 | 说明 | |------|----------|------| | `likely(x)` | ✅ 功能+优化等价 | 使用 `__builtin_expect(x, 1)` 提示编译器 | | `unlikely(x)` | ✅ 功能+优化等价 | 使用 `__builtin_expect(x, 0)` | | C++20 `[[likely]]` / `[[unlikely]]` | ✅ 语义等价且更现代 | 推荐新项目使用 | | 普通 `if` | ✅ 功能等价 ❌ 无优化提示 | 缺少编译器优化线索 | ---
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值