gcc的内建函数__builtin_expect

本文介绍了GCC编译器提供的分支预测宏likely和unlikely的使用方法及其原理。这些宏可以帮助编译器更好地理解代码的执行路径,从而进行更有效的优化。

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

1.问题引出

在内核代码的条件分支中的判断中,经常可以看到如下两条语句:

[cpp]  view plain copy
  1. /* ...... */  
  2. if(unlikely(!something))  
  3. /* ...... */  
  4. if(likely(something))  
  5. /* ...... */  

追述一下可以看到有如下的宏定义:

[cpp]  view plain copy
  1. #define likely(x)   __builtin_expect(!!(x), 1)  
  2. #define unlikely(x) __builtin_expect(!!(x), 0)  

2.查看gcc的info文档可以看到相关的说明


 -- Built-in Function: 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.
     For example:


          if (__builtin_expect (x, 0))
            foo ();

     would indicate that we do not expect to call `foo', since we
     expect `x' to be zero.  Since you are limited to integral
     expressions for EXP, you should use constructions such as

          if (__builtin_expect (ptr != NULL, 1))
            error ();

     when testing pointer or floating-point values.

大致的意思是这么说的:

你也许会使用'__builtin_expece'为编译器体统分支预测的信息。因为程序员在预测他们的程序

的执行流程和分支走向时,表现得臭名昭著地糟糕,所以在一般情况下,作为一个程序员的你也许

会更喜欢使用gcc的一个参数'-fprofile-arcs'来收集程序运行的关于执行流程和分支走向的实际反馈

信息。然而,从一些比较复杂的程序中收集这些信息是非常困难的。

 

该内建函数的返回值EXP必须是一个整型的表达式。C的值必须是一个常量。这个内建函数的意思就是

我期望得到的是 EXP == C。

 

例子:

[cpp]  view plain copy
  1. if (__builtin_expect (x, 0))  
  2.   foo ();  

上面表示,我们不期望函数foo()会执行。因为我们期望x的值是0。因为EXP被限制为整型表达式,

所以如果你想测试指针(point)或者浮点数(floating-point),应该采用如下的语法结构:

[cpp]  view plain copy
  1. if (__builtin_expect (ptr != NULL, 1))  
  2.   error ();  

上面当中,我们是期望error()函数会被执行。

 

3.再回到上面看看我们讨论的likely和unlikely

[cpp]  view plain copy
  1. #define likely(x)   __builtin_expect(!!(x), 1)  
  2. #define unlikely(x) __builtin_expect(!!(x), 0)  

现在关于likely的返回值就很清晰了,就是期望为1,或者更加可能为1。

同样,unlikely的返回值就是更大可能性是0。



在Linux2.6内核中,看到一对奇怪的函数,likely(), unlikely()。

追踪回去,发现其实是一对宏,如下实现:

#define likely(x)  __builtin_expect((x), 1)

#define unlikely(x)  __builtin_expect((x), 0)

查了一下是这样说的:

例如: if( likely(val))  

if( unlikely (val))

他们都等于if(val),也就是在理解上来说,是等价的。那么为什么要这样用呢?

__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。

__builtin_expect((x),1) 表示 x 的值为真的可能性更大;
__builtin_expect((x),0) 表示 x 的值为假的可能性更大。

也就是说,使用 likely() ,执行 if 后面的语句 的机会更大,使用unlikely(),执行else 后面的语句的机会更大。
例如下面这段代码,作者就认为 prev 不等于 next 的可能性更大,

if (likely(prev != next)) {
       next->timestamp = now;
        ...
} else {
        ...;
 }
通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。


还有一个有趣的例子:
__builtin_expect(!!(x), 1)  这个是说,如果x == 0,那么结果就是 0,如果x == 1, 那么结果就是1,使用了!!是为了让x转化成bool型的。

