Android 本地代码中的LIKELY和UNLIKELY宏

本文深入探讨了在AndroidFrameworks中使用LIKELY和UNLIKELY宏的作用,解释了它们如何帮助编译器进行更有效的分支预测,从而优化代码执行效率。通过实例分析,展示了正确应用这些宏能够显著提升程序性能。

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

阅读Android Frameworks中的C++代码时,经常会碰到在条件判断语句中使用了LIKELY和UNLIKELY这两个宏,找到这两个宏的定义如下:
#define LIKELY( exp )       (__builtin_expect( (exp) != 0, true  ))  
#define UNLIKELY( exp )     (__builtin_expect( (exp) != 0, false ))  
long __builtin_expect (long exp, long c) 是GCC的内建函数,解析如下:

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

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

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

if( LIKELY(exp) )   
{   
}   
else  
{   
}  


if(exp)   
{   
}   
else  
{   
}  
执行的结果是一样的。

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

if(speed >= 200){   
    //异常处理代码   
    .....   
    stop();   
}else{   
    //正常处理代码   
    continue();   
}  
也可以这样写:

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

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

if(UNLIKELY(speed >= 200)){   
    //异常处理代码   
    .....   
    stop();   
}else{   
    //正常处理代码   
    continue();   
}  
加上UNLIKELY宏后,相当于告诉编译器:速度大于200是很少出现的。这样编译器在编译代码时,会适当地调整条件判断的方式,让CPU的指令执行顺序尽可能不被打乱,已达到优化性能的效果。

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

if(LIKELY(speed < 200)){   
    //正常处理代码   
    continue();   
}else{   
    //异常处理代码   
    .....   
    stop();   

}


转载: http://www.linuxidc.com/Linux/2011-03/33737.htm

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

余额充值