# 克里斯·拉特纳：编译器、LLVM、Swift、TPU 与机器学习加速器

> LLVM 与 Swift 之父回顾编译器设计历程，并谈 TPU 等机器学习加速器的工程取舍。

- 原标题：Chris Lattner: Compilers, LLVM, Swift, TPU, and ML Accelerators
- 期数：特别节目
- 发布日期：2019-05-13
- 嘉宾 / 主题：克里斯·拉特纳
- 网页版：https://lexfridman-cn.pages.dev/ep/chris-lattner/
- 官方页面：https://lexfridman.com/chris-lattner/
- 精读字数：约 11174 字（阅读约 25 分钟）

> 本文为 lex.qiaomu.ai 基于原始访谈的 AI 改写中文精读，非官方翻译；引用请注明出处并链接网页版。

2019 年，你手里那部 iPhone 上的每一个 App，索尼用来渲染电影特效的机器集群，Google 生产服务器上跑的搜索服务，PlayStation 4 里的游戏，这些看起来毫无关系的东西，编译它们背后走的是同一套代码。

这套代码叫 LLVM。

它最初是伊利诺伊大学厄巴纳香槟分校的一个研究生项目，只有几个人在做。二十年后，它成了英特尔、AMD、英伟达、苹果、Google 这些平时在市场上往死里打的公司共同维护的公共地基。做这件事的人是克里斯·拉特纳（Chris Lattner）。他还顺手写了 Clang，那个今天几乎所有人都在间接使用的 C/C++/Objective-C 编译器，然后在苹果内部发起了 Swift。

这期 Lex Fridman 的访谈长达一小时十三分钟，信息密度极高。拉特纳几乎没讲什么励志故事，他讲的都是具体的：一个 for 循环怎么从语法树变成控制流图，寄存器分配为什么是编译器最硬的骨头之一，bfloat16 为什么把比特挪个位置就能让 TPU 快起来，以及他在特斯拉那五个月到底看到了什么。

## 一个只想读一年硕士的人，写出了二十年的基础设施

拉特纳的第一段程序是照着父母买的一本 Basic 教材敲进去的。他的回忆里没有什么顿悟时刻，只有一件事：敲进去，跑起来，敲错一个字符，然后盯着屏幕找原因。

“我开始的时候就是把书上的程序打出来，看看它们怎么工作，然后打错，再搞明白为什么跑不起来。”（克里斯·拉特纳）

这条路走得挺有意思。Basic 之后是 GW-Basic、QBasic、QuickBasic，都是微软系的，一层层往上叠。然后是 Pascal，然后是汇编，然后才是 C++。中间他还摸过 Smalltalk。Fridman 在节目里开玩笑说他走了黑魔法那条路，本来可以走 Lisp 那条更高层、更哲学、更嬉皮士的路线，结果一头扎进了机器。

这个选择很关键。Pascal 给了拉特纳两样东西：字符串类型、更强的抽象，以及第一次意识到“资源是要管理的”。真正改变他的却是 C。用 Pascal 的时候你写字符串，不用想它在内存里长什么样。用 C 的时候你必须想：这些东西是怎么被摆到内存里的。

他后来做的所有事情，都发生在这一层。

## 编译器在两头之间搭桥，两头都很麻烦

要理解拉特纳做了什么，得先理解编译器是什么。

他的定义很朴素：这是一道双边问题。一边是写代码的人，人不想写二进制，也不想为每一块硬件重新想一遍；另一边是跑程序的机器，机器的种类越来越多，x86、ARM、GPU，现在还有一堆专门为机器学习设计的加速器。编译器做的事，就是让人在想问题的抽象层级上思考，然后把他们写下的东西变成某块具体硬件能跑的东西。

“编译器的艺术，是让人类用他们想用的抽象层级去思考，然后让他们写的东西在某一块具体的硬件上跑起来。”（克里斯·拉特纳）

工程上的解法是把这个问题切成三段。前端负责解析某一种语言，C 的前端、C++ 的前端、JavaScript 的前端各不一样。中间是优化器。最后是后端，负责针对具体硬件生成机器码。

这样切是为了复用。编译器是极其庞大的软件，如果每种语言、每种硬件都要从头写一遍，成本谁都扛不住。所以大家把中间和后端标准化，让所有语言共用同一套优化和代码生成。LLVM 干的就是这件事。

