__builtin_expect与if条件行为:&&结合unlikely实现或逻辑的原理
1. 「跳过后续代码」的核心是C语言&&的短路求值特性,和unlikely宏无关
C语言标准明确规定了&&运算符的短路求值规则:当&&左侧的表达式求值结果为假(值为0)时,整个逻辑与表达式的结果已经可以确定为假,编译器会直接跳过右侧表达式的所有求值操作,不会执行右侧涉及的函数调用、变量运算等逻辑,这是C语言本身的语法特性,和外层的unlikely宏没有关联。
对应你的代码逻辑验证:
arch_get_random_seed_long、arch_get_random_long两个函数的约定为:成功将随机值写入参数指向的地址时返回非0值,失败时返回0。
代码中的判断条件拆解为左右两部分:
- 左表达式:
unlikely(!arch_get_random_seed_long(&v)) - 右表达式:
unlikely(!arch_get_random_long(&v))
当arch_get_random_seed_long调用成功时,返回非0值,!非0的结果为0,左表达式整体求值结果为0,触发&&短路规则,直接跳过右表达式的执行,自然不会调用arch_get_random_long重复赋值,和你观测到的运行效果完全一致。
只有当左表达式求值结果为1(也就是arch_get_random_seed_long调用失败,!0结果为1)时,才会继续执行右表达式,调用arch_get_random_long尝试赋值。
2. unlikely宏仅为分支预测优化提示,不改变逻辑
unlikely是Linux内核中定义的编译优化宏,标准定义为:
#define unlikely(x) __builtin_expect(!!(x), 0)
其中__builtin_expect是GCC/Clang等编译器提供的内置函数,作用仅为给编译器传递分支概率提示:告知编译器表达式x的取值大概率为第二个参数的值,让编译器生成汇编指令时,把概率更高的分支放在指令流的连续位置,减少CPU分支预测失败带来的性能损耗,完全不会改变表达式本身的求值结果和逻辑执行顺序。
你代码中使用的unlikely(!arch_get_random_seed_long(&v)),只是告诉编译器:arch_get_random_seed_long调用失败的概率极低,这个表达式大部分情况下取值为0,优化生成指令的执行效率,不会对短路逻辑产生任何影响。
3. 逻辑或效果的来源
你提到的“实现逻辑或功能”是业务层面的等价效果:整个if分支只有在两个获取随机数的函数都失败的情况下才会进入,反过来就是「只要arch_get_random_seed_long成功 或 arch_get_random_long成功,就不会进入if分支」,业务逻辑等价于:
if (!(arch_get_random_seed_long(&v) || arch_get_random_long(&v))) { break; }
只是代码用负向判断的方式通过&&实现了相同的业务逻辑,核心还是依赖&&的短路特性。
内容的提问来源于stack exchange,提问作者paavaanan

