首页 › programming › 你的代码,配不上你的硬件

你的代码,配不上你的硬件

导语

如果只允许给写了三五年代码、感觉遇到瓶颈的工程师推荐一场演讲,我会毫不犹豫地推这场:Mike Acton 在 CppCon 2014 上的《Data-Oriented Design and C++》(面向数据的设计)。

这场演讲出名,是因为它开场就掀桌子:你以为你在「写逻辑」,其实你在「搬数据」;你以为封装、继承、设计模式是「好设计」,其实它们很多时候只是没付过钱的奢侈品。Acton 时任 Insomniac Games(《瑞奇与叮当》《抵抗》系列背后的工作室)引擎负责人,他不是学院派理论家,而是被真实游戏的帧率deadline追着跑了十几年的实战派——他要回答的问题非常朴素:同样的算法,为什么别人的代码比你快十倍?

答案几乎从不在算法本身,而在数据怎么摆、缓存怎么用、硬件怎么想。这场演讲十年后仍在被反复引用,因为它讲的不是C++技巧,而是一套被大多数程序员跳过的基本功:理解硬件的真实成本。哪怕你不写C++,甚至不写游戏,这堂课都值得你花一个小时。

视频背景卡片

项目 内容
视频标题 Data-Oriented Design and C++(面向数据的设计与C++)
频道 CppCon(C++ 大会官方频道)
演讲人 Mike Acton(时任 Insomniac Games 引擎负责人)
发布时间 2014年(CppCon 首届大会)
时长 约1小时
播放量 数十万级(官方频道历年累计,以频道页面实际数据为准)
核心主题 面向数据设计(DOD):硬件成本、缓存友好与数据布局

核心内容拆解

1. 掀桌开场:硬件不在乎你的感受

[视频核心观点] Acton 开场攻击的不是某段代码,而是程序员的三种集体自负:第一,你以为你理解问题,其实你理解的是你对问题的想象;第二,你以为硬件在配合你,其实硬件根本不在乎你的架构漂不漂亮;第三,你把「业界惯例」当成了真理,却从没算过惯例的成本。他明确提出一个尖锐的判断:大多数程序员写出的慢代码,不是输在智力,而是输在先验地拒绝理解机器。

[延伸分析] 这段批评放在今天听反而更扎心。 abstraction 层层堆叠的现代开发环境里,一代工程师可以从没看过一条汇编、没算过一次缓存命中就交付产品——多数场景下这没问题,问题出在热点路径上:高频交易、游戏帧循环、数据库内核、网卡驱动,这些地方的规则完全不同。这很像通信行业的常识:优化空口之前先看信道,信噪比是物理给定的天花板,方案再花哨也只能在天花板下做事。Acton 的核心主张用一句话说就是:软件工程师的第一课,是承认自己活在物理约束里。

2. 程序的真相:一台搬运数据的机器

[视频核心观点] 演讲给出了贯穿全场的一句话定义:软件的本质是数据变换(data transformation)——输入、变换、输出。程序不是「对象的社区」,不是「类的艺术作品」,它就是一连串状态与状态的转换。所以设计软件的正确顺序是:先彻底理解问题,再把问题翻译成数据模型,最后才轮到代码。代码只是数据模型的「形状」自然长出来的部分。

[延伸分析] 这个视角的威力在于它把「设计」重新排序了。多数人拿到需求后的第一反应是建模类图——找名词、建对象、套设计模式;而 DOD 的顺序是先问三个问题:数据的体量有多大?访问模式是什么(谁在读、谁在写、按什么顺序)?每帧/每次请求的预算是多少纳秒? 这三问之后,很多「标准设计」会自己垮掉。有意思的是,这个思路在高性能领域早有共识:数据库设计者先想页与索引,HPC 研究者先想内存布局,编译器作者先想中间表示。Acton 做的事,是把这个「内核工程师的常识」搬到了普通应用开发者面前。

3. 一张延迟表,值十年经验