今天跑在 LLVM 上的语言有一长串：Swift、Rust、Julia，还有 Clang 处理的 C、C++、Objective-C。它们语法差别巨大，语义模型完全不同，JavaScript 里“什么算假值”这件事和 C 里根本不是一回事，但这些差异全部留在前端。到了中间，它们变成同一种东西。

## 从语法树到控制流图：一个 for 循环的变形记

这是整期访谈里技术密度最高的一段，值得慢慢看。

前端解析完之后，代码变成一棵抽象语法树（AST）。你写了 a 加 b，树上就有一个加号节点，下面挂着两个子节点。你写了一次函数调用，树上就是一个 call 节点，挂着函数和参数。

然后开始“下降”（lowering）。语法树被转成中间表示，LLVM 用的那种叫控制流图（control flow graph）。

这里的差别用 for 循环最能说明。在语法树里，一个 for 节点下面挂着四个指针：初始化的表达式、条件的表达式、递增的表达式、循环体。层层嵌套，像俄罗斯套娃。

到了控制流图里，它变成几个基本块。一个块放初始化代码，一个块放循环体和递增，块与块之间用跳转连接。循环体执行完跳到条件判断，条件成立跳回循环体开头，不成立跳到出口。

每个块内部是一串直线排列的简单操作：这个加法，那个乘法，这里调用一次。没有嵌套，只有顺序和跳转。

为什么要费这么大劲换一种形状？因为**这种形状更接近汇编，也更语言无关**。JavaScript 那套让人抓狂的假值规则、Python 的隐式类型转换，全部可以留在前端处理掉。到了中间表示这一层，剩下的是纯粹的、干净的计算和跳转，所有语言共享同一套优化器。

## 编译器和神经网络图，像是同一个念头的两个版本

Fridman 在节目里问了一个很聪明的问题：你描述的这些东西，听起来和神经网络的图结构太像了。

拉特纳的回答点出了一个区别。

神经网络是层层不同的表示。输入进来是一层表示，经过一层变换变成另一种表示，越深层的表示越抽象。每一层都在做不同的事。

编译器是**一种表示，反复变换**。

同一个中间表示在编译器里被反复揉搓。找出循环里不随迭代变化的表达式提到外面去，找出重复计算，把 2 乘 x 换成左移一位，把常量提前算出来。这些变换是迭代进行的，一轮优化完了再来一轮，有时候一个 pass 会跑几十次。

两者都在做“变换表示”这件事，方向与层次完全不同。做机器学习系统的人可以从中拿到一个有用的判断：如果一层表示只被用一次，它大概是中间产物；如果一层表示被反复打磨几十轮，它才是真正的地基。

## 寄存器分配：编译器替你打的最硬的一仗

问编译器优化里最值钱的是什么，拉特纳直接说：寄存器分配。

场景是这样的。现代处理器里，内存慢，寄存器快，但寄存器数量极其有限。x86 在某些模式下只有十六个寄存器。而你写代码的时候随手就是几十个临时变量，循环里还套着循环，这段代码可能要执行几百万次。

如果循环里每个值都要往内存里读写一次，性能就废了。如果能把循环里所有活跃的值都塞进寄存器，速度会快一个数量级。

问题是怎么塞。同一个变量在函数的不同位置，活跃状态不一样。你在第三行定义了 x，在第五行用了它，之后再也不碰，那 x 在第五行之后就是“死的”，它占的寄存器可以给别人用。编译器要做的事情，是把整个程序看成一个有时间维度的图，然后在这个图里给每一个活着的值分配物理资源。

“你写的是 x，编译器看到的是四个不同的值，每一个都有它在整个函数里的生命周期，每一个都可能被放进寄存器、放进内存、或者在某些位置干脆重新算一遍，而不是从内存里读出来。”（克里斯·拉特纳）

这一步难在组合爆炸。可能的分配方案有无数种，而且这个决定会连锁影响后面所有的优化。寄存器分配做砸了，前面所有的优化都白搭。

另一场硬仗是指令调度。RISC 架构把流水线塞进了处理器，处理器能同时处理多条指令，前提是指令之间的顺序不能让流水线卡住。编译器的活是把指令重新排序，让流水线一直有活干。这一步的约束条件极其复杂，牵扯到各个指令的延迟、依赖关系、硬件端口数量。

这些技术都被研究了几十年，教材里都有。把一个算法变成能在几百人协作的代码库里稳定工作的实现，是另一回事。拉特纳反复强调，编译器的难点落在**软件工程**上。

