在做 C++ 性能优化时,likely / unlikely
是一个非常容易被提到、但也非常容易被误解的话题。我接触到很多程序员(包括一些非常资深的程序员)经常会被这两个关键字所误导,甚至夸大他本身的作用。
很多人第一次接触这两个宏时,都会自然地产生两个问题:
likely 能控制 CPU 的分支预测吗?unlikely 会把代码自动放到 .cold
段吗?直觉上我们很容易把 likely / unlikely
理解成某种“性能开关”。事实上,它们更准确的定位应该是:向编译器提供分支冷热倾向的提示(hint),帮助编译器优化代码布局,而不是直接命令
CPU 如何预测。
本文就围绕这个问题展开,结合汇编和工程实践,聊清楚
likely / unlikely
到底优化了什么、什么时候值得用、以及什么时候不值得用。
在 GCC / Clang 体系下,常见的定义通常如下:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)这里的核心是 __builtin_expect。
它的含义不是“让 CPU 一定预测成功”,而是告诉编译器:
x 的结果,大概率是 10其中 !!(x)
只是把任意表达式规范化为布尔值,避免传入复杂类型后产生歧义。
在 C++20 之后,也可以写成标准属性的形式:
if (cond) [likely](likely.html)
{
fastPath();
}
else [unlikely](unlikely.html)
{
slowPath();
}这两种写法的目标是一致的:告诉编译器哪个分支更“热”,哪个分支更“冷”。
很多文章会简单地说一句:“likely
能优化分支预测。”
这句话不能说完全错,但并不精确。
更准确地说,likely / unlikely
更常见的影响有下面几类:
这是最常见、也是最直接的影响。
编译器在生成汇编时,会决定:
如果某个分支被标记为
likely,编译器通常更倾向于把它布置成“顺着执行流直接落下去”的那条路径;而把冷分支放到需要跳转的位置。
这样做的好处是:
所以,likely / unlikely
的核心价值,往往首先体现在代码布局上。
现代 CPU
的分支预测器非常复杂,主要依赖运行时历史行为进行动态预测。
这意味着,真正执行时分支能不能预测成功,更多取决于:
likely / unlikely 并不能越过 CPU
的硬件预测器,直接“命令”它以后必须按某个方向预测。
所以更合理的理解是:
有些同学会问:unlikely 会不会把代码放到
.cold 段?
答案是:有可能,但不是绝对。
是否真的被拆到冷代码区,通常还要看:
-O2 / -O3)因此,不能简单理解为“写了 unlikely,代码就一定进
.cold”。更准确的说法是:unlikely
可能帮助编译器识别冷路径,但最终是否拆分,取决于编译器整体决策。
我们先看一个非常典型的例子:
int foo(int x)
{
if (likely(x > 0))
{
return x + 1;
}
else
{
return slowPath(x);
}
}这里的意图很清楚:
x > 0 是绝大多数情况slowPath(x) 是少数情况这时编译器更可能把 return x + 1;
这条路径直接排在判断之后,把 slowPath(x)
放到跳转路径上。
这里有一段代码很适合配合观察汇编差异:
当代码块使用likely的时候,编辑器会把最终生成的代码快放在离判断条件更近的地方, 使得他有更好的code prefetch.
如果你在 Godbolt 中对比开启和不开启 likely
的版本,通常能看到:
这也是一个非常值得建立的性能直觉:
我们看到的源码顺序,并不等于最终机器执行时的指令排布顺序。
这是最核心的误区。
如果把“控制分支预测”理解成:
那么答案是否定的。
CPU 的动态分支预测器并不会因为你写了
likely(x),就放弃自己的历史统计和运行时判断。
但如果把“控制分支预测”理解成:
那么答案又是肯定的。
所以更准确的表达应该是:
likely/unlikely影响的是编译器对分支的静态理解和代码布局,它可能间接帮助执行效率,但不是直接操控硬件分支预测器。
我更推荐在系统的关键路径上来增加这种可能影响CPU性能的行为。CPU的预测在绝大多数的时候都能工作地很好,所以,如果你想增加likely/unlikely,请一定要有足够的性能测试数据来支撑你的代码!
likely / unlikely
的真正价值,不在于“控制
CPU”,而在于帮助编译器更好地理解代码的冷热结构,并尽可能把热路径排布得更连续。
它适合用于:
但它并不适合滥用。
在现代 C++ 性能优化中,likely / unlikely
更像是一把小而精的手术刀,而不是一把万能锤子。
真正可靠的性能优化,依然应当建立在性能分析工具、业务路径统计和汇编验证的基础之上。