C++关键字之likely和unlikely
在 C++ 中,likely 和 unlikely 并不是语言标准中的关键字(直到 C++20 才引入了标准化的属性语法),但它们广泛用于性能优化,尤其是在条件分支预测(branch prediction)方面。下面是对 likely 和 unlikely 的全面总结,涵盖历史背景、用法、原理、兼容性及最佳实践。
一、基本概念
1. 分支预测(Branch Prediction)
现代 CPU 使用分支预测器来猜测程序中 if-else 或循环等控制流语句的执行路径,以提前加载指令、提高流水线效率。如果预测错误,会导致流水线冲刷(pipeline flush),带来性能损失。
2. likely / unlikely 的作用
likely(x):提示编译器表达式
x很可能为真(true)unlikely(x):提示编译器表达式
x很可能为假(false)
这些提示帮助编译器:
-
调整生成的汇编代码布局(将“更可能执行”的代码放在连续内存中,提升缓存局部性)
-
辅助 CPU 分支预测器做出更准确的预测(间接)
⚠️ 注意:这些提示不影响程序逻辑,只影响性能。
二、实现方式(不同编译器/标准)
1. GCC / Clang 扩展(C/C++)
GCC 和 Clang 提供了内置函数:
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtinEXPECT(!!(x), 0)
使用示例:
if (likely(ptr != nullptr)) {
// 大多数情况下 ptr 不为空
use(ptr);
} else {
handle_error();
}
__builtin_expect(expr, expected_value)告诉编译器
expr的预期值!!(x)确保结果为 0 或 1(布尔化)
2. C++20 标准属性(推荐新代码使用)
C++20 引入了标准化的属性语法:
if (ptr != nullptr) [[likely]] {
use(ptr);
} else [[unlikely]] {
handle_error();
}
三、效果与限制
1. 实际效果
- 代码布局优化
:编译器会把
[[likely]]分支放在“热路径”(hot path),减少跳转 - 对 CPU 预测的帮助有限
:现代 CPU 主要依赖运行时历史(动态预测),静态提示作用较小,但在冷启动或简单分支中仍有价值
- 调试/发布模式差异
:某些编译器在
-O0下忽略这些提示
2. 限制与注意事项
-
❌ 不能用于任意表达式,只能用于控制流分支(C++20)
-
❌ 滥用可能导致性能下降(如错误预测方向)
-
✅ 应基于性能分析数据(如 perf、VTune)使用,而非猜测
-
✅ 在错误处理、异常路径、边界检查等场景最有效(这些通常是
unlikely)
四、跨平台兼容性建议
为了兼容 C++17 及更早版本,同时支持 C++20,可定义宏:
#if __cplusplus >= 202002L
#define LIKELY(condition) (condition) [[likely]]
#define UNLIKELY(condition) (condition) [[unlikely]]
#else
#define LIKELY(condition) (__builtin_expect(!!(condition), 1))
#define UNLIKELY(condition) (__builtin_expect(!!(condition), 0))
#endif
使用:
if (LIKELY(x > 0)) { ... }
if (UNLIKELY(error_occurred)) { ... }
注意:MSVC 对 __builtin_expect 支持有限(可通过 _expect 或忽略),在 Windows 上需额外处理。
五、典型应用场景
|
场景 |
推荐写法 |
|---|---|
|
指针非空检查 |
if (LIKELY(ptr)) |
|
错误码检查 |
if (UNLIKELY(ret != 0)) |
|
循环终止条件罕见 |
for (...) { if (UNLIKELY(break_condition)) break; } |
|
断言失败路径 |
if (UNLIKELY(!cond)) abort(); |
|
协议解析中的非法包 |
if (UNLIKELY(invalid_header)) discard(); |
六、总结对比表
|
特性 |
__builtin_expect
(GCC/Clang) |
C++20 |
|---|---|---|
|
标准化 |
否(编译器扩展) |
是(ISO C++20) |
|
语法位置 |
表达式内部 |
分支语句后 |
|
可读性 |
较差(宏封装后改善) |
更清晰、直观 |
|
可移植性 |
限于 GCC/Clang |
所有 C++20 兼容编译器 |
|
推荐使用 |
旧项目、C 代码 |
新 C++ 项目 |
七、参考资料
-
C++20 Standard: Attributes for likelihood
-
GCC Manual:
__builtin_expect -
Intel Optimization Reference Manual
-
“What Every Programmer Should Know About Memory” – Ulrich Drepper
✅ 最佳实践建议:
仅在性能关键路径且有实测依据的情况下使用
likely/unlikely。过早优化是万恶之源,但合理使用可显著提升热点代码效率。
更多推荐


所有评论(0)