# 克里斯·拉特納：程式設計與 AI 的未來

> LLVM 與 Swift 的創造者談程式語言的演進、編譯器技術，以及 AI 將如何改變寫程式碼的方式。

- 原標題：#381 – Chris Lattner: Future of Programming and AI
- 期數：第 381 期
- 釋出日期：2023-06-02
- 嘉賓 / 主題：克里斯·拉特納
- 網頁版：https://lex.qiaomu.ai/tw/ep/chris-lattner-3/
- 官方頁面：https://lexfridman.com/chris-lattner-3/
- 精讀字數：約 5001 字（閱讀約 11 分鐘）

> 本文為 lex.qiaomu.ai 基於原始訪談的 AI 改寫中文精讀，非官方翻譯；引用請註明出處並連結網頁版。

Mojo 官方給出的效能數字是：某些場景下比 Python 快 35000 倍。

說這個數字的人是克里斯·拉特納（Chris Lattner）。他造過 LLVM 和 Clang，發明了 Swift，在 Google 參與過 TensorFlow 和 TPU，在特斯拉當過 Autopilot 軟體副總裁。現在他聯合創辦了 Modular，一家做 AI 基礎設施的公司，Mojo 是這家公司的程式語言，一門 Python 的超集。

這是他第三次上 Lex Fridman 的播客。兩年不見，他手裡多了一門新語言。

## 一門語言的副檔名，可以是一個火苗

Mojo 還有個設計，比 35000 倍更能說明這個人的審美：原始檔的字尾名可以是一個 emoji，比如 🔥。

他半開玩笑地說，這大概是這門語言最重要的決定。理由倒不全是玩笑：副檔名本質上是後設資料，既然是後設資料，為什麼要讓它佔掉螢幕上一塊地方，還要和一堆 txt、pdf 擠在一起，長得灰撲撲的。想酷一點，就得在視覺上跳出來。

實際用起來有一點小麻煩。Git 不認這個，它把火苗當成不可列印字元，在命令列裡吐出一串奇怪的十六進位制。**GitHub、VS Code、Windows 資源管理器都沒問題**，因為它們早就為國際化路徑做過準備，emoji 只是再往前邁一步。

拉特納對這事的態度是：Git 可以被修好。他把整個反應看成一條光譜，一端是“哦這個挺酷”，另一端是“你在殺我的孩子”。他判斷世界會走到前面那一端，而且不必賭上全部家當，兩條路可以同時鋪。

這個細節值得多看一眼。一個人願意在副檔名這種毫秒級的體驗上花心思，說明他做語言不是在做規範，是在做產品。

## 兩頭都在動，中間就成了一團亂麻

要理解 Mojo 為什麼存在，得先看 Modular 想解決的問題。

拉特納的觀察是，TensorFlow 和 PyTorch 這些框架，設計於七八年前。那時候大語言模型還不存在。今天人們拿它們做的事情，和它們被造出來時的目標已經差了一大截。這是軟體這一頭的變化。

硬體那一頭的變化更劇烈。新晶片、新加速器、各種 ASIC，幾乎每家像樣的公司都在釋出自己的晶片，TPU、GPU、NPU、IPU，各種 P 打頭的處理器。拉特納自己在 Google 做過 TPU，那是人類部署過最大規模的 AI 計算系統之一。

> 一邊是移動的系統，另一邊也是移動的系統，中間就變成了一團巨大的亂麻。（克里斯·拉特納）

亂麻的代價落在使用者身上。搞軟體的人面對一堆自己用不上的硬體，理論上能程式設計和實際上能把全世界聰明人的創造力釋放出來，是兩件完全不同的事。

Modular 想做的就是中間那層軟體棧，讓研究者把想法送進生產環境的路徑短一點。Mojo 是這層棧裡最關鍵的一塊磚。

## Python 到底好在哪

拉特納在節目裡反問 Lex：你喜歡 Python 什麼？

Lex 的回答很感性：直觀，寫起來像寫英文，讀別人的程式碼也比讀 C++ 快，同樣的意思用更少的字。還有生態，想幹什麼都有現成的包。

拉特納接著往下挖了一層。他認為機器學習確實幫了 Python 大忙，TensorFlow 和 PyTorch 擁抱 Python，直接把 Python 推到了今天的位置。但真正讓它長成這樣的，是它扮演的角色。

> Python 是那個通用聯結器。它能把很多不同的系統拼在一起，讓你組合出更大的東西，而不必先搞懂每個零件內部怎麼轉。（克里斯·拉特納）

這個定位挺準確。Python 更像一塊樂高底板，形狀各異的積木都能插上去，雖然每塊積木本身不是它造的。生態和易用性互相餵養，滾成一個正向迴圈，一旦滾起來就很難被替代。

那問題在哪？

慢。

## 35000 倍這個數字，一層層拆開

這個倍數聽著像營銷話術，其實拆開看，每一層都有具體的技術來源，加起來還真能到這個量級。

