4.1 動態翻譯的理論(1970-80年代 Smalltalk 與 Self 的貢獻)#
動態翻譯要解決的根本矛盾#
靜態編譯(如 C)效能最好,但前提是編譯時就知道一切:型別、方法目標、呼叫圖。動態語言(Smalltalk、JavaScript、Python)把這些都推遲到執行期:任何物件都可以在執行中改變結構,任何方法呼叫的目標都可能因接收者不同而不同。傳統的選擇只有兩個:全部靜態編譯(放棄動態性)或全部直譯(放棄效能)。動態翻譯(dynamic translation)/ 即時編譯(JIT)的理論貢獻在於指出第三條路:程式的行為在執行中會收斂到少數可預測的形態,觀察這些形態、據此生成專用機器碼、並在假設破功時退回直譯。這是「以推測換效能、以退路保正確」的樂觀優化哲學,至今仍是所有高效 VM 的指導原則。
Smalltalk:動態系統與早期編譯實驗#
Smalltalk 由 Alan Kay 團隊在 Xerox PARC 創造(Smalltalk-72、Smalltalk-76,到 1980 年的 Smalltalk-80 定型),是第一個全面物件導向(「一切皆物件,一切互動皆訊息傳遞」)的系統。訊息傳遞的語義是查表式分派:receiver class 找到 method dictionary、查 selector、沿繼承鏈上溯——若每個訊息都這樣做,效能慘不忍睹。PARC 的工程回應分成兩條線:
- 位元碼直譯:Smalltalk-80 採用類似 P-code 的位元碼直譯器(由 Adele Goldberg 與 David Robson 的《Smalltalk-80: The Language and its Implementation》1983 年定義),保證可攜。
- 動態翻譯先驅:Peter Deutsch 與 Allan Schiffman 在 1984 年發表的《Efficient Implementation of the Smalltalk-80 System》是 JIT 史上最重要的論文之一,其在 68000 上實作了:動態方法內聯快取(即 4.3 節的 inline cache 雛形)、將熱門位元碼序列動態翻譯為 68000 機器碼、以及字典樹式方法快取。這篇論文證明動態語言的效能可以接近靜態語言,直接啟發了 Sun 的 HotSpot(HotSpot 團隊中多位核心人物來自 Smalltalk 世界)、Animorphic 的 Strongtalk(1990 年代中期,Lars Bak 等人,後被 Sun 收購成為 HotSpot 基礎)、乃至日後 Lars Bak 在 V8 的成就。
同一時期 Berkeley 的 David Ungar 開發了 SOAR(Smalltalk on a RISC,1984)與後來的 Self,提出分代 GC(1984)與「型別回饋」(type feedback)的思想:用執行期觀測到的型別資訊指導編譯優化。
Self:推測優化的理論巔峰#
Self(1987 年起,Stanford 的 David Ungar 與 Randall Smith,後移至 Sun)是為了研究「物件導向語言還能有多動態」而生的語言:它取消了類別(classless、以原型與克隆為基礎),並允許在執行期修改任何物件的方法與結構。這使分派與優化的難度達到極限,也逼出了 VM 技術最輝煌的一批發明(Ungar 與 Chambers 1991 年的論文《Optimization of Object-Oriented Programs Using Static Class Analysis...》以及 Chambers、Hölzle 等人 1990 年代初的一系列論文):
- 型別回饋 / 形態推測(type feedback / speculative optimization):編譯某方法前,先詢問執行時期「這個呼叫點歷史上看過哪些接收者型別?」若 95% 是
Point,編譯器就以Point為假設生成直連碼,並安插守衛(guard):當假設失效時去優化(deoptimize)回直譯器。這把「不確定的動態呼叫」轉化為「推測性的靜態呼叫 + 保險絲」。 - 客製化(customization):為每個接收者型別各編譯一份方法,使欄位存取變成常數位移。
- Splitting 與內聯:以推測的型別資訊大膽內聯,展開後又創造新的優化機會——這個「內聯—特化」的乘數效應正是動態語言優化的核心槓桿。
- 去優化(deoptimization)的理論:Hölzle、Chambers、Ungar 1992 年的《Debugging Optimized Code with Dynamic Deoptimization》解決了「已內聯並特化的機器碼如何退回直譯狀態」的問題——以物化(materialization)把被優化掉的值重建出來,把機器碼的執行狀態精確映射回位元碼狀態。這是所有現代多層 JIT(V8 的 TurboFan、JVM 的分層編譯)都依賴的關鍵機制。
理論基礎:為什麼推測是對的#
Self 團隊的方法論有一個統計學支柱:真實程式的執行具有強烈的形態穩定性——大多數呼叫點終其一生只看到一種型別、大多數分支走向固定。這是「90/10 法則」在型別空間的體現。配合另一支柱:優化的成本是不對稱的——把冷碼誤當熱碼編譯,浪費的是少數時間;把熱碼誤當冷碼直譯,浪費的是執行的大頭。因此「先便宜地跑、觀察、再對少數熱點投入昂貴編譯」是期望成本最小的策略。這套「分層 + 熱點 + 推測 + 退路」的理論框架,在 1990 年代由 HotSpot 完成工程整合,在 2008 年後的 JavaScript 引擎競賽中反覆重驗,成為程序虛擬機執行引擎的標準理論。
與編譯器理論的關聯#
JIT 中的優化(內聯、常數傳播、逃逸分析、死碼刪除)與 AOT 編譯器同源,但多了一個資訊來源:執行期觀測。這使得 JIT 的 IR 常以 SSA 形式承載 profile 元資料(HotSpot 的 C2 與 V8 的 TurboFan 皆然),並引入 AOT 編譯器沒有的問題:編譯時間本身就是執行時間的一部分(因此編譯器必須分層、漸進)、以及去優化的正確性證明。從學科角度看,JIT 是編譯器理論、體系結構(如何生成好的機器碼)與統計決策(何時編譯、依何推測)的交會點。
小結#
1970-80 年代的 Smalltalk 與 Self 提供了動態翻譯的完整理論:動態系統的效能來自「觀察形態 → 推測特化 → 守衛保護 → 失效退回」。下一節將看到這套理論如何在 HotSpot 中被工程化為方法式 JIT 與熱點偵測。