# 克里斯·拉特納：編譯器、LLVM、Swift、TPU 與機器學習加速器

> LLVM 與 Swift 之父回顧編譯器設計歷程，並談 TPU 等機器學習加速器的工程取捨。

- 原標題：Chris Lattner: Compilers, LLVM, Swift, TPU, and ML Accelerators
- 期數：特別節目
- 釋出日期：2019-05-13
- 嘉賓 / 主題：克里斯·拉特納
- 網頁版：https://lex.qiaomu.ai/tw/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 年，做的所有專案都有一個共同點，起點小得幾乎不體面。一個夜間專案，一本教材上的程式，一個只想讀一年碩士就回去工作的人。**值得做的事，往往一開始看起來不值得向任何人彙報。**