expect(!!(x), 1

__builtin_expect(!!(x), 1)


<think>我们被要求介绍C++中常用的__builtin内建函数。这些内建函数是编译器(如GCC、Clang)提供的,用于执行一些底层操作或优化。以下是一些常用的__builtin函数及其用途: 1. `__builtin_return_address(level)` - 用途:获取当前函数或其调用者的返回地址。参数`level`表示调用栈的层级,0表示当前函数的返回地址,1表示调用当前函数的函数的返回地址,以此类推。 - 示例:如引用[1]所示,在函数中调用`__builtin_return_address(0)`可以获取当前函数的返回地址。 2. `__builtin_expect(exp, c)` - 用途:指示编译器条件表达式`exp`的预期值,以帮助分支预测优化。通常用于告诉编译器某个条件为真的可能性很大(`c`为1)或很小(`c`为0)。 - 示例:`if (__builtin_expect(x > 0, 1)) { ... }` 表示x>0的可能性很大。 3. `__builtin_popcount(x)` - 用途:计算整数`x`的二进制表示中1的个数(即汉明重量)。 - 示例:`int count = __builtin_popcount(0xF); // 返回4` 4. `__builtin_clz(x)` - 用途:计算整数`x`的二进制表示中前导0的个数(Count Leading Zeros)。注意`x`不能为0。 - 示例:`int leading_zeros = __builtin_clz(1); // 对于32位整数,返回31` 5. `__builtin_ctz(x)` - 用途:计算整数`x`的二进制表示中末尾0的个数(Count Trailing Zeros)。注意`x`不能为0。 - 示例:`int trailing_zeros = __builtin_ctz(0x8); // 返回3` 6. `__builtin_ffs(x)` - 用途:返回整数`x`的二进制表示中最低位的1的位置(从1开始计数,如果x为0则返回0)。 - 示例:`__builtin_ffs(0x8) // 返回4` 7. `__builtin_parity(x)` - 用途:返回整数`x`的二进制表示中1的个数的奇偶性(1的个数为奇数则返回1,偶数则返回0)。 - 示例:`__builtin_parity(0xF) // 返回0(因为4个1,偶数)` 8. `__builtin_prefetch(const void *addr, ...)` - 用途:预取数据到缓存。可以指定是读还是写,以及数据的时间局部性(临时性)。 - 示例:`__builtin_prefetch(p, 0, 3);` 其中第一个参数是地址,第二个参数0表示读(1为写),第三个参数表示局部性(0-3,3表示最弱局部性)。 9. `__builtin_unreachable()` - 用途:告诉编译器某个位置不可能被执行到,用于优化代码生成。 - 示例:在`switch`语句的`default`分支中,如果确定不可能执行到,可以加上`__builtin_unreachable()`。 10. `__builtin_trap()` - 用途:生成一个陷阱指令,使程序异常终止。通常用于不可恢复的错误。 - 示例:`if (error) __builtin_trap();` 11. `__builtin_constant_p(exp)` - 用途:判断表达式`exp`在编译时是否为常量,如果是则返回1,否则返回0。 - 示例:`if (__builtin_constant_p(x)) { ... }` 12. `__builtin_frame_address(level)` - 用途:获取指定层级函数的栈帧地址。类似于`__builtin_return_address`,但获取的是栈帧地址。 - 示例:`void* frame = __builtin_frame_address(0); // 当前函数的栈帧地址` 这些函数通常用于底层编程、性能优化或系统编程。需要注意的是,这些函数是编译器特定的,因此在使用时应注意可移植性。如果使用GCC或Clang,则通常可用。 另外,引用[4]提到了Cache的工作流程,而`__builtin_prefetch`就是用来主动控制缓存预取的函数,可以帮助减少Cache未命中的情况。 §§相关问题§§ 1. `__builtin_return_address`函数在调试中有哪些实际应用? 2. 如何利用`__builtin_expect`来优化程序性能? 3. 在C++中,除了`__builtin`函数,还有哪些编译器特定的扩展功能?</think>### C++常用`__builtin`内建函数详解 `__builtin`是GCC/Clang编译器提供的内建函数集合,用于执行底层操作、优化性能或访问硬件特性。以下是常用函数及其用途: #### 1. 栈帧与地址操作 - **`__builtin_return_address(level)`** 获取调用栈中指定层级的返回地址。`level=0`表示当前函数,`level=1`表示调用者函数。 示例(引用[1]): ```c void f() { int *p = __builtin_return_address(0); // 获取f()的返回地址 printf("f return address: %p\n", p); } ``` 用途:调试栈回溯、安全检测。 - **`__builtin_frame_address(level)`** 获取指定层级的栈帧指针(frame pointer)。 示例:`void* fp = __builtin_frame_address(0);` 用途:手动栈展开、内存分析。 #### 2. 位运算优化 - **`__builtin_popcount(x)`** 计算整数二进制中`1`的个数(汉明重量)。 示例:`int ones = __builtin_popcount(0b1011); // 返回3` 用途:位图处理、加密算法。 - **`__builtin_clz(x)`** 计算前导零(Leading Zeros)的数量。 示例:`__builtin_clz(1) // 32位系统返回31` 用途:位扫描、优先级队列。 - **`__builtin_ctz(x)`** 计算后缀零(Trailing Zeros)的数量。 示例:`__builtin_ctz(8) // 返回3(0b1000)` 用途:内存对齐、位图索引。 #### 3. 分支预测优化 - **`__builtin_expect(exp, c)`** 提示编译器条件`exp`的预期值(`c`通常为0或1),优化分支预测。 示例: ```c if (__builtin_expect(x > 0, 1)) { // 编译器优先优化此分支 } ``` 用途:性能关键路径优化(引用[4]缓存机制相关)。 #### 4. 内存与缓存控制 - **`__builtin_prefetch(ptr, rw, locality)`** 主动预取数据到缓存(`rw=0`读,`rw=1`写;`locality=0~3`表示时间局部性)。 示例:`__builtin_prefetch(arr+i, 0, 3);` 用途:减少Cache Miss(引用[4])。 #### 5. 数学运算 - **`__builtin_ffs(x)`** 返回最低位`1`的位置(从1开始计数)。 示例:`__builtin_ffs(0b10100) // 返回3` 用途:位掩码处理。 - **`__builtin_parity(x)`** 计算奇偶校验位(1的个数模2)。 示例:`__builtin_parity(7) // 7=0b111,返回1` 用途:错误检测编码。 #### 6. 编译器指令 - **`__builtin_unreachable()`** 标记不可达代码,优化生成代码。 示例: ```c if (error) { abort(); __builtin_unreachable(); // 提示编译器后续代码不可达 } ``` 用途:消除冗余分支警告。 - **`__builtin_constant_p(exp)`** 判断表达式是否为编译期常量。 示例: ```c if (__builtin_constant_p(x)) { // 编译期优化路径 } ``` 用途:编译期条件优化。 --- ### 注意事项 1. **编译器依赖**:`__builtin`是GCC/Clang扩展,非标准C++,需注意可移植性。 2. **性能权衡**:如`__builtin_prefetch`滥用可能降低缓存效率。 3. **调试兼容性**:栈操作函数可能受编译器优化影响(如`-fomit-frame-pointer`)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值