<!-- title: 数据怎么摆，代码快十倍 -->
<!-- domain: programming -->

# 数据怎么摆，代码快十倍：一堂讲透缓存的编程神课深读

## 概述

在程序员的必修课清单里，有一堂快十年前留下的演讲，至今仍被反复引用：Mike Acton 在 CppCon 2014 上的《Data-Oriented Design and C++》。它不是一堂 C++ 语法课，而是一次对默认编程习惯的公开审判——你以为性能问题是算法问题、语言问题、编译器问题，Acton 说：都不是，是你摆数据的方式出了问题。这堂课的价值在于，它把“计算机体系结构”课本里躺着的那几页缓存知识，直接怼到了每一个写过 `class`、`new` 和指针的人面前。本文带你完整吸收这堂课的论证结构，并补充它背后的真实研究脉络与十年来被验证、被修正的部分。没看过视频也能读懂，看过视频的人会多懂一层。

## 视频基本信息

| 项目 | 内容 |
|---|---|
| 视频标题 | Data-Oriented Design and C++（数据导向设计与 C++） |
| 频道 | CppCon（C++ 大会官方频道） |
| 主讲人 | Mike Acton，时任 Insomniac Games 技术总监（代表作《瑞奇与叮当》系列），此后加入 Unity |
| 发布时间 | CppCon 2014 大会演讲（2014 年 9 月，美国贝尔维尤），会后上传至官方频道 |
| 时长 | 约 1 小时 |
| 热度 | 据频道页面，播放量为百万级，是 CppConv 历史上被引用、被讨论最多的演讲之一 |

## 一、开场炮：硬件不是你的朋友

这堂课最出名的不是某个技巧，而是 Acton 的开场姿态。他几乎是在挑衅台下：你们大多数人根本不知道自己的硬件每秒钟在干什么。CPU 的主频、缓存的层级、内存的延迟、总线的带宽——这些参数写在每一个规格表上，但绝大多数程序员写代码时假装它们不存在。

他的原话大意是：“硬件不是你的朋友。”这不是说硬件不可靠，而是说硬件的优化逻辑和程序员的直觉完全相反。程序员直觉里，代码是顺序执行的、内存是均匀的、访问任何一个变量的代价差不多。而真实硬件上，一次内存访问和一次缓存命中的代价可以差上百倍。Acton 的第一个论点由此立住：**写高效程序的前提，是先承认硬件的真实脾气，而不是活在抽象的幻觉里。**

这里值得补一层视频里没有展开的数字感：一颗 3GHz 的 CPU，一个时钟周期约 0.3 纳秒；而一次主存访问通常是几十到上百纳秒。也就是说，缓存未命中一次，CPU 要空等几百个周期。这个量级差距，就是整堂课所有论证的物理基础。

## 二、程序的本质：变换数据

Acton 抛出的第二击，是对“软件是什么”的重新定义：**软件就是对数据的变换（transformation of data）**。你写的每一行代码，本质上都在描述“输入数据如何变成输出数据”。因此，设计程序的起点不应该是对象、类、设计模式，而应该是数据本身。

他给出了四个必须在动手前回答的问题，这也是整堂课最实用的框架：

1. 你有多少数据？
2. 这些数据的生命周期多长，创建和销毁多频繁？
3. 这些数据多久被读一次、改一次？
4. 这些数据以什么模式被访问——顺序扫过，还是随机跳跃？

这四个问题决定了一件事：数据在内存里应该怎么摆。Acton 认为，绝大多数性能问题的根子，就是程序员从来没回答过这四个问题，直接从“我要建哪几个类”开始写代码。

## 三、缓存行：性能差距的物理来源

接下来是全课的核心技术段：缓存行（cache line）。现代 CPU 和内存之间隔着 L1、L2、L3 三级缓存，数据搬运的最小单位不是字节，而是一整个缓存行——x86 上是 64 字节。哪怕你只想读结构体里的一个浮点数，硬件也会把周围整整 64 字节拖进缓存。