## 让死对头坐到一张桌子上

LLVM 最了不起的地方，跟技术创新关系不大。

拉特纳自己说得很清楚：LLVM 里实现的各种算法，大部分在学术界早就有了，教科书上都写着。他的贡献是**标准化**，是把中间表示和后端做成一个大家都能用的公共层，然后把这件事做对了。

对到什么程度？Google 和苹果在同一个代码库上提交代码，AMD 和英特尔在同一个代码库上提交代码，英伟达和 AMD 在图形这部分提交代码。这些公司平时在法律诉讼和市场份额上打得不可开交。

它们为什么愿意合作？**因为自己实现一遍太贵了，而且更缺的是人。**编译器工程师是极难招的一类人，就算是大公司，也没办法为一个内部项目凑出一支顶级团队。共享基础设施是唯一理性选择。

更有意思的是这个基础设施被用到了它从没被设计过的地方。索尼把 LLVM 用在了电影制作的图形编译流程里，用拉特纳的话说，这让他们能做更好的特效。这说明分层做对了，接口抽象做得干净，才会有人拿它去做原设计者完全没想到的事。

反过来看 GCC。GCC 在它擅长的领域里非常强，但它是一个 C 编译器，一个基础设施。它的代码里塞满了全局变量，为特定用途设计，很难被复用到别的地方去。差别出在架构决策上，而这种差别要十年、二十年才显现出来。

社区治理也是同一套逻辑。今天 LLVM 有几百个贡献者，靠的是一套“代码所有者”制度。硬件厂商拥有自己硬件相关的那些代码，负责确保补丁被及时 review，负责这个方向上的架构决策。拉特纳名义上还在这个金字塔的顶端，他早就不 review 每一个补丁了，他做的事是调解技术分歧，保证整个社区在往前走。七年前还成立了 LLVM 基金会，管钱、管活动，不碰技术方向。

有意思的是，拉特纳在研究生阶段写的第一版寄存器分配器，后来被一个“比他聪明得多的人”整个换掉了。他把这件事当成 LLVM 设计成功的证据：模块化的好处就是别人能替换你的代码。

## Clang 的起点，只是想把报错信息写清楚

2005 年拉特纳加入苹果。LLVM 在苹果内部落地，赶上了好几次大的架构迁移：转向英特尔、转向 64 位、iPhone 转向 ARM。因为是模块化的，每次迁移的成本都低得惊人。

开发者体验却很糟糕。当时的 Objective-C 程序员面对的报错信息、编译速度、工具链，都不怎么样。

然后他做了一件很拉特纳的事：谁也没告诉，下班之后自己写一个 C 编译器试试。

“我不会跟任何人承诺，也不告诉任何人。我就是晚上和周末开始写。”（克里斯·拉特纳）

写着写着他撞上了预处理器。这个东西人人都不喜欢，它极其复杂，里面塞着一堆古怪的历史包袱，比如三字符组（trigraph）这种鬼东西。而且它正是编译性能问题的一个关键症结。写到这里他发现，难和不可能之间有一道鸿沟，很多人一辈子没跨过去。

他跟经理说了。经理的反应是：太好了，这个问题我们确实要解决，我再给你一个人。

Clang 的目标定得很务实：更好的报错信息，更快的编译速度，还有 GCC 从来不支持的新的工具能力，比如重构工具和静态分析。要做到这些，编译器必须在内部维护大量额外的信息，还要保证这些信息不拖慢编译。这是个很精细的平衡。

C++ 是这里面最难啃的部分。规范文档厚得像砖头，语义规则之间的相互作用极其复杂。拉特纳的说法是：它几乎不可能实现，差一点就真的不可能。

跨过去的办法只有一个：一块一块地建，增量地做。还得招到真正懂各个子系统细节的工程师，一个人不可能同时精通模板推导、异常表、ABI 兼容性这些完全不同的深水区。

## Swift 真正的阻力来自会议室

C++ 支持刚做完第一版，拉特纳脑子里冒出来一个念头：这语言有太多丑陋的地方，实现它的过程中你不可能不想到“一定存在更好的东西”。

