bigywater.tech
Rust · 111

Zero-Cost Abstraction

“零开销抽象”常被当口号。这篇给它做实验:把 7 组高层写法与手写底层写法编译成汇编逐行比较,再看真正有开销的抽象是哪些,以及优化关闭时这个说法还成不成立。

4 / 7
高层写法与手写汇编逐行相同的组数
11 对 2 条
v[i] 带边界检查与 get_unchecked 的指令数
1.4×
debug 构建里 Newtype + 迭代器比裸写慢的倍数(release 里约 1×)

抽象与手写

实测 · 本机执行输出
  Newtype 包装                   高层   2 条指令 / 手写   2 条指令  逐行相同: 是
  Option<&T> vs 裸指针判空          高层   3 条指令 / 手写   3 条指令  逐行相同: 是
  iter().sum() vs for 循环       高层  60 条指令 / 手写  65 条指令  逐行相同: 否
  map().sum() vs 手写累加          高层  65 条指令 / 手写  65 条指令  逐行相同: 否
  泛型 max<T> vs 具体类型            高层   3 条指令 / 手写   3 条指令  逐行相同: 是
  闭包调用 vs 直接计算                 高层   3 条指令 / 手写   3 条指令  逐行相同: 是
  &dyn Fn 调用 vs fn 指针调用        高层   3 条指令 / 手写   3 条指令  逐行相同: 否

迭代器链的汇编见Iterator 链的性能;泛型单态化见泛型单态化。

开销来自哪里

实测 · 本机执行输出
  下标访问 v[i](有边界检查)             带安全检查/抽象的  11 条 / 无检查的   2 条
  checked_add vs 回绕加           带安全检查/抽象的   4 条 / 无检查的   2 条
  Rc 克隆 vs 复制 u32              带安全检查/抽象的   7 条 / 无检查的   2 条

debug 构建

实测 · 本机执行输出
  编译:成功
  debug  : Newtype + 迭代器 fold:    27.36 ms   裸 f64 + for 循环:    19.63 ms   结果相同: true
  release: Newtype + 迭代器 fold:     5.06 ms   裸 f64 + for 循环:     4.85 ms   结果相同: true

汇编指令数来自 aarch64 的 -O 输出,换平台数字会不同;耗时为本机单次微基准。

分步演示

零开销抽象
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。汇编用 rustc --emit=asm -O,规范化局部标签后比较
    • 没有验证:x86_64 汇编、LTO 与 PGO 对结论的影响、更大的程序里内联预算耗尽时的行为、Box 的虚调用相对静态分发的实际耗时(见静态分发与动态分发一篇)
    京ICP备2026067502号-1京公网安备11010502063487号