在 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 [[likely]]/[[unlikely]]

标准化

否(编译器扩展)

是(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。过早优化是万恶之源,但合理使用可显著提升热点代码效率。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