于是他又一次开始了夜间项目。没有承诺，没有汇报，就是玩玩看。做完一些原型之后，他觉得这条路走得通，才去找当时的软件高级副总裁伯特兰·塞莱特（Bertrand Serlet）聊。塞莱特本身对语言有兴趣，给了不少早期指导，也鼓励他把这事推下去。之后才是告诉经理、告诉更多人。

技术上最难的部分已经过去了，剩下的是社会层面的阻力。

当时苹果内部的共识是：iPhone 之所以成功，是因为 Objective-C。这话背后有真实的文化支撑。当年苹果招软件工程师，招的就是喜欢 Objective-C 的人；很多高层和骨干工程师的文化记忆一路追溯到 NeXT 时代，那是 Objective-C 真正成型的年代。他们是“写 Objective-C 长大的”。

在这种氛围里提“我们做个新语言”，接近于异端。

拉特纳的观察角度不太一样。他觉得苹果内部喜欢 Objective-C 的人确实很多，最敢说话的那批人也确实是真爱，还有一大批人每天都在被这门语言的尖角割伤。它难学，它有各种奇怪的边界情况，它对新人不友好。

真正让这件事从“异端”变成“必须做”的，是一个词：**内存安全**。

Objective-C 的对象系统整个建立在 C 的指针之上，它本身就是 C 的一层扩展。指针不安全，这件事没得商量。**只要你保留指针，你就没法在语言层面解决内存安全问题。**你不能靠给 Objective-C 打补丁解决，因为指针就是它的地基。

这句话把讨论从“要不要”变成了“怎么做”。一旦承认 Objective-C 修不好，剩下的问题就是：新语言该长什么样？这完全是另一个阶段的工作了。

## 渐进式披露：为什么第一行代码必须能跑起来

Swift 的设计里，拉特纳最看重的一条原则来自 UI 设计：**复杂度的渐进式披露**。

它的意思是，一门语言应该允许人从最简单的入口进来。Swift 的第一课是打印一行 hello world。一行，没有 main 函数，没有头文件，没有 Java 那句 public static void main。Python 也是这样，这正是 Python 能成为编程第一课的原因。

然后往下走。加变量，写一句 var x = 4，什么叫变量，x 加 1 是什么意思。加控制流，if 怎么回事，for 怎么回事，while 怎么回事。加函数。加类。加泛型。加模块。每一层都比上一层复杂，每一层都能独立地跑起来。

“这在编译型语言里非常不典型。”（克里斯·拉特纳）

为什么编译型语言做不到这一点？因为它们必须在编译期知道所有类型、所有边界。这些信息对性能、对重构工具、对一切都有好处，代价是初学者一进门就要面对一堆声明和样板代码。Swift 想做的是保留静态类型的全部好处，同时把入口的门槛降到和 Python 一样低。

这件事的另一面是给不同的人留不同的层级。写库的人想要能钻到底层去抠细节，用库的人只想在高层拼装。一门语言如果只能服务其中一种人，它就废掉了一半的生态。

还有一个容易被忽略的点：Swift 不只做静态编译。它在 Colab 和 Jupyter 里被当作脚本语言用。你敲一行代码，背后发生的事情是这样的：先起一个 Unix 进程，然后每一行输入都过一遍 Swift 编译器前端、优化器、代码生成，最后把机器码**注入到那个正在跑的进程里**，边跑边替换。你在页面上敲代码的时候，它在原地打补丁。

“它能做到这件事，是因为设计时就考虑到了。”（克里斯·拉特纳）

拉特纳对编译这件事的看法也因此变得很不一样：静态编译、动态编译、解释执行，这些选项处在一条光谱上，彼此之间没有墙。把栈分好层，你就能在光谱上自由移动，不会把自己锁死在某一头。

## 一千二百行 Swift 代码，把 Python 拖了进来

Swift for TensorFlow 里有一个看起来像魔术的功能：在 Swift 里 import numpy，然后直接用。

拉特纳解释了实现原理，简单得出人意料。

一个动态类型语言可以有两种理解方式。大多数人会说 Python 没有类型。另一种理解是：**Python 只有一种类型**，就是那个 python object，所有东西都是它，正因为只有一种，类型才是隐式的。

Swift 有很多类型，数组、字符串、各种类。现在给它加上第二种：python object。

于是 import numpy 拿回来的是一个 python object，也就是那个模块。你调用 np 点 exp，Swift 做的事情是：告诉 Python 解释器“我要取这个对象上叫 exp 的成员”，拿回来又是一个 python object；再告诉解释器“把这个对象用这些参数调一下”，拿回来还是 python object。每一次都只是动态查表加一次跨界调用。

