跳轉到

1957-FORTRAN與優化編譯器#

案件摘要#

1957 年 4 月,John Backus 領軍的 IBM 20 人小組交出了 FORTRAN 編譯器——當時最複雜的程式之一。Backus 下了一個賭注:「自動編碼的效率必須接近手工組碼,否則沒人用。」他們用暫存器分配、公共子表達式消除、迴圈不變碼外提等技術贏了這場賭局,徹底證明高階語言可行——程式設計從此脫離組合語言的奴役。

前因 -- 為什麼會有這個案子#

  • IBM 704 上組語的成本:1950 年代,在 IBM 704 上寫一個科學計算程式,需要熟練程式師數週到數月,且錯誤率極高——組合語言的每個指令都要人腦做暫存器調度與位址計算。
  • 機器時間貴、人力貴:704 每小時租金數百美元,但程式師的時間更貴。Backus 的算盤:若高階語言只損失 10-20% 的執行效率,卻省下 80% 的開發時間,這是划算的買賣。
  • 之前的自動編碼都失敗:Speedcoding、Autocode 等系統執行效率太差(常慢 10 倍以上),學界普遍認為「高階語言不可能兼顧效率」。
  • Backus 的賭注:他向 IBM 提議一個「理論上不可能」的專案——設計一門語言與一個編譯器,使編譯出的碼效率接近手工組碼。IBM 同意了。

線索與推理 -- 數學式、程式、理論#

案件的核心推理:編譯器可以比人更聰明#

Backus 團隊的洞見是:人類程式師做優化時,其實是在做「局部模式匹配 + 代數化簡」——這正是機器最擅長的事。若把這些優化規則形式化,機器可以在所有地方套用它們,而人類只在少數地方想到。

優化的數學描述:若原始程式為 \(P\) ,編譯後程式為 \(C(P)\) ,優化的目標是:

\[ \forall x: \; \llbracket P \rrbracket(x) = \llbracket C(P) \rrbracket(x), \qquad \text{cost}(C(P)) \le \text{cost}(P) \]

即語意等價(結果不變)且成本下降(更快或更小)。

優化一:公共子表達式消除(CSE)#

若表達式中出現相同的子表達式,且其間變數未被修改,則只需計算一次:

優化前:                優化後:
X = A*B + C            T = A*B
Y = A*B - D            X = T + C
                       Y = T - D

A*B 只需算一次乘法(704 上一次乘法要數十個週期)。

優化二:迴圈不變碼外提(LICM)#

若迴圈內的計算不依賴迴圈變數,就把搬到迴圈外:

# 迴圈不變碼外提(LICM)的模擬
# 原始程式(FORTRAN 風格):
#   DO I = 1, N
#     A(I) = B(I) * (X**2 + Y**2)   -- (X**2+Y**2) 與 I 無關!
#   END DO

def naive(b, x, y, n):
    """優化前:每圈重算 X**2+Y**2"""
    a = [0.0] * n
    for i in range(n):
        a[i] = b[i] * (x**2 + y**2)      # 不變碼在圈內,重複計算
    return a

def optimized(b, x, y, n):
    """優化後:LICM 把不變碼提到圈外"""
    a = [0.0] * n
    t = x**2 + y**2                       # 只算一次
    for i in range(n):
        a[i] = b[i] * t
    return a

n = 1_000_000
assert naive(list(range(n)), 3.0, 4.0, n) == optimized(list(range(n)), 3.0, 4.0, n)
# 語意等價驗證:結果完全相同,但乘加次數從 3n 降為 2n+2
print("語意等價 ✓,圈內乘法次數從 3n 降為 2n+2")

其成本分析:原始每圈 3 個浮點運算(兩次平方一次乘),優化後每圈只剩 1 個乘法:

\[ \text{cost}_{\text{before}} = 3n, \qquad \text{cost}_{\text{after}} = n + 2 \approx n \]

當 \(n = 10^6\) ,這是近 3 倍的加速——Backus 的賭注贏在這些細節上。

優化三:暫存器分配(Register Allocation)#

704 有 3 個索引暫存器,分配策略直接決定效率。這是 NP-hard 問題(圖著色),Backus 團隊用貪婪法與局部啟發式解決——後來 Chaitin(1981)把完整理論化為圖著色:

\[ \text{衝突圖 } G = (V, E), \quad \chi(G) \le k \Rightarrow \text{可分配到 } k \text{ 個暫存器} \]

20 人小組的工程壯舉#

1954 年先出語言規格(FORTRAN I),1957 年交出編譯器:共 25,000 行組合語言,含 6 個優化 pass——當時最複雜的單一程式。測試時鎖定 17 個大型程式(含 Boeing 的彈道程式),編譯出的碼平均只比手寫組碼慢 20%,部分甚至更快。

結案 -- 後果與影響#

  • 高階語言的勝利:FORTRAN 的效率證明讓所有懷疑者閉嘴——到 1958 年,IBM 704 上一半以上的程式用 FORTRAN 寫。高階語言從此成為主流。
  • 優化編譯器的誕生:CSE、LICM、暫存器分配等技術成為現代編譯器的標準配備——今天 GCC/LLVM 的優化 pass 仍是 1957 年這些思想的直系後裔。
  • FORTRAN 的長壽:FORTRAN II (1958)、IV (1962)、77、90 至今仍在科學計算中服役——史上最長壽的程式語言之一。
  • Backus 的反思:他後來轉向 ALGOL(BNF 的發明者),1977 年獲圖靈獎時提出 FP 函數式程式設計——對「命令式語言的暴政」再發動一次革命。
  • 軟體工業的起飛:高階語言 + 優化編譯器使「軟體可以量產」——這是軟體工業的工業革命。

關鍵人物與文獻#

  • John Backus:FORTRAN 負責人,BNF 發明者,1977 年圖靈獎。
  • 團隊成員含 Irving Ziller、Harlan Herrick、Sheldon Best 等 20 人。
  • 文獻:
  • J. W. Backus et al., "The FORTRAN Automatic Coding System," Proc. Western Joint Computer Conference (1957).
  • J. W. Backus, "The History of FORTRAN I, II, and III," IEEE Annals of the History of Computing 20(4), 1998.
  • G. Chaitin, "Register Allocation & Spilling via Graph Coloring," SIGPLAN (1981).