详解likely和unlikely函数

本文详细介绍了Linux内核中likely和unlikely函数的两种实现方式,包括对__builtin_expect的封装和使用__branch_check__函数。通过实际代码示例,解释了这些函数在提升性能方面的应用,以及如何利用__builtin_constant_p判断表达式是否为常量。文章还提供了关键代码片段和测试结果,帮助读者理解这些函数在不同场景下的工作原理。

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

    内核源码:linux-2.6.38.8.tar.bz2

    参考文档:http://gcc.gnu.org/onlinedocs/gcc-4.6.2/gcc/Other-Builtins.html#Other-Builtins

 

     在Linux内核中likely和unlikely函数有两种(只能两者选一)实现方式,它们的实现原理稍有不同,但作用是相同的,下面将结合linux-2.6.38.8版本的内核代码来进行讲解。

    1、对__builtin_expect的封装

    它们的源代码如下: 

/* linux-2.6.38.8/include/linux/compiler.h */
# define likely(x)	__builtin_expect(!!(x), 1)
# define unlikely(x)	__builtin_expect(!!(x), 0)

    __builtin_expect 是GCC的内置函数,用来对选择语句的判断条件进行优化,常用于一个判断条件经常成立(如likely)或经常不成立(如unlikely)的情况。

    __builtin_expect的函数原型为long  __builtin_expect (long exp, long c),返回值为完整表达式exp的值,它的作用是期望表达式exp的值等于c(注意,如果exp == c条件成立的机会占绝大多数,那么性能将会得到提升,否则性能反而会下降)。

    在普通的应用程序中也可以使用__builtin_expect,如下面的例子: 

#include <stdio.h>

int main(void)
{
    int a;

    scanf("%d", &a);

    if(__builtin_expect(a, 4))
	printf("if: a = %d\n", a);
    else
	printf("else: a = %d\n", a);

    return 0;
}

    分别输入整数0到4来进行5次测试,它们的输出分别为: 

else: a = 0

if: a = 1

if: a = 2

if: a = 3

if: a = 4

    注意,在上例中只有输入整数0的时候才执行else后的打印语句,也就是说__builtin_expect(a, 4)函数的值就是表达式a的值。

    记住,它们只是用来提升性能的优化手段,并不会改变原来表达式的值。

    2、使用__branch_check__函数

    它们的源代码如下: 

/* linux-2.6.38.8/include/linux/compiler.h */
# ifndef likely
#  define likely(x)	(__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 1))
# endif
# ifndef unlikely
#  define unlikely(x)	(__builtin_constant_p(x) ? !!(x) : __branch_check__(x, 0))
# endif

    (1)、先使用内置函数__builtin_constant_p忽略表达式x为常量的情况

    __builtin_constant_p也是GCC的内置函数,函数原型为int  __builtin_constant_p(exp),用于判断表达式exp在编译时是否是一个常量,如果是则函数的值为整数1,否则为0,如下面的例子:

#include <stdio.h>
#include <stdlib.h>

#define VALUE 5

int main(void)
{
    char *ptr = NULL;
    int num, count;

    ptr = malloc(20);
    num = __builtin_constant_p(ptr) ? 20 : 20 + 10;
    printf("num = %d\n", num);
    free(ptr);

    count = __builtin_constant_p(VALUE) ? 20 + VALUE : 10;
    printf("count = %d\n", count);

    return 0;
}

    例子的输出结果: 

num = 30
count = 25

    例子中的ptr为指针变量,所以__builtin_constant_p(ptr)的值为0,num的值为30。

    (2)、函数__branch_check__的实现 

/* linux-2.6.38.8/include/linux/compiler.h */
#define __branch_check__(x, expect) ({					\
			int ______r;					\
			static struct ftrace_branch_data		\
				__attribute__((__aligned__(4)))		\
				__attribute__((section("_ftrace_annotated_branch"))) \
				______f = {				\
				.func = __func__,			\
				.file = __FILE__,			\
				.line = __LINE__,			\
			};						\
			______r = likely_notrace(x);			\
			ftrace_likely_update(&______f, ______r, expect); \
			______r;					\
		})

    使用它来检查判断条件并记录likely判断的预测信息,之后根据预测信息进行相应的优化以提升性能。

    函数__branch_check__的返回值为______r的值,也就是参数x的值。

<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
发出的红包

打赏作者

tanglinux

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值