这一层胶水，纯 Swift 写的，只有一千二百行左右。它建在 Swift 跟 C 的互操作能力之上，因为跟 Python 解释器打交道本质上就是 C 层面的调用。

要让这一千二百行能写出来，Swift 语言本身必须先加两个特性：动态成员查找和动态调用。这两个特性的提案、实现、标准化、进入语言，花了一年。

**用户看到的是一行 import。支撑这一行的是语言层面的新能力。**这正是 Swift 这类有社区提案流程的语言能做到、纯库方案做不到的事。Python 那边为了机器学习加过什么语言特性？拉特纳想了一下，说大概只有 @ 这个矩阵乘法运算符。

## TensorFlow 本质上是一个编译器，这句话值得展开

拉特纳提了一个视角，理解 TF 生态的混乱局面很有帮助。

TensorFlow 干的事情是：拿模型进来，让它在硬件上跑得飞快。这就是编译器干的事。它有前端，有优化器，有多个后端。

按这个框架看，Swift for TensorFlow 只是又一个前端。它和 Python、Go、Rust、Julia 的绑定共享同一套图表示、同一套运行时、同一套后端。

前端之间并不平等，它们分成三类。

第一类是 Python。它特殊，因为社区绝大部分的工程投入都在这边，它有自己的自动微分方案，有自己的 API 层。它的天花板也被这个位置限死了：你只能在 Python 这个库的边界之内做优化，改不了语言本身。

第二类是其他语言的绑定。它们就是包装层，调用 TF 的运行时 C API，没有自动微分，没有太多别的东西。

第三类是 Swift，只有它一个。它的思路是：既然我们可以改整条栈上的任何一个环节，那就从头问一遍，人和编译器之间的分工应该怎么切？

切法的核心在两处。第一处是类型系统。有了类型信息，编译器可以自动帮你构图，你不用手动想这些。汇聚、融合、优化，全都是白送的。第二处是自动微分，拉特纳认为这是 Swift for TensorFlow 最关键的贡献之一。

自动微分这件事的历史比大多数人想的要长。七十年代做数值计算的人写 Fortran，那时候就有一类工具叫源到源翻译器：你把自己的代码喂进去，它吐出来一份新的 Fortran 代码，里面带着你所有函数的导数。那是一整套技术积累，包括大量的优化技巧，包括怎么处理数值不稳定的各种花招。

问题在于，这些技术很难搬到今天的执行模型里。现代框架一次只能看到一个算子，看到一个加法、一个矩阵乘，而七十年代那批工具需要看到整个函数才能推理。**当语言本身集成了自动微分，你就能重新打开这四十年的积累，把它们搬过来用。**只是语言集成这件事本身也有一堆设计空间和复杂度要处理。

## bfloat16：把比特挪个位置，性能就上去了

TPU 是软硬件协同设计最典型的样本，拉特纳在访谈里讲得挺细。

当时已经是第三代 TPU，整块液冷机箱的算力超过一百 petaflops。他的原话是“我们还没把想法用完”。

关键是那个数字格式：bfloat16。

它是 16 位浮点，但不是 IEEE 那种 16 位。标准半精度浮点把更多的位给了尾数，牺牲了指数范围。bfloat16 反过来，尾数更短，指数更长，和 32 位浮点一样长。

代价是精度更低。收益是能表示的范围和 float32 一样大。

为什么这个交换在机器学习里划算？因为训练过程中你会遇到极小的梯度，那些数字小到标准半精度直接下溢变成零，而它们恰恰是推动模型学习的东西。同时你也会遇到数量级很大的值。**两头都要覆盖，中间精度松一点没关系。**

更妙的是硬件成本。乘法器的面积和延迟跟尾数位数的平方成正比，跟指数位数是线性关系。把位从尾数挪到指数，硬件便宜了，模型效果没掉。

甚至还有理论说，精度降低反而能提升泛化能力。这个说法未必站得住，结果摆在那里：便宜、够用、有效。

这个格式的来历也挺有意思。它最初来自研究侧，那批人在想办法压缩网络传输中权重的体积，找各种压缩方案，后来这个方案被直接烧进了硅片。从一个传输优化的小技巧，变成了 TPU 性能的核心组成部分。