[视频核心观点] 演讲中最著名的一幕,是那张硬件成本表。以2013年前后的机器为参照,量级大致是:访问一级(L1)缓存约0.5纳秒,分支预测失败约5纳秒,跨到主内存约100纳秒,机械硬盘寻道约10毫秒。Acton 当场问观众「你这段代码大概多快」,几乎没人答得上来。他的观点:没背过这张表的工程师,等于不知道自己做的东西成本几何。

[延伸分析] 这张表后来演化成网上广为流传的「每个程序员都该知道的延迟数字」(Latency Numbers Every Programmer Should Know)。看它的正确方式不是记数字,而是记比例:主存比L1慢约两个数量级,机械盘又比主存慢约五个数量级——也就是说,一次缓存不命中再补上,和一次磁盘IO的差距,大约等于「走路去便利店」和「徒步横穿大陆」的差距。具体数字每年都在变(SSD、NVMe、新缓存层次),但比例结构十年未变。这也解释了为什么一切高性能系统的第一原则都是:能不碰主存就不碰主存,能把数据搬到离计算近的地方就搬。 顺带一提,这个「搬数据比做计算贵」的直觉,日后会成为理解GPU和AI推理的钥匙,这是 Acton 当年未必料到的。

4. 64字节:性能的地板与天花板

[视频核心观点] 进入正题后,Acton 讲了缓存工作的基本单元:缓存行(cache line),典型大小64字节。CPU不按变量取数,而是整块搬。于是两条铁律浮现:其一,顺序访问是免费的——硬件预取器(prefetcher)会提前把后面的数据搬进缓存;其二,指针追逐是毒药——每跳转一次指针,就可能触发一次缓存不命中,预取器完全帮不上忙。由此引出全场最实用的工具:AOS(Array of Structs,结构数组)与 SOA(Struct of Arrays,数组结构)之争——按「遍历方式」而不是按「业务对象」来组织内存。

[延伸分析] 举个视频里那类例子:你有一百万个敌人对象,每帧只更新其中两个浮点字段。若按传统 OOP 写法 vector<Enemy>(每个 Enemy 内嵌几十个字段),每读2个字段就要陪跑62字节无关数据,有效带宽利用率不到4%;改用 SOA 把「每帧要用的字段」单独抽成连续数组,同样的循环瞬间变成带宽友好型——没改一行算法,速度却换了一个量级。配套手段还有热冷分离(hot/cold splitting):把每次访问的字段和偶尔才碰的字段拆进不同结构。这套思想最漂亮的「远亲」是列式数据库: analytical 数据库(如 ClickHouse 这类)把表按列存储,本质上就是数据库级的 SOA。你在游戏引擎和数仓里会看到同一个主意,因为它们对抗的是同一个物理定律。

5. CPU最怕两种「惊喜」:分支与虚函数

[视频核心观点] Acton 接着拆解了两个被低估的成本。分支(if/switch):现代CPU靠流水线和推测执行榨性能,一旦分支预测失败,整条流水线作废重填,代价约在个位数十纳秒量级——看着小,在每秒百万次的内层循环里就是大头。虚函数:一次虚调用意味着一次间接跳转,地址运行期才知道,既打乱分支预测,又阻断内联优化——编译器看见的不再是确定的函数,而是一个谜。

[延伸分析] 这一节常被误读成「别用虚函数」,其实 Acton 的原意更精确:默认值不等于免费。虚函数、动态分配、异常,这些机制的 trouble 不在「用了」,而在「无意识地用在最内层循环里」。工程上对应的解法也成熟:把多态从热循环里提出来(比如先按类型分桶,再对每桶跑无分支的紧循环);用查表或算术替代小分支(所谓 branchless 写法);数据结构上用连续数组加索引替代指针图。补充一个演讲十年后的注脚:这些原则在今天不仅没过时,反而更硬了——SIMD 指令集要求规则、连续、无分支的数据流,GPU 的 warp 调度更是把「同一分支走向」从优化建议变成了硬性要求。当年是性能技巧,如今是并行时代的入场券。