这个设计的赌注是“局部性”：如果你接下来要用的是旁边的数据，缓存就赚了。这里补一层背景：局部性原理是有正式学术出处的——Peter Denning 在 1968 年建立的局部性模型，是虚拟内存和缓存体系的理论地基；1995 年 Wulf 和 McKee 的著名论文《Hitting the Memory Wall》更是直接警告：CPU 越来越快而内存越来越跟不上的“内存墙”，将成为体系结构的核心矛盾。三十年过去，这堵墙非但没有消失，反而更高了。

Acton 用一个经典演示把抽象道理变成体感：屏幕上大量粒子各自带有位置、速度、颜色、名字等一堆字段，每帧更新时你其实只用其中两三个字段。如果把对象一个挨一个存（面向对象的自然写法，业界称为 AOS），硬件每次都要为两个浮点数拖回 64 字节，其中大半是当帧完全用不上的颜色和名字；如果反过来按字段拆开、把所有位置连续存放、所有速度连续存放（AOS 改 SOA），一行缓存里全是有效数据，缓存命中率暴增，硬件预取器还能顺着连续地址提前把数据喂进缓存。两种写法在演示机上的差距是数倍——而算法一行都没改。

顺带多懂一层：缓存行至今仍是硬件厂商的博弈点。x86 长期是 64 字节，而苹果 M 系列芯片用的是 128 字节缓存行——为移动端能效做的取舍，也意味着跨平台的高性能代码不能把 64 当成宇宙常数。另外在多线程场景下，如果两个核心各自频繁写同一缓存行里的不同变量，缓存一致性协议会让这行数据在两个核心之间来回踢皮球，这就是所谓的“伪共享”，是数据摆放问题的并发版。

## 四、对过度抽象的审判

技术论证之后，Acton 转入哲学段，也是争议最大的部分。他把矛头对准了“默认面向对象”的工程文化：虚函数调用、堆上散落的对象、指针乱跳的引用关系，每一个都在破坏缓存的连续性；而“为了未来可能的扩展”设计的通用抽象，往往在当下就是纯粹的税。

他那句被引用最多的话大意是：我们不解决通用问题，我们解决的是具体问题。通用方案解决的是通用问题，而你手里那个问题并不通用。在 Acton 看来，不加辨别地套用抽象，本质上是把工程决策让渡给了习惯。

需要说明的是，把这段读成“面向对象已死”是误读。Acton 的立场更精确：抽象不是免费的，每一次抽象都要付代价，而付不付这个代价，应该由数据和访问模式说了算，不是由教条说了算。他在演讲里也承认，工具用在对的地方就是好工具——他反对的是把某种范式当成默认信仰。

## 五、数据导向设计是什么，不是什么

最后收束成方法论：数据导向设计（Data-Oriented Design，DOD）不是让你去写汇编，也不是放弃高级语言，而是一套决策顺序——先回答数据的四个问题，再按访问模式决定内存布局，然后才写变换逻辑，最后用性能工具验证，而不是凭感觉判断。

这套思路此后在游戏工业里结出了果实。最著名的是实体-组件-系统（ECS）架构：把“实体”从一个大对象拆成一列列组件数组，系统按列批量处理——本质上就是 SOA 思想的结构化落地。Unity 的 DOTS 技术栈把 DOD 做进了主流商业引擎的官方路线，据公开报道其落地过程几经调整，但《守望先锋》团队在 GDC 2017 公开的 ECS 实践、以及各家自研引擎对“数据布局”的重视，足以说明这堂 2014 年的课如何改变了行业默认值。另一层延伸在科学计算领域早已是常识：NumPy、Pandas 之所以快，正是因为它们把同类型数据连续存放、整列批量处理——很多 Python 用户每天在享受 DOD 的红利而不自知。

## 批判性审查

这堂课的硬核部分证据充分：缓存层级对热循环性能的主导地位、AOS 与 SOA 的数倍差距、内存墙的物理趋势，都可以用 `perf`、VTune、Instruments 等工具在自己的机器上复现，属于可测量、可证伪的主张，这也是它十年不衰的根本原因。