这就是拉特纳说的那个“只有 Google 能做的三合一”：底层编译器和芯片设计、上层软件框架、以及算法本身，三层可以同时改动。算法在变，硬件从设计到量产要几年，你必须押注几年后什么算法会流行，然后把晶体管花在最值的地方。

## MLIR：LLVM 二十岁了，世界变了

访谈前三周，Google 在一个编译器会议上公布了一个新项目，MLIR（Multi-Level Intermediate Representation）。

背景是这样：TF 这条栈里嵌着一堆来自不同厂商的编译器。Google 有 XLA，英伟达有 TensorRT，英特尔有 nGraph。它们各自解决一部分问题，都在重复造轮子，都在想办法接进 TF。

MLIR 想做的是给这些子系统提供一层公共基础设施。第一步是让它们能插到一起，共享代码。更长远的目标是让整个行业开始协作，不要一遍一遍重新发明同样的东西。这套逻辑在传统编译器领域已经被验证过一次。

有人开玩笑说，MLIR 就是从 LLVM 学到的一切教训的 LLVM 版本，是一次修正错误的机会。拉特纳自己也承认这一点：LLVM 在它被设计出来要解决的问题上非常强，二十年过去，人们开始解决更高层的问题，需要新的技术。

这句话背后有一个很朴素的道理：**一个好的抽象层会活得比它的设计者预期的更久，然后开始显出老态。**这不是失败，成功的代价本来就是如此。

## 开源这件事，Google 和苹果的差别在哪

Fridman 问了一个直接的问题：苹果和 Google 在开源上的文化差异。

拉特纳的回答没有绕弯：他更认可 Google 的做法。他给了苹果一个公道的解释：苹果的文化有它的历史成因，那是三十五年前形成的路径，苹果也在适应。

他的论证是这样的：一家公司关心赚钱，这完全理性。真正让你赚钱的东西是什么？是那个做得极好的产品、那个差异化的体验，不是你的字符串库。**把字符串库捂住不放，今天早就不是关键的那一层了。**十五年前可能还是，今天的平台竞争不在那儿。

Google 开源 TensorFlow 这件事，拉特纳看来是一次“深刻的、极其聪明的洞察”。他特意设想了一个反事实的世界：一家公司说机器学习是我们的核心竞争力，我们不给别人。那个世界完全可能发生。TensorFlow 开源改变了整个机器学习领域，引发了一场独立的革命，同时在很多层面回馈了 Google 自己。

## 特斯拉那五个月：他respect愿景，不respect所有方法

2017 年前后，拉特纳在特斯拉待了大约五个月，职位是自动驾驶软件副总裁。他赶上的时间点是特斯拉从 Hardware 1 切到 Hardware 2，从第三方提供的视觉栈换成自研视觉栈。

他对这段经历的评价很坦率，也很体面。

Hardware 1 最初的设计目标其实很简单：交通感知巡航、车道保持这类功能。能在这个硬件上做到后来那种程度的辅助驾驶，他认为相当惊人。当时最大的挑战，是从第三方视觉栈往自研栈上迁。这一步时间压力很大，好在他可以沿用原来那支团队的知识和经验。

然后他说了一句很重的话：

> 我在特斯拉的时候，经历了也目睹了我此前在任何公司见过的最高的离职率，这有点让我吃惊。（克里斯·拉特纳）

紧接着，他说他从这段经历里学到并开始尊重的一件事：马斯克能吸引到极其优秀的人，因为他有非常清晰的未来图景，并且能让人相信那个图景。街角随便一个人都能喊“我们要去火星”，真正能让别人买账、并且真的把路径搭出来的人，极少。他对这种愿景的力量评价很高。

最后那句补充很关键：“我不认同他的所有方法，但我对这一点有极大的尊重。”（克里斯·拉特纳）

## 什么叫工作努力

Fridman 问了一个很少被认真回答的问题：工作努力到底指什么？

拉特纳先给了最常见的答案：很多小时。这是事实，他不否认。

他说真正难的是另一件事：**同时保持短期交付的专注和长期的判断。**

如果你眼里只有眼前这个任务，把它干完，然后只想着下一步，你会错过那个本该跳过去的下一个大坎。反过来，如果你天天只想着三年后的架构，你交付不出任何东西。在这两端之间来回摆，是最消耗人的。

他承认自己在苹果能做到这一点，靠的是搭起了团队和领导结构，让那些人能成长、能承担责任，从而把自己解放出来去“稍微疯一点”，去想下一件事。