6. 把「不可能」变成「不需要」:砍数据的艺术

[视频核心观点] 演讲中有个漂亮的段落,Acton 称之为挑战那些从未被挑战的前提(他用了「normalize the impossible」的说法):你真的需要64位浮点存这个坐标吗?真的需要字符串存这个状态吗?真的需要运行时动态分配吗?他举例说,游戏里大量数据完全可以用8位、16位定点数表达——每砍掉一半位宽,同一条缓存行就能多装一倍的有效数据。所谓优化,很多时候不是把代码写快,而是把数据变小。

[延伸分析] 这段在今天读尤其有既视感:AI领域这几年最热的工程方向之一正是量化(quantization)——把大模型的权重从16位浮点压到8位甚至4位,本质和 Acton 说的是同一件事:用「够用的精度」换「翻倍的带宽与缓存收益」。再往深处走一步,这里藏着一条更普适的工程哲学:性能问题常常是需求问题。规格说明书上每一个「顺便加个字段」「就用double吧」的随意决定,都在为未来每一帧的循环埋税。敢于回头质疑需求、给产品经理列出「这个字段的带宽账单」,是高性能工程师区别于普通工程师的分水岭。这不是抠门,这是对物理规律的尊重。

7. 封装不是免死金牌

[视频核心观点] 作为一场C++大会演讲,Acton 却对 OOP 传统冷嘲热讽: encapsulation(封装)被当成「好设计」的同义词卖了几十年,但封装本身并不让代码更快或更正确——它只是改变了代码的组织方式。把数据藏进对象再通过getter/setter访问,若因此放弃了控制内存布局的能力,那是在为一种美学付性能税。他同时澄清:他反对的不是抽象,而是从不核算成本的抽象。

[延伸分析] 这里的分寸感值得细品,因为它和「OOP已死」式的流量口号完全不同。Acton 的立场可以概括成一句话:抽象必须交租——每个间接层、每次泛化、每个虚基类,都要能回答「它换来了什么?值多少钱?在热路径上吗?」。答不上来的,默认砍掉。有趣的是,这和「面向对象」概念原作者的本意并不冲突——Kay 那一脉强调的是消息传递与行为封装,而非把内存布局绑死在继承树上;换句话说,被批判的其实是后人发明的「对象即数据+方法的表格」这种惯性写法。对普通开发者的启示很具体:写一个类之前先问,这个类十有八九会以什么顺序被批量遍历? 如果答案是「按某个字段全量扫描」,那它就该按数据布局设计,而不是按语法书上的UML设计。

8. 十年回响:一场演讲的身后事

[视频核心观点] 演讲收尾时,Acton 把希望寄托在听众身上:把硬件成本意识带回各自的团队,先理解问题、再理解数据、最后才写代码。

[延伸分析] 十年过去,这场演讲的影响远超游戏行业。游戏圈长出了 ECS(Entity-Component-System)架构:Unity 推出 DOTS 技术栈,Rust 社区的 Bevy 引擎把「数据导向」写进了设计哲学;Zig 作者 Andrew Kelley 2021年的演讲《Practical Data-Oriented Design》几乎是对 Acton 的隔空致敬,把同一套方法带进了系统编程圈;数据库、网络栈(如内核旁路方案里的预取与批量处理)、乃至大模型推理优化(KV cache 管理、量化、算子融合的核心都是「少搬数据」),处处是它的变体。一场2014年讲硬件缓存的演讲,成了AI时代基础设施的预习材料——这大概是它至今仍在被反复推荐的根本原因:物理定律不会过时,只会换一个行业再收一次学费。

知识结构图

