__builtin_expect 不提升单次分支性能,仅在 -o2+ 且分支高度偏斜(>95%)时有效;无效常见原因:未开优化、概率不偏斜、函数未内联;验证须用 perf stat 对比 branch-misses。

__builtin_expect 不会提升单次分支性能,只在 -O2 及以上、GCC/Clang 下对高度偏斜(>95% 或
为什么 -O2 是硬性门槛
__builtin_expect 是编译期提示,不是运行时函数。GCC/Clang 在 -O0 或 -O1 下直接忽略它,生成的汇编和普通 if 完全一样。
- 验证方法:用
g++ -S -O2 main.cpp生成汇编,搜索目标if对应的je/jne指令 —— 加了likely()后,主路径应是直行(fall-through),错误处理跳转到函数末尾附近 - 用
objdump -d a.out | grep -A10 "your_function_name"观察指令地址是否更连续 - 在 -O0 下加
likely纯属心理安慰,别测性能
likely/unlikely 宏必须这么写才安全
直接调用 __builtin_expect(ptr != nullptr, 1) 风险极高:指针非空时值可能是任意非零地址(如 0x7fffabcd),传给第二个参数的其实是该地址而非 1,提示彻底失效。
- 必须用双叹号:
!!(x)强制转成0或1 - 只靠
#ifdef __GNUC__判断,__clang__不用单独加 —— Clang 兼容 GCC 内置函数,但宏定义行为可能不一致 - 宏必须放在头文件中统一包含,禁止在多个
.cpp里重复定义 - 别在宏里展开带副作用的表达式,例如
likely(i++)会导致i自增两次
哪些分支真值得加 likely/unlikely
不是所有 if 都该干预。真正起效的场景极窄,且必须满足「高频 + 极偏斜 + 已被 perf 定位为瓶颈」三条件。
- 指针判空:
if (likely(ptr != nullptr))—— 已知 99%+ 调用中ptr非空 - 系统调用错误码:
if (unlikely(ret == -1 && errno == ENOMEM))—— 内存充足时几乎不触发 - 循环内固定边界检查:
if (likely(i —— <code>i在绝大多数迭代中合法 - 协议字段校验:
if (unlikely(header->version != 2))—— 线上版本已稳定为 2 - 别碰:日志开关、配置项检查、用户输入校验、调试构建中的分支 —— 概率不可控,编译器静态分析后会直接放弃你的提示
加了却没提速?先查这三件事
常见“无效”不是语法错,而是上下文不匹配。
- 没开 -O2 或更高:检查编译命令,确认
-O2或-O3存在且未被覆盖 - 分支实际概率不偏斜:用
perf record -e branches,branch-misses ./a.out看branch-misses占比,低于 5% 的分支加了也白加 - 函数未内联:提示只作用于当前函数层级。若分支在被调函数里,外层加
likely没用 —— 先确保该函数被内联(加[[gnu::always_inline]]或看汇编是否展开)
最可靠的验证方式,永远是 perf stat -e branches,branch-misses 对比前后数值,而不是看编译是否通过或主观感觉快慢。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