<p

还有一部分是经验带来的连接能力。做过足够多的事情之后，你能看到别人看不到的关联，那些关联本身能省掉大量的试错。他把这算作时间给的一笔复利：同样一个问题，二十年前他得从头推导，今天他能在几秒钟内认出这是哪一类坑。

底层还是大量的小时。他说自己对工作生活平衡有一套自己的理论，这理论是他对自己用的，不往团队身上套。他的目标说得很直白：改变世界，让它变好一点，这就是他的动力来源。

这段话里有个容易被忽略的分寸感。他谈的是自己愿意怎么活，没有把它包装成普适标准，更没有暗示别人不够努力。这种区分在技术圈其实挺稀缺。

## 那个龙

LLVM 的标志是一条龙。官方说法是龙代表力量、速度、智慧，也能代表流畅、优雅、模块化。真实原因简单得多：编译器领域有一本很老很经典的教材，叫《龙书》（Compilers: Principles, Techniques, and Tools）。

当年在苹果，大家做演示要放幻灯片，总得有个 LLVM 的图标，但没有。有人问，编译器技术该配个什么标志？拉特纳说，我不知道，龙大概是我们手上能拿到最好的东西了。苹果的设计团队把图标做了出来，社区接受了它，后来越来越多的设计师参与改进，就成了今天的样子。

有个细节挺有意思。拉特纳的妻子也是编译器工程师，她负责运营 LLVM 基金会，会去 Grace Hopper 大会发贴纸，想办法让更多女性对编译器产生兴趣。结果因为《权力的游戏》，大家都喜欢那个龙的贴纸，抢着要。文化偶尔会用这种方式帮上忙，让下一代工程师对编译器多看两眼。

一条原本只是为了填满幻灯片角落的龙，最后变成了一个跨公司、跨国籍、二十年的合作符号。这件事本身挺说明问题：**基础设施的生命力来自被借用，不来自被保护。**

## 把这一小时的信息用起来

这期访谈最有价值的地方不在结论，在那种看问题的方式。拉特纳反复在做同一件事：把复杂系统拆成层，让每一层都能被独立替换，然后耐心等十年看结果。这套方法可以迁移到很多地方。

- **设计任何长期系统时，先问“这一层会被谁替换”。**拉特纳第一版寄存器分配器被别人整个换掉了，他当成好事。如果你的架构不允许这种替换发生，它就只能活到你的想法过时那天。
- **别用“这不可能”结束讨论，改成“这有多难”。**Clang 和 Swift 都起于“我只是晚上写写看”。区别在于他撞墙之后去判断的是难度等级，可行性压根不是他问的问题。
- **遇到僵局时，去找那个修不好的地基。**Swift 能立项靠的是一个不可辩驳的限制条件：指针就是内存安全问题的根，打补丁永远修不好。把争论从“要不要”转成“怎么做”，只需要这一句话。
- **把渐进式披露用到文档、API、产品上。**第一行代码必须能跑起来，然后才是第二层复杂度。任何要求用户一进门就理解全貌的设计，都会把大批人挡在门外。
- **做软硬件或跨层项目时，接受“押注”这件事。**硬件从设计到量产要几年，算法几个月就换一轮。bfloat16 之所以能成，是因为有人敢在不确定的情况下押注“指数比尾数更重要”，并且这个判断在模型层面站得住。
- **评估一个开源项目，先看它有没有被用在设计之外的地方。**索尼拿 LLVM 做电影特效的图形编译，这个信号比任何性能基准都更能说明它的分层是对的。
- **判断一个基础设施值不值得做，看重复成本。**英特尔、AMD、英伟达、Google、苹果愿意在一个代码库上合作，动力来自两个硬约束：自己实现一遍太贵，招不到足够的人。找到别人绕不过去的高成本，就是协作的入口。
- **关于工作强度：不要只优化小时数。**真正稀缺的能力是在短期交付和长期判断之间来回切换。把执行层交给能成长的人，是唯一能让自己腾出手去想下一件事的办法。

最后留一句拉特纳没有明说、但整期访谈都在示范的事：他从研究生到 2019 年，做的所有项目都有一个共同点，起点小得几乎不体面。一个夜间项目，一本教材上的程序，一个只想读一年硕士就回去工作的人。**值得做的事，往往一开始看起来不值得向任何人汇报。**