flowchart TD
    A["软件 = 数据变换"] --> B["先懂问题"]
    A --> C["再懂数据"]
    A --> D["最后懂硬件"]
    B --> B1["拒绝用想象建模"]
    B --> B2["先画数据流"]
    C --> C1["AOS 换 SOA"]
    C --> C2["热冷字段分离"]
    C --> C3["砍位宽砍字段"]
    D --> D1["缓存行64字节"]
    D --> D2["顺序访问帮预取"]
    D --> D3["分支与虚函数有税"]
    D1 --> E["L1 纳秒级"]
    D1 --> F["主存慢两个数量级"]
    D1 --> G["机械盘再慢五个量级"]
    E --> H["抽象必须交租"]
    F --> H
    G --> H
    H --> I["成本意识进团队"]

实战启示

给一线工程师:把「测完再改」变成肌肉记忆。 下次想优化代码时,忍住直接上算法的冲动,先做三件事:找到真正的热循环(用 profiler,不要靠猜);画出这个循环触碰的数据在内存里的真实样子(连续还是散落?每步真正用了几个字节?);对着延迟表估一估每步的成本。多数情况下,把 vector<unique_ptr<T>> 换成连续存放、把冷字段拆出去、把内层分支提出来,这三板斧就能带来一个量级的改善——一行算法都不用动。

给架构师与团队负责人:把成本表贴进评审流程。 DOD 最大的组织学启示是「抽象要交租」可以制度化:设计评审时多问一句「这个接口会在什么体量、什么频率下被调用?单次成本估多少?」,就能拦下大量「顺手多包一层」的隐性债务。往大处看,选型时也多一个尺度:为什么分析型数据库几乎都走向列存?为什么高性能网络栈要批量收发包?它们的答案和 Acton 的是同一张考卷。

给 AI 时代的学习者:这是一把理解 GPU 与大模型的钥匙。 你以后会遇到 GPU 编程、推理优化、量化部署——所有这些领域的底层语言都是「数据怎么搬」。先用这场演讲在 CPU 上建立成本直觉,再去看 GPU 会轻松得多:所谓 SIMT,不过是把「顺序访问、批量处理、别让硬件等你」推到极致的产物。2014年的这一小时,是2026年基础设施工程的最短预习路径。

延伸学习路径

  1. CppCon 官方频道(youtube.com/@CppCon)—— 在频道内检索 "Data-Oriented Design and C++" 即可找到本片;同频道的编译器与性能专题演讲质量普遍很高。
  2. 《Data-Oriented Design》在线版(dataorienteddesign.com/dodbook)—— Richard Fabian 把 DOD 思想系统化成书,作者开放了全文在线阅读,适合作为本片之后的系统教材。
  3. Wikipedia: Data-oriented design(en.wikipedia.org/wiki/Data-oriented_design)—— 快速了解 DOD 的术语谱系与 ECS 的来龙去脉。
  4. 《Latency Numbers Every Programmer Should Know》(gist.github.com/jboner/2841832)—— 视频里那张成本表的社区维护版,数字会更新,比例是永恒的。
  5. 《Practical Data-Oriented Design》—— Andrew Kelley(可在 YouTube 检索)—— Zig 作者2021年的十周年致敬之作,用更现代的案例把同一套方法再讲一遍,与本片对照着看效果最佳。

一句话总结

这场演讲最大的价值,是把一句反直觉的话钉进了工程师的脑子里:代码的速度不是写出来的,是数据摆出来的——先理解硬件的成本,再谈设计的品味,你的代码才配得上你的机器。

参考来源

  1. Data-Oriented Design and C++ — CppCon — 本文解读的核心视频(如链接有变动,可在 CppCon 频道内按标题检索到达,内容以频道页面为准)
  2. CppCon 官方频道 — 演讲出处,历年高性能与底层主题演讲聚集地
  3. Data-oriented design — Wikipedia — DOD 术语、思想源流与 ECS 架构综述
  4. Data-Oriented Design (Richard Fabian) — 作者开放在线阅读的系统性教材
  5. Latency Numbers Every Programmer Should Know — 广为流传的硬件延迟数字速查表

本文由「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学通信知识星球海报-扫码加入