**第一層，把直譯器拿掉。**大家平時敲 *python3* 跑的那個叫 CPython，是個直譯器。程式碼要先變成位元組碼，再被讀、被解析、被解釋執行，這中間多了一整層。改用編譯器，光是消掉這一層，根據程式碼不同就是 2 倍到 5 倍、10 倍的提升。

**第二層，把資料從盒子裡拿出來。**CPython 有個設計決定：一切皆物件，為了讓所有物件的記憶體表示統一，每個物件都帶一個頭，後面跟載荷。這意味著你傳遞一個整數，傳的其實是一個指向資料的指標。每次分配要記賬，每次引用計數要加減，現代 CPU 最討厭的就是這種追著指標滿記憶體跑的行為，因為快取會被打穿。把這層間接去掉，讓數字直接躺在暫存器裡，又是一個 10 倍。兩層乘起來，量級已經很可觀。

**第三層，用上向量。**現代 CPU 一條指令能同時處理 4 個、8 個、16 個甚至 32 個資料。Python 不暴露這個能力。Mojo 暴露了。

**第四層，用上多執行緒。**這裡牽扯到 GIL。很多人抱怨 Python 的全域性直譯器鎖讓並行變得半殘，拉特納指出，GIL 和引用計數是死死綁在一起的，你沒法只扔一個留一個。

**第五層，控制記憶體層次。**資料放在哪一級快取、怎麼佈局，直接決定跑多快。這些在 Python 里根本沒法表達。

這幾層疊起來，就是那個嚇人的數字。Mojo 給這些能力的語法還相當省事，比如你想讓一段迴圈並行，寫一句 parallelize 就行。

效能的意義不止是快。拉特納給了兩個理由，第二個更值得琢磨。

對企業和環境來說，效能等於成本和能耗。他開了個玩笑：**你可以說 Python 對環境不好，換到 Mojo 至少立刻好上十倍。**

對產品來說，效能等於體驗。模型延遲降下來，產品團隊就敢把模型做大；模型做大，使用者感受到的東西就更好。這條因果鏈在機器學習行業裡反覆出現，很多時候模型能力的上限不取決於演算法，取決於推理成本。

## 把直譯器搬進編譯器

Mojo 最有意思的設計在這裡。

Python 的表達力很大一部分來自動態特性。你定義一個類，過載它的 *__add__*，加號就能用在這個類上。PyTorch 的自動微分、eager 模式，底下全是這類執行期的魔法。

問題是這些魔法在 GPU 上跑不了，就算能跑，效能也不划算。

Mojo 的做法是：既然 Python 內部本來就有一個直譯器，那就讓這個直譯器在編譯期執行。於是你得到編譯期超程式設計。API 寫法一點不變，那些動態特性被整體抬到編譯期執行，生成的程式碼再去跑在加速器上。

他拿 Lisp 做類比。Lisp 最強大的地方在於，宏和普通程式碼是同一套東西，同一套語法、同一套概念。這讓它簡單、一致、可組合，你寫的庫在執行期和編譯期都能用。

C++ 走的是反方向。模板系統跟執行期程式設計是兩個平行宇宙，語法不同，概念不同，除錯方式也不同。

> 它是意外變得強大的。它並不是被設計成圖靈完備的，那是後來一路上被發現的事情。（克里斯·拉特納）

他說自己寫過足夠多的 C++，有資格抱怨幾句。這話不假，一個維護過 Clang 的人對 C++ 的感情大概最複雜。

他提到 Zig 也在這個方向上探索，把執行期能做的事挪到編譯期。這條路這幾年確實在聚攏共識。

技術上難在哪？Mojo 底下是一套全新的編譯器設計，建在 MLIR 之上，還塞進了快取、直譯器、JIT。也就是說，編譯器內部裝了一個直譯器，這個直譯器現在還得把程式碼拿到目標機器上真跑一遍，看哪個版本最快，再縫回編譯流程裡繼續。

編譯過程本身變成了機器相關的搜尋。這在傳統編譯器裡是很少見的形態。

## 讓人去記 TPU 的 tile size 是 128，本來就不合理

自動調優（auto-tuning）是 Mojo 另一個被低估的特性。

先看現實。硬體專家腦子裡裝著一堆魔數：快取多大、暫存器多少個、向量多長最划算、GPU 有多少個 SM、warp size 是多少、TPU 的 tile 是 128。這些數字確實重要，寫對了程式碼能快好幾倍。

> 我的看法是，大多數普通程式設計師其實不想知道這些東西。（克里斯·拉特納）

而且這些魔數還在變。A100、H100、Volta，演算法是同一套，引數各不相同。再往遠處看，你的程式碼半年前寫在筆記本上，今天要搬到伺服器上跑，明天可能換了張顯示卡。每次都要重調一遍，誰受得了。

自動調優的思路很樸素：既然外面有的是算力，何必讓人去猜。你把演算法結構寫清楚，比如切成塊、並行處理、按 128 或者別的粒度切，然後告訴編譯器“這些方案你挑一個”，編譯器在真實硬體上跑一遍，誰快用誰，結果快取下來，下次直接用。

