在编程圈,每年几百场技术大会演讲里,绝大多数沉寂无声,能突破百万播放的凤毛麟角。CppCon 2021 上 Fedor G. Pikus 的《Branchless Programming in C++》(C++无分支编程)就是其中之一——它讲的是一个每个程序员每天都在制造、却几乎没人真正理解的代价:你写的每一个 if,都是让 CPU 去赌一次。这条视频在性能工程圈长期霸榜,被无数博客、课程和面试题库反复引用;在 AI 推理成本被斤斤计较的今天,"如何压榨硬件每一分性能"的话题让它重新高频出现在技术社区的引用链里。本文带你完整吸收这条视频的论证链,并补上它背后的真实研究脉络——从 1981 年的分支预测论文,到 2018 年震动世界的 Spectre 漏洞。看完这篇,你不仅懂了视频讲了什么,还会懂得它为什么对。
说明:本文写作环境无法实时抓取 YouTube 页面,视频数据依据频道历史公开信息整理,播放量为百万量级(精确数字以频道页面为准)。
| 项目 | 内容 |
|---|---|
| 标题原文 | Branchless Programming in C++ |
| 标题中译 | C++ 无分支编程 |
| 频道 | CppCon(C++ 大会官方频道,@CppCon) |
| 演讲者 | Fedor G. Pikus(时任 Calculus AI 首席科学家,前 Google 高级工程师) |
| 发布时间 | 2021 年(CppCon 2021 大会演讲,据频道页面) |
| 热度 | 百万量级播放(据频道页面,本次未实时核验),CppCon 历史上传播最广的演讲之一 |
| 演讲者著作 | 《The Art of Writing Efficient Programs》(Packt,2021) |
多数程序员对性能的心智模型是"语句级"的:一行代码等于一份工作量。照此模型,if 很便宜——判断、跳转,不过一条指令的事。Pikus 的整场演讲,就是把这个模型连根拔起,换成一个更真实的模型:分支的代价不在判断本身,而在"猜错"之后。CPU 现代微架构下,一条分支如果预测正确,几乎免费;一旦预测失败,代价是流水线上十几个已完成的指令全部作废、冲刷重来。也就是说,同样的 if 语句,在不同数据面前,性能可以差出一个数量级。
这个"同代码不同性能"的演示,是整条视频最抓人的部分:同一段处理数据的循环,仅仅因为数据是否有序,耗时相差近一倍。这不是玄学,是分支预测器在起作用。
Pikus 的叙事层层递进,可以拆成五个要点。
现代 CPU 执行指令像工厂流水线:取指、译码、执行、访存、写回,多条指令同时处于不同阶段。流水线最怕的不是指令多,而是"下一步往哪走"不确定——遇到条件分支,CPU 必须在判断结果出来之前就决定流水线灌哪条路的指令。等结果?流水线会空转十几个周期,荒废殆尽。于是现代 CPU 的选择是:不等,先猜,猜了就干——这就是投机执行(speculative execution)。
视频用流水线冲刷(pipeline flush)的代价解释了为什么一次分支误预测要烧掉 15~20 个时钟周期:不是判断本身贵,而是"已经干完又得倒掉"的那批工作贵。
演讲中最反直觉的一点:你的 CPU 每秒钟要做几十亿次赌注,而它猜中的概率超过 95%,对规律性强的分支甚至超过 99%。它靠的不是水晶球,而是结构化的"经验记忆":
换句话说,分支预测器是一个靠训练工作的硬件学习系统——用历史数据训练、用模式匹配推断,和机器学习是同一个哲学。这也是视频标题"无分支编程"的真正含义:不是消灭 if,而是写出对赌徒友好、或者干脆不让它赌的数据与代码。
Pikus 明确划了边界,这是这条视频区别于网络上"炫技帖"的关键。分支优化只在以下场景值得:
一个每秒只跑几次的 if,怎么写都无所谓。
视频主体演示了几类消除分支的手法,每类都有真实工程权衡:
a > b ? a : b 在编译器开启优化时常被编译成 cmov 指令——两条路都算,选一条留下,没有跳转就没有赌注。暗刺:cmov 把控制依赖变成数据依赖,两条路都会执行,若某条路代价巨大(如解引用空指针)反而出 bug 或更慢。x & mask 直接把条件"算"出来。最后一个要点是反浪漫的:现代编译器在 -O2 级别会自动做 if-conversion、循环展开、分支重排,很多"手工无分支魔法"编译器早替你做了,手写版本甚至可能妨碍它。所以 Pikus 的结论是工程化的:先 profile(用 perf stat 看 branch-misses 硬件计数器),确认分支误预测真是瓶颈,再动手;动手后还要再测量,确认真的变快了。
视频的每个主张都能对应到真实的文献与工程史,这条谱系本身就是半部计算机体系结构史:
1. 流水线与分支问题的起源(1960s)。IBM 的 Stretch 项目(1961)和 Seymour Cray 设计的 CDC 6600(1964)最早大规模使用指令流水线,也最早遇到"分支让流水线断流"的问题。Stretch 用简单的静态预测(总是猜顺序执行),这是分支预测的史前时代。
2. 动态分支预测开山之作。James E. Smith 1981 年论文《A Study of Branch Prediction Strategies》(发表于 International Symposium on Computer Architecture)首次系统提出动态预测与 2 位饱和计数器,视频里"赌徒"机制的学术源头就是它。这篇论文后来被公认为计算机体系结构领域引用最高的经典之一,Smith 也因此在 1999 年获 Eckert–Mauchly 奖。
3. 现代预测器架构。Tse-Yu Yeh 与 Yale Patt 1991 年的《Two-Level Adaptive Training Branch Prediction》提出两级自适应预测(用分支历史索引模式历史表),奠定了此后三十年主流 CPU(包括今天 Intel/AMD/Apple 芯片)预测器的骨架。今天的 TAGE 类预测器(Seznec 与 Michaud 于 2006 年提出)仍是这个思路的进化版。
4. 官方工程背书。Intel《Optimization Reference Manual》专章讨论分支预测与无分支代码,直接建议开发者对不可预测分支使用条件移动与数据重排——视频的建议不是个人口味,是芯片厂商的官方工程指南。
5. 那个让全世界记住分支预测的案例。2012 年 Stack Overflow 问题《Why is processing a sorted array faster than an unsorted array?》(问题编号 11227809)成为互联网史上最著名的性能问题之一:同一段代码,数据排不排序耗时差近一倍,高票答案用分支预测给出了教科书级解释。视频里的对照实验正源于这个公共知识。
6. 阴暗面:投机执行的反噬。2018 年 1 月,Google Project Zero 等团队公布 Spectre(Kocher et al.,《Spectre Attacks: Exploiting Speculative Execution》,IEEE S&P 2018)与 Meltdown(Lipp et al.)两族漏洞:攻击者利用分支预测与投机执行的"副作用"(缓存时序)偷取本不该读到的数据——预测器猜错的那些指令虽然被冲刷,但它们在缓存里留下的痕迹抹不掉。这是分支预测第一次从性能话题变成安全头条,几乎所有 1995 年后的 CPU 中招,全球被迫进行多年微码修补。视频里"CPU 是赌徒"的比喻,在 Spectre 之后有了更冷的注脚:赌徒会偷看牌。
7. 体系结构正统教材。Hennessy 与 Patterson 的《Computer Architecture: A Quantitative Approach》(《计算机体系结构:量化研究方法》)用整整章节阐述"分支是流水线的头号敌人",两人因此书与相关工作获 2017 年图灵奖。
证据充分的部分:分支误预测的周期代价(约 15~20 周期)、排序数据加速实验、2 位计数器机制、Spectre 与投机执行的因果关系——全部有论文、厂商手册和硬件计数器实测支撑,是体系结构领域的硬共识。
需要打折扣的部分:
1. 把"测量文化"带进日常。Linux 下一行 perf stat -e branches,branch-misses ./your_program 就能看到真实的分支误预测率;误预测率超过百分之几且在热点循环里,才值得动手。这条纪律适用于一切性能优化——先让硬件说话,再让直觉闭嘴。
2. 优化数据,往往比优化代码便宜。排序输入、调整数据布局、把随机访问变顺序访问,经常比任何代码魔法收益更大且不伤可读性。"预测器友好"本质上等于"模式友好"——这是从数据库索引到 AI 推理批处理共通的底层逻辑。
3. 高层语言里它同样存在,只是换了马甲。Python 解释器的主循环、JIT 的内联决策、数据库查询计划器的选择,全都在做"预测+投机"。理解了 CPU 分支预测,你就理解了整台机器乃至整个软件栈的一个基本哲学:用历史的模式换取未来的速度,并为猜错准备好退路。
flowchart TD
A[每个 if 都是一次分支] --> B[CPU 是流水线工厂]
B --> C{分支走向确定吗}
C -->|可预测| D[投机执行几乎免费]
C -->|不可预测| E[流水线冲刷<br/>损失 15~20 周期]
E --> F[硬件计数器确认<br/>branch-misses 高]
F --> G{值得优化吗}
G -->|否| H[保持可读代码<br/>先测量再动手]
G -->|是| I[无分支工具箱]
I --> I1[条件移动 cmov]
I --> I2[位运算与掩码]
I --> I3[查找表<br/>警惕缓存代价]
I --> I4[重排数据<br/>让预测器自己赢]
I1 --> J[改完再测<br/>确认真的变快]
I2 --> J
I3 --> J
I4 --> J
E --> K[副产物<br/>投机执行催生 Spectre 漏洞]
本文由「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 等场景全覆盖。
