6IT 最近放出了《Efficient C++ Programming for Modern 64-bit CPUs》第 4 章草稿。题目讲 CPU Physics and CPU Cycles,听着像底层教材,实际是在拆一个工程误会:C++ 程序慢,很多时候不是算得慢,是等得太久。

最刺眼的是一组数字。寄存器里的简单操作通常约 1 个周期,L1D 约 3 个周期,L2 约 10-15 个周期,L3 约 30-70 个周期,主内存约 200-300 个周期。再往外到 SSD、HDD、fsync(),已经不是同一张账单。

这章真正有用的地方,不是给出某个万能技巧。它把高性能 C++ 拉回一个冷事实:离 CPU 核心越远,代价越陡。程序员能写代码,但 CPU 在付电气账、缓存账、预测账和落盘账。

成本先摊开:缓存之外就是断崖

原文反复说的“距离”,不能简单理解成光速限制。在芯片尺度上,更要命的是寄生电容和连接长度。线越长,驱动成本越高,访问延迟越难压。

这些数字不是所有 CPU 的铁律。平台、代际、频率、负载都会改变结果。但数量级很有参考价值。

访问位置 / 操作通常延迟工程含义
寄存器到寄存器简单操作约 1 cycle算术本身常常不贵
L1D cache约 3 cycles热数据最值钱
L2 cache约 10-15 cycles已经开始疼
L3 cache约 30-70 cycles共享缓存不是免费午餐
主内存约 200-300 cyclescache miss 才是真刀口
分支预测失败约 15-25 cycles流水线会被冲掉
持久化存储 / fsync取决于 NVMe、SATA SSD、HDD已经跨到另一个数量级

这里有一个很反常、也很容易被忽略的点:现代 CPU 计算太快了,快到很多业务代码的瓶颈不在“算”,而在“拿数据”。

你写一段看似漂亮的 C++,模板很精巧,指令数也不多。但数据散在堆上,访问路径像随机游走,CPU 就只能等。算术单元不是没能力,是经常没饭吃。

SoC、on-chip RAM 也不能被粗暴神化。原文的意思更接近:实际延迟仍可能落在主内存约 200-300 cycles 这一档,具体看平台。硬件名字变了,物理账本不会自动消失。

真正能管的:布局、命中、预测、转换

对开发者来说,这章最直接的提醒是:少迷信小语法,多盯数据路径。

std::vector 这类线性结构,通常比链表、树更友好。原因不神秘:连续内存更容易命中缓存,也更容易被预取器照顾。链表和树的问题不是“老”,而是节点分散,访问像踩石头过河。

这不等于所有地方都该换成 vector。插入删除、稳定引用、复杂索引结构,都可能让树或链式结构仍有意义。问题在于,很多性能讨论连数据访问形态都没量,就开始谈容器信仰。

TLB 也一样。TLB 是虚拟地址到物理地址转换的缓存。每次内存访问都绕不开地址转换,所以它重要。

但它不是普通应用必然撞上的墙。原文的边界很清楚:多数 vector-oriented C++ 程序里,DTLB miss 往往不是应用层第一问题。数据库、JVM、大内存应用、虚拟化环境,更容易把 TLB 成本踩实。

分支预测也别神化语法。C++ 的 [[likely]] / [[unlikely]] 看着像给 CPU 下命令,其实作用有限。现代 CPU 的动态分支预测才是主角。

除非你非常确定某个分支概率极端,比如错误处理、罕见边界路径,否则乱标大概率只是安慰自己。写了标注,不等于 CPU 就听你的。

对 C++/系统性能工程师,动作应该很具体:先 profile,再动刀。看 cache miss、branch miss、IPC、内存带宽、热点函数和调用栈,不要凭感觉改容器。

对后端开发者,重点不是把每行 C++ 写成竞赛题。更现实的动作是:热路径尽量顺序访问,批量处理减少随机 I/O,关键写入确认 fsync() 位置,区分 NVMe、SATA SSD、HDD。它们不能混为一谈。

我的判断:性能优化的分水岭,是从写代码转向管数据移动

我更在意的是,这章把一个行业分水岭讲得很清楚:现代 C++ 的高手,不只是会写更聪明的代码,而是知道数据在哪里、怎么移动、在哪里等待。

很多优化失败,不是因为开发者不努力。是努力方向错了。

他们盯着一处小语法,抠一两个分支,换一种写法。结果真正烧时间的是 cache miss、页表转换、随机访问、落盘同步。刀没砍在骨头上。

这有点像铁路时代的老问题。火车头再先进,如果货场堆放混乱、轨道切换低效,吞吐还是上不去。不完全一样,但结构相似:真正控制效率的,常常不是最显眼的发动机,而是货物怎么摆、路线怎么排、等待在哪里发生。

落到存储层,这个判断更硬。NVMe、SATA SSD、HDD 的延迟不是一回事。fsync() 对数据库尤其敏感。企业级 NVMe 的 fully-ACID 写入延迟可以到约 150 微秒,消费级 SATA SSD 可能是 0.5-10 毫秒,HDD 可能到几十毫秒。

到了这个量级,CPU 里的小修小补就很尴尬。你在前台省了几十个 cycle,后台一次同步写把账全吞了。

所以接下来最该观察的,不是哪种语法更“现代”。而是三件事:

  • 热数据是否连续,能不能提高缓存命中。
  • 分支是否可预测,能不能让常见路径更直。
  • 慢路径到底在内存、TLB、锁、系统调用,还是落盘。

“天下大事,必作于细。”放在这里不是鸡汤,是工程现实。现代 C++ 的“细”,不是把语法抠到发亮,而是知道每一次访问、每一次预测失败、每一次落盘,账记在谁头上。

这章草稿的价值,不在万能优化表。它逼人承认:性能不是代码修辞学,是硬件层级的会计学。会计不讲情面,离核心越远,账越贵。