<!-- title: 分支预测神课：别让CPU白赌 -->
<!-- domain: programming -->

# 别让CPU白赌：一堂百万级播放的无分支编程神课

## 概述

在C++圈，有一类演讲每隔几个月就会被 Hacker News 和 Reddit 的 r/cpp 版块重新翻出来讨论一轮——Fedor Pikus 在 CppCon 2021 大会上的这场《Branchless Programming in C++》就是典型。它讲的是一个非常小、却直插性能工程命门的问题：**你的 if 语句，正在让 CPU 赌博，而且赌输了要付出十几级流水线的代价**。这场演讲之所以常年被开发者社区惦记，是因为它没有停留在“分支很慢”的口号上，而是把“什么时候分支慢、编译器已经替你做了什么、无分支写法的真实代价是什么”这三件事讲透了。本文按视频的叙事脉络逐层拆解，并把背后的体系结构研究、工程实践和争议点一并补齐——没看过视频，你能看懂这门课；看过视频，你能多懂一层。

## 视频基本信息

| 项目 | 内容 |
|------|------|
| 视频标题 | Branchless Programming in C++（C++ 无分支编程） |
| 频道 | CppCon（C++ 大会官方频道，[youtube.com/@CppCon](https://www.youtube.com/@CppCon)） |
| 演讲者 | Fedor Pikus，性能工程专家，著有《The Art of Writing Efficient Programs》 |
| 发布时间 | 2021年 CppCon 大会演讲录像，由官方频道于会后发布（据频道页面） |
| 热度数据 | 长期居该频道近年演讲播放前列，是开发者社区每逢性能话题必翻出的旧课（据频道页面） |
| 主题定位 | 编译器优化 × CPU 微架构 × 性能测量方法 |

## 一、核心主张拆解：演讲的四层递进

### 第一层：分支的本质，是让流水线去赌

演讲的开场没有直接讲代码，而是先摆了一个硬件事实：现代 CPU 是深度流水线机器，取指、译码、执行、访存、写回被拆成十几级流水级，每一级都必须时刻有活干，否则吞吐量就会塌方。而分支指令（if、循环的跳转、switch）恰恰是流水线的天敌——在分支条件真正算出来之前，CPU 根本不知道下一条该取哪个地址的指令。

硬件的解法是**投机执行（speculative execution）**：分支预测器先猜一个方向，流水线按猜的结果满负荷跑下去；猜对了，猜的成本约等于零；猜错了，已经流入流水线的指令全部作废、清空重来，这就是所谓**分支预测失败惩罚**。按 Intel 优化手册和 Agner Fog 微架构资料里业界常用的量级，现代大核上这个惩罚大约在十几到二十个时钟周期。演讲用这个背景立起全片的核心命题：**分支本身不贵，赌输了才贵**。

### 第二层：可预测的分支几乎免费，不可预测的分支才要命

这是整场演讲最有认知价值的一层。Pikus 强调，“去掉 if 就能加速”是社区流传最广的误读。现代分支预测器对**有规律的模式**极其擅长：循环边界、状态机的固定跳转、跟你上一千次行为一致的判断，预测准确率轻轻松松上到 95%–99% 的量级，这类分支写成再“分支化”的代码都不心疼。

真正贵的只有一类：**数据相关、模式上接近随机的分支**。比如对一串无序数据逐个判断大小、解析器里字符分类这种“上帝都猜不准”的决策点。演讲里那个经典对照实验的思想来自一个著名的 Stack Overflow 提问：同样的循环，对**已排序**数组跑比对**未排序**数组跑快好几倍——代码一行没改，唯一的区别是排序让分支变得可预测了。所以正确的目标从来不是“消灭分支”，而是“消灭不可预测的分支”，或者把数据组织得让分支变得可预测。

### 第三层：编译器早就比你勤快

第三层是给手写“性能代码”的人泼的冷水。Pikus 指出，在开启优化的编译器眼里，很多 if 语句根本活不到汇编那一层。编译器会把简单的条件赋值直接翻译成 x86 的**条件传送指令（cmov）**：两个值都算出来，用条件决定哪个生效，全程没有跳转、不给预测器出题。你写 `if (a > b) x = a; else x = b;`、写三目运算符、写 `std::min`/`std::max`，在汇编层面很可能是同一条 cmov。

但编译器这么做是有前提的，演讲把两个坑都点了出来：一是**语义等价**——`std::min` 对 NaN 的行为、条件分支里带副作用（比如自增、IO）的情况，编译器不敢也不会替你转成 cmov；二是**溢出与未定义行为**会让看似等价的改写悄悄变质。结论是：你以为你在“手动优化”，很多时候只是在跟编译器抢活，还抢输了。

### 第四层：无分支写法是手艺，但不是玄学

在真正需要消除分支的热点上，演讲给出了一组可复用的模式：

- **布尔当整数用**：`count += (a > b);` 这类写法让编译器生成无跳转的累加，统计型循环里非常好用；
- **先计数、后批量处理（count-then-apply）**：把“逐个判断并处理”拆成“先统计满足条件的个数、再统一处理”，这一招是解锁 SIMD 自动向量化.blocking 的关键，因为带提前退出的变长循环向来是向量化的头号障碍；
- **查表代替判断**：小规模的多路分支用查找表收敛成一次数组访问；
- **算术掩码**：用位运算把“选A还是选B”变成纯算术。

每一招演讲都配了汇编和性能计数器的对照，反复传递同一个方法论：**改之前看汇编、改之后看数据，一切以测量为准**。C++20 的 `[[likely]]`/`[[unlikely]]` 属性、GCC 的 `__builtin_expect`、基于真实负载数据的 PGO（配置文件引导优化），都被定位为“比手写汇编级技巧更值得优先尝试”的正规军武器。

## 二、知识与理论溯源：这门课背后的五十年体系结构史

演讲里一句带过的内容，背后是一整条真实的研究脉络，这也是它经得起深挖的原因。

**动态分支预测的起点**是 James E. Smith 1981 年的经典论文《A Study of Branch Prediction Strategies》，他提出的饱和计数器思想——用两位状态机记录“最近猜对猜错”，错了才降级、对了连对升级——直到今天仍是几乎所有预测器的地基。

**两级行进**发生在 1991 年：Yeh 与 Patt 提出“两级自适应分支预测”，用“最近 n 次分支的历史模式”索引一张预测表，让预测器学会更长的行为周期；1993 年 McFarling 的 gshare 方案把分支地址与全局历史异或后查表，成为教科书级组合。此后高端处理器进入“锦标赛预测器”时代——Digital 的 Alpha 21264 就同时装了局部历史和全局历史两套预测器，动态择优，这些在 Hennessy 与 Patterson 的《计算机体系结构：量化研究方法》里都有系统记述。

**现代王座**属于 TAGE：André Seznec 与 Philippe Michaud 在 2006 年提出的几何历史长度标记预测器，用多条不同长度的全局历史互相竞争备份，把预测准确率推到新高。据体系结构文献与公开资料，从 Oracle SPARC 系列开始，TAGE 系设计陆续进入商用高性能核，AMD Zen 一系处理器采用 TAGE 风格的二级预测器也经社区逆向分析与厂商渠道广为流传。换句话说，你在写 `if` 时面对的，是一台背着一整本“你的代码行为史”在赌博的机器。

**赌局的另一面**在 2018 年爆发：Kocher 等人的 Spectre 论文与 Lipp 等人的 Meltdown 论文证明，投机执行虽然“失败会回滚”，但它的**副作用（缓存状态）会留下来**，攻击者可以借这条信道把内核内存读出来。从此“分支预测”不再只是性能话题，也是安全话题——这也是为什么理解这条演讲的机制，对任何一个写系统级代码的人都不算多余。

工程侧的支撑则非常成熟：Linux 的 perf 工具可以直接统计 `branch-misses` 事件，Intel 优化手册和 Agner Fog 的微架构手册给出了各代处理器的分支失败代价参考值，编译器社区把 cmov 化、likely 提示、PGO 打磨成了日常工具链的一部分。

## 三、批判性审查：哪些可信，哪些要打折

**证据充分的部分**：流水线与投机执行的机制描述、预测失败惩罚的量级、cmov 与分支预测的硬件权衡，全部与公开的体系结构文献和厂商手册一致，属于教科书级共识；“先测量再优化”的方法论在性能工程界没有争议。

**需要打折扣的部分**有三处。

其一，“无分支=快”这个标题式的口号天然容易被夸大。准确表述是：**只有消除“数据相关且不可预测”的分支才可能赚**。对可预测分支强行无分支化，反而可能更慢——cmov 的代价是两边都算，无论预测对错都要付；如果两个分支体里有一条长依赖链或一次访存，无分支版本的**最坏情况**好看了，**平均情况**却输了。演讲本身反复强调这一点，但社区转述时经常只留下“if 慢”这一个字节。

其二，微基准与真实负载的鸿沟。演示用的基准数据往往是特意造出来的“最坏分布”（如均匀随机数），生产代码里这么纯的不可预测分支并不多见；反过来，真实服务的分支分布会随流量漂移，实验室里测得的收益可能上线即蒸发。

其三，汇编层面的结论有时效性。同一份代码，换一版编译器、换一个目标微架构，cmov 化决策就可能翻转。这既是这门课的价值（教你读懂编译器在做什么），也是它的边界（具体结论必须逐版本重验）。性能工程的老规矩依然成立：先算法、再数据布局、最后才是指令级技巧。

## 四、认知提升与实用启示

**第一条：给热点代码装上“分支体检”。** 遇到性能问题不要先猜，用 `perf stat -e branch-misses,branches` 看一眼热点函数的分支失败率。失败率个位数百分比的分支根本不值得动手；只有看到百分之十几以上的失败率，才说明你真的抓到了一个“赌输 ”的热点。

**第二条：优先改数据，其次改提示，最后才改写法。** 排序、分桶、把偶发事件从热路径剥离，往往一行架构级的改动胜过一百行位运算体操；确定热点后，先试 `[[likely]]`/`[[unlikely]]` 和 PGO 这类不动代码结构的手段，最后才考虑手写无分支模式——并保持可读性注释，告诉半年后的自己这段代码为什么长这样。

**第三条：把“预测器是个学习者”内化成设计直觉。** 你的程序行为越有规律，CPU 替你跑得越快。这意味着：状态机要有稳定的转移模式、热路径上的分支要尽量与数据分布解耦、批量处理要成块成段。写代码时想象有一个学生在一旁记录你的每一步、试图预测你的下一步——别出难题给它。

## 视频论证结构图

```mermaid
flowchart TD
    A[分支指令进入流水线] --> B{结果可预测吗}
    B -->|有规律| C[预测器命中<br/>流水线全速]
    B -->|接近随机| D[预测失败<br/>清空十几级流水线]
    C --> E[结论一<br/>可预测分支近乎免费]
    D --> F{优化路线怎么选}
    F --> G[无分支写法<br/>cmov与掩码]
    F --> H[编译器提示<br/>likely与PGO]
    F --> I[重排数据<br/>排序分桶成块]
    G --> J[代价<br/>两边都算可读性降]
    H --> K[代价最小<br/>适合绝大多数项目]
    I --> L[让分支重新可预测]
    J --> M[铁律<br/>用perf测量后再动手]
    K --> M
    L --> M
```

## 结语

这堂课最大的价值，不在教你几招位运算，而在纠正一个因果：不是“分支慢所以要消灭分支”，而是“**预测失败贵，所以要喂给预测器一个可学习的世界**”。CPU 每天在你代码上下的几百万次赌注里，绝大多数都赢了——性能工程要处理的，只是它赢不了的那一小撮。看懂这一点，你对 `if` 的敬畏和对编译器的信任，都会落在该落的位置上。

## 延伸阅读

1. Fedor Pikus《The Art of Writing Efficient Programs》（Packt, 2021）— 本场演讲作者的系统化展开，性能工程方法论全覆盖
2. Wikipedia: Branch predictor — 从饱和计数器到 TAGE 的分支预测器技术脉络

## 参考来源

1. [CppCon 官方频道](https://www.youtube.com/@CppCon) — 演讲视频所在频道，站内搜索 "Branchless Programming in C++" 可直达原视频（据频道页面）
2. [Branch predictor - Wikipedia](https://en.wikipedia.org/wiki/Branch_predictor) — 分支预测器历代结构与准确率数据
3. [Spectre (security vulnerability) - Wikipedia](https://en.wikipedia.org/wiki/Spectre_%28security_vulnerability%29) — 投机执行副作用演变为安全漏洞的始末
4. [CppCon 官网](https://cppcon.org) — 大会与历年议程档案

---
> 本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵**爱卫生**（微信ID: gprshome201101）维护，聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。
> 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
