加了__builtin_expect不一定快,甚至可能变慢;它仅在分支高度偏斜(>95%)且编译开启-O2以上、函数内联、实际概率符合预期时才有效,需用!!(x)封装并兼容GCC/Clang,否则提示失效或引发隐晦问题。

加了 __builtin_expect 不一定快,甚至可能变慢;它只在分支高度偏斜(>95% 或 -O2 及以上优化时才可能生效。
什么时候该用 likely 和 unlikely 宏
不是所有 if 都值得标记。真正起效的场景非常窄:
- 指针判空:
if (likely(ptr != nullptr))—— 已知 99%+ 调用中ptr非空 - 系统调用错误码检查:
if (unlikely(ret == -1 && errno == ENOMEM))—— 内存耗尽是罕见事件 - 循环内固定边界:
for (int i = 0; i —— <code>i几乎总在合法范围内 - 协议字段校验:
if (unlikely(header->version != 2))—— 线上版本长期稳定为 2
别用在调试开关、用户输入校验、随机数据判断或概率接近 50% 的分支上——编译器自己猜得比你准。
如何安全定义和使用 likely/unlikely 宏
直接调用 __builtin_expect(x, 1) 极易出错。必须用宏封装,并满足三个硬性条件:
立即学习“C++免费学习笔记(深入)”;
- 用
!!(x)强制转为 0/1,避免传入指针地址(如ptr)本身导致提示失效 - 仅靠
#ifdef __GNUC__判断,__clang__兼容 GCC 内置函数,但宏定义不一致会引发隐晦问题 - 宏必须放在公共头文件中统一包含,禁止在多个
.cpp文件里重复定义
标准写法:
#ifdef __GNUC__ #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) #else #define likely(x) (x) #define unlikely(x) (x) #endif
为什么加了提示却没提速?先查这三件事
“加了等于没加”是最常见误判,原因往往与代码逻辑无关:
- 编译没开
-O2或更高:在-O0下__builtin_expect完全被忽略,汇编和普通if一模一样 - 分支实际概率不偏斜:比如日志开关
if (g_log_enabled),若配置常开常关,编译器静态分析后直接放弃提示 - 函数未内联:提示只作用于当前函数层级;若分支在被调函数里,外层加
likely没用
验证是否生效最直接的方法:objdump -d ./a.out | grep -A10 your_func_name,看高概率路径是否为连续指令流(fall-through),低概率路径是否跳转到函数末尾附近。
性能收益必须实测,不能靠感觉
分支预测优化的收益极其依赖硬件和上下文:
- 用
perf stat -e branches,branch-misses ./a.out对比加前加后,分支失误率下降 >5% 才算有效 - 关注真实耗时:在热点函数里跑百万次迭代,用
std::chrono计时,避免被 IPC、缓存抖动干扰 - 换 CPU 再测一遍:Skylake 和 Zen3 的分支预测器设计不同,同一段代码收益可能差一倍
最容易被忽略的一点:它不改变程序行为,也不减少指令数,只调整指令布局。如果瓶颈根本不在分支预测(比如在内存带宽或 ALU 争用),加了也白加。



















