第8.10章: 为什么“零开销”与“零成本”毫无意义

English

概述

“零开销”和“零成本抽象”这两个说法在 C++ 与 Rust 社区里被反复念诵。它们出现在会议演讲、Reddit 帖子、博客文章,甚至标准委员会的讨论里。但这些口号不是工程概念,而是营销话术——更糟的是,它们常常被用来封杀那些有细致分析和证据的讨论。

本章将考察这些口号为什么在实践中毫无意义,为什么它们被习惯性地误用,以及为什么现实世界中的性能取决于二进制体积、存储层次结构、平台约束和经济成本,而不是口号。

1. 作为宣传的口号

在很多在线社区(r/cpp、r/rust 等)里,“零开销”被当作修辞武器使用。人们不去讨论取舍,而是直接断言:

“It’s zero‑overhead, so your argument is invalid.”

这不是工程,这是宣传。它阻止人们讨论:

这些没有一项是“零”。这些没有一项可以被优化掉。

2. 异常处理不是零开销

C++ 异常处理常被宣传为“零开销”,理由是正常路径中没有显式检查。但这忽略了一些真实代价:

对于崩溃是可接受的应用(也就是大多数现代应用)来说,std::abort() 显然更便宜。

Linux 的 overcommit 会杀掉进程,Android 和 iOS 会未经许可杀掉应用。用户并不在意,他们照样买手机。

所以说异常处理是“零开销”的说法根本不成立。

参见 Raymond Chen 的文章: “Zero-cost exceptions aren’t actually zero cost” 。

3. lambda 不是零开销

lambda 会复制代码。即使两个 lambda 的函数体完全相同,它们也会各自生成独立的实例。这增加了:

更大的二进制加载更慢、映射更慢,优化器也更难对它进行推理。这些没有一项是“零开销”。

4. 在 Windows x64 上 span 与 string_view 不是零开销

一个常见的说法是 std::span 和 std::string_view 是“零开销”抽象。这在 Windows x86‑64 上并不成立,原因非常具体:Windows x64 调用约定只把大小为 1、2、4、8 字节的参数放在寄存器里传递。

在 64 位平台上,std::span 和 std::string_view 都是 16 字节的聚合体(一个指针加一个大小)。在 Windows x64 上,这类 16 字节类型不满足寄存器传递的条件,因此总是通过内存传递。这纯粹是 ABI 规则,不是 STL 或 MSVC 的 bug。

在 32 位(i686)与 Windows on ARM 上,以及在 SysV x86‑64(Linux、BSD、macOS)上,16 字节聚合体可以通过寄存器传递,因此同一份 span/string_view 代码在不同平台上的调用约定代价并不相同。Godbolt 上的例子清楚地展示了 64 位 Windows 与 32 位以及 SysV ABI 之间的这种差异。

关键在于:“零开销”并不是某个抽象的普遍属性,它取决于 ABI。一个在 SysV 上看起来“零开销”的抽象,在 Windows x64 上可能贵得多。平台规则压倒口号。

5. 指令顺序与优化器复杂度

甚至只是改变指令的顺序也会影响性能。优化器必须推理依赖关系、别名关系和调度。

更多抽象 → 更多中间表示 → 更高复杂度 → 更少的优化。这就是开销,它不是零。

6. C++ 被采用是为了可移植性,而不是性能

很多人声称 C++ 被采用是因为它“快”。实际上,C++ 被采用是因为它:

性能往往是次要的,在许多场合甚至无关紧要。

关于这一点,我们会在讲 freestanding 与渐进式 Web 应用的那一章里进一步讨论,也会讨论移动应用的沙箱机制(Android、iOS、UWP)如何让原生应用启动更慢、更新更慢、分发更难。

7. PWA 可以比原生应用更快

考虑一个简单的媒体播放器。一个 PWA 可以:

而 VLC 的安装包超过 100 MB,SMPlayer 也差不多。更新代价高,启动更慢,因为整个应用都必须从磁盘加载。

我的 PWA Player 就是一个具体例子。播放器本身不到 100 KB,而浏览器中那个用高度优化的 C/C++/汇编写成的媒体引擎早已加载在内存中,并在多个进程之间共享。PWA 并不重新实现媒体引擎,它只是使用浏览器已经提供的那个。

这种架构让一个轻量的 PWA 在启动时间、更新成本和分发便利性上胜过重量级的原生应用。它直接反驳了“C++ 总是更快”这种简单化叙事。

在这里试试: https://pwa-player.pages.dev/

8. 经济成本是实实在在的开销

增加异常处理这类特性会消耗:

这些资源本可以用于:

经济成本就是开销。它虽然间接,却显著地影响着现实世界中的性能。

参见 Chandler Carruth 在 CppCon 2019 的演讲:

要点