一步到位，寫完就不用管了。換機器的時候它自己重新試。

這套機制解決的其實是個老問題：可移植性和極致效能長期矛盾。以前的做法是 *#ifdef* 一層套一層，程式碼越來越難看，測試的組合數爆炸。自動調優把這件事從“人手寫分支”變成“機器做實驗”，本質上是用算力換人的注意力。

## 縮排這件事，他願意吵

中間有一段岔得很遠，但挺有意思。

Lex 提到自己從 Emacs 換到 VS Code，順帶說了句考慮過 Vim 但沒選，結果收到一堆 hate mail。拉特納接話說，縮排就是比花括號好，而且他能給出理性論證。

他的論證有三條。Python 有幾百萬人用，佔住了機器學習，這是事實層面的理由。花括號語言最後也要跑格式化工具，跑完的結果是縮排加花括號兩套並存，既然要分組，為什麼留兩套。還有那些“只有一條語句時可以省略花括號”的設計空間，Python 式的縮排根本不需要面對。

Lex 想看資料：因為縮排寫錯導致的 bug，Python 裡多還是 C++ 裡多。兩個人都覺得大機率不多，因為編輯器會替你縮排，你甚至不需要跟它較勁。

拉特納承認這是宗教問題，但他樂於捅一下熊。捅完還是回到正題：

> 我們不想破壞 Python，也不想改它，或者打引號地“修復”它。我們愛 Python 本來的樣子。我們的看法是，Python 只是還沒寫完。（克里斯·拉特納）

這句話是理解 Mojo 定位的鑰匙。它不打算取代 Python，它打算接著寫。

## 型別是可選項，不是教條

Python 有型別，字串、整數、字典，但全都活在執行期。所以你寫函式不用標型別，代價是所有東西必須是同一種表示：指向帶物件頭的物件的指標。方法分發永遠走同一條路徑，不管接收者實際是什麼。

Mojo 允許你漸進式地加型別。這個整數是 32 位還是 64 位，這個浮點是雙精度還是單精度，編譯器拿到這些資訊就能做更好的最佳化，程式碼補全也更準，因為它知道這個物件上有哪些操作。

整套設計的重心落在“漸進”兩個字上。你可以在任何地方加任意多的型別，不想加就不加，Python 那套動態語義照樣完整保留。

拉特納的立場是：型別不是對的東西，也不是錯的東西，它是有用的東西。這個說法比兩邊陣營的吵架都更接近工程現實。

## Mojo 到底是什麼

回到最基本的問題。它是編譯型還是解釋型？

答案是全都幹。它可以是解釋執行的，可以是 JIT 編譯的，也可以是靜態編譯的。三種模式共存，因為不同場景的需求不一樣：試驗階段要快，部署階段要極致。

它是不是隻服務 AI？AI first，是的，AI 的需求在推著它往前走。但目標是通用語言。判斷標準他給的很簡單：只要某個地方出現“上面一層 Python 做 API、下面一層 C 和 C++ 做計算”這種結構，那就是 Mojo 的機會。

這個模式在今天的軟體世界裡到處都是。科學計算、資料處理、金融建模、影象處理，全是這個形狀。用一門語言從頭寫到尾，把兩層合成一層，收益不只是省掉一門語言的學習成本，還有除錯、部署、團隊協作上的簡化。

至於那個火苗字尾，拉特納說得輕描淡寫：不必賭上全部家當，兩條路可以同時鋪。這話聽著像在講副檔名，其實也是在講 Mojo 和 Python 的關係。

這場對話發生的時候，Mojo 還沒開放。但從他描述的技術路徑看，方向是清楚的：**不要讓使用者為了效能離開 Python，而是讓 Python 長出自己的效能。**

至於能不能成，得看接下來幾年有多少人願意把生產程式碼搬過去。

- 去 mojolang.org 申請早期訪問，找一段你熟悉的 Python 數值程式碼，原樣跑一遍，再給熱點函式加上型別標註跑一遍，自己看差距，別信二手數字。
- 翻一遍你手上的專案，找出“Python 調 C++ 擴充套件”的地方。這些地方就是最值得試點的位置，收益最直接，風險最可控。
- 如果暫時用不上 Mojo，也可以先借它的思路：把程式碼裡那些手調的魔數（分塊大小、向量長度、執行緒數）抽出來，寫成可配置的引數，讓程式自己在目標機器上試。哪怕用 Python 寫指令碼做這件事，也比憑經驗拍腦袋強。
- 想理解編譯期超程式設計，別從 C++ 模板入手，先看 Lisp 的宏，理解“程式碼即資料”之後再看 Mojo 的設計會順很多。
- 把 GIL 和引用計數當成同一個問題看。這樣你就能判斷某個併發方案的宣傳是真解決了問題，還是繞過去了。
- 縮排還是花括號，別在這上面花時間吵架了。真的，兩個人都說這是宗教問題，信哪個都不影響你寫出好程式碼。