但有三处需要打折听取。其一，演示的数倍加速针对的是“每帧扫过海量小对象”这种游戏引擎典型负载，不能外推到所有程序——对一个瓶颈在磁盘和网络上的业务系统，改数据布局大概率是白费劲，Acton 讨论的语境始终是热路径上的热数据。其二，对面向对象的批判是修辞层面的简化：现代编译器对连续布局的代码能自动向量化、自动内联，但对指针乱跳的结构无能为力——这恰恰反过来支持 DOD，不过它同时说明问题不在“对象”这个概念，而在“对象放在堆上、用指针串起来”这个默认实现。其三，DOD 常伴随可读性代价：按字段拆散的数据让调试器和断点不再直观，团队协作时这类代码需要更强的约定和注释，这是真实的工程成本，演讲里着墨不多。

## 认知提升与实用启示

**第一，动手前给数据做一张“体检表”。** 把 Acton 的四个问题写成你设计文档的第一节：数据量多大、生命周期多长、读写频率多高、访问模式是扫还是跳。热路径上的关键数据结构，先回答这四问再决定布局。你会发现很多“要不要加一层抽象”的争论，答案就藏在这张表里。

**第二，用测量代替信仰。** 性能是工程事实，不是观点。改动前后跑一次 `perf stat -e cache-misses`（macOS 用 Instruments，GPU 用对应 profiler），看缓存未命中数有没有真的降下来。从这堂课学到的最重要习惯是：任何“我这样写更快”的说法，包括 Acton 自己的，都要落在计数器上验一遍。

**第三，从最热的一个循环开始练手。** 不要试图把整个系统改造成 DOD。挑一个 profiler 里最烫的循环，把它的输入数据从对象数组重构成字段数组，对比耗时。一次成功的重构经验，比十篇讲缓存的博客更能改变你写代码时的直觉。顺带一提，如果你写 Python，用 NumPy 把逐对象循环改成向量化运算，就是在做同构的练习。

## 视频论证结构

```mermaid
flowchart TD
    A["程序的本质<br/>是数据变换"] --> B["先给数据做体检<br/>多少·多久变·怎么访问"]
    B --> C["按访问模式摆数据<br/>SOA 按列连续存放"]
    C --> D["缓存行装满有效数据<br/>预取器顺流喂料"]
    D --> E["未命中大减<br/>自动向量化顺水推舟"]
    E --> F["热循环快出数倍<br/>且更省电"]
    B --> G["默认对象写法<br/>AOS 整对象散落堆上"]
    G --> H["只为几个字段<br/>拖回整行 64 字节"]
    H --> I["缓存未命中<br/>CPU 空等上百纳秒"]
    I --> J["性能差数倍<br/>抽象变成了税"]
```

## 延伸阅读

1. Richard Fabian《Data-Oriented Design》：把这堂课的思想写成完整专著，官网提供全文免费阅读（dataorienteddesign.com/dodbook/），游戏与高性能计算从业者均可读。
2. Chandler Carruth 在 CppCon 2018 的《There Are No Zero-Cost Abstractions》：同一频道里的另一堂名课，用编译器产物证明“零成本抽象”的账单藏在哪，与本文正好构成上下篇。

## 参考来源

1. [CppCon 官方频道](https://www.youtube.com/@CppCon) — 本视频官方出处，站内搜索标题 Data-Oriented Design and C++ 即可定位原始演讲
2. [Locality of reference — Wikipedia](https://en.wikipedia.org/wiki/Locality_of_reference) — 局部性原理条目，缓存与虚拟内存体系的理论源头
3. [Unity DOTS 官方页面](https://unity.com/dots) — 数据导向设计在主流商业引擎中的工业化落地
4. [Wulf & McKee, Hitting the Memory Wall（ACM SIGARCH, 1995）](https://scholar.google.com/scholar?q=Hitting+the+Memory+Wall+Implications+of+the+Obvious) — “内存墙”原始论文，全文可经学术检索获取
5. [Data-Oriented Design（Wikipedia）](https://en.wikipedia.org/wiki/Data-oriented_design) — DOD 概念脉络与游戏工业应用综述

---
> 本文由「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)
