Google 以 59 分鐘將 FVCOM 的 174,000 行 Fortran 遷移至 Rust,先花 11 分鐘建立基準
Google migrated 174,000 lines of Fortran to Rust in 59 minutes: the 11-minute baseline is the part to copy
Google Antigravity 團隊的 Agent 用 59 分鐘將 FVCOM 的 174,000 行 Fortran 核心遷移至 Rust,並報告執行速度達原版的 1.88x、記憶體用量減半。
文章將這次遷移拆解為建立基準、梳理依賴、移植和優化,具體展示如何先固定舊版輸出,再逐步核對程式改動造成的行為差異。
Google 的 Antigravity 團隊於 11 月 3 日發佈了一段操作示範,展示一個 Agent 如何把 FVCOM 的核心移植到具記憶體安全性的 Rust。FVCOM 是一個歷經數十年以 Fortran 編寫的沿岸海洋模型。整個執行過程用時 59 分鐘,處理了 174,000 行程式碼,找出 6 個自 1990 年代起一直潛藏在求解器中的靜默錯誤,將記憶體用量減半,完成速度亦比已編譯的原始版本快 1.88 倍。
大家都會引用的數字是 59 分鐘。我把文章讀了幾遍,真正令這件事得以實現的部分,其實不是模型,而是執行次序。Agent 用了最初的 11 分鐘建立基準,在基準建立之前完全沒有寫 Rust 程式碼。這個決定令一次看似花巧的重寫,變成可供核查的移植工作;而這正是你今天可以用任何 Agent 複製的做法,包括只能在終端機操作的 Agent。
59 分鐘實際包括哪些內容
文章把整個執行過程分為三個階段,時間計算毫不含糊:11 加 3 再加 45,等於 59。
| 階段 | 時間 | 執行內容 |
|---|---|---|
| 0 | 11 分鐘 | 複製 FVCOM、查閲舊版建置旗標、使用 gfortran -O3 編譯原始版本、記錄 100 個隨機種子的基準輸出、執行時間和記憶體用量 |
| 1 | 3 分鐘 | 掃描全部 264 個檔案,追蹤跨檔案相依關係,以及 2D 和 3D 求解器迴圈中的每個全域變數,並撰寫一份分為六部分的藍圖文件 |
| 2 | 45 分鐘 | 使用 #![forbid(unsafe_code)] 移植、產生 42 項測試、自行修復失敗項目、以 Fortran 執行檔檢查 100 個種子的結果是否一致,然後執行 LLVM 剖析導向最佳化程序 |
Google 亦把整個流程打包成開源技能,放在 google/skills 儲存庫,因此在 Antigravity 中,整套流程會以 /migrate-to-rust <repo-url> 執行。這項技能包含提示詞防護規則、基準探索、錯誤稽核、自我修復測試迴圈、多種子結果一致性測試框架,以及 PGO 程序。這種打包方式本身已説明一切:Google 把它視為可重用的工具,而不是一次性的示範。
讓其餘 48 分鐘值得信賴的 11 分鐘
建立基準的階段最不起眼,卻是整個流程的關鍵支柱。在編寫任何 Rust 程式碼之前,Agent 已使用實驗室實際採用的旗標編譯原始 Fortran 程式,建立效能測試框架,並記錄 100 個隨機種子的基準輸出、執行時間和記憶體峯值。從那一刻起,「移植後是否保留原有行為」便不再是主觀判斷,而是可以透過差異比對結果核實的事實。
這一點很重要,因為數值偏差不會顯而易見。邊界條件中的正負號錯誤、浮點數加總次序改變、迴圈讀取未初始化的儲存格:這些情況都不會令程式崩潰,卻全都會破壞結果。移植完成後,Agent 以同一批 100 個種子執行 A/B 結果一致性測試框架,每次執行的結果都吻合至小數點後 12 位(文章中記錄為 3.91e-13)。沒有基準,就不可能作出這項陳述。最有力的説法只會是「測試全部通過」,但這些測試是由負責移植的同一個 Agent 編寫的。
編譯階段發現、測試卻無法發現的問題
這份錯誤清單值得一讀,正正因為當中的錯誤都很平凡。網格建構器在複雜的海岸網格上超出 50 欄的限制。三角形邊緣檢查以 32 位元精度將三個節點 ID 相乘,在節點數目超過 1,300 的網格上造成溢位。垂直湍流程序在淺水網格上讀取索引 0 的越界元素,並從乾地網格單元讀取未初始化的值。海水密度程序對鹽度值開平方,而數值向下偏差令該值略低於零。還有一個可追溯至數十年前的邊界變通方法,註解內容為 H1(0) = H1(1) ! STRANGE WAY TO PREVENT DIVISION BY ZERO,悄悄令海岸邊界的數學運算取決於哪個三角形恰好被編號為 1。
Rust 編譯器將陣列映射至嚴格型別後,直接拒絕了其中幾個問題。審查階段則標示出其餘問題;單層邊界情況是在測試迴圈中發現的:首次執行時,37 項單元測試有 36 項通過,失敗的測試在水盆只有單一垂直水層時觸發了邊界檢查。Agent 開啟垂直求解器,加上防護條件,並反覆重新執行測試,直至 42 項測試全部通過,分支覆蓋率達 96.8%。
刻意把加速留到最後
最佳化的先後次序是這個教訓的另一半。單是採用慣用且安全的 Rust,執行時間已由 883 ms 降至 682 ms(1.29x),峯值記憶體則由 11.84 MB 降至 6.39 MB。接着,在 2D Runge-Kutta 迴圈中,以預先配置的結構陣列工作區取代每個時間步驟都要進行的 24 次堆積配置,以及反覆配置和釋放的八個暫存陣列,執行時間進一步降至 548 ms(1.61x)。再以即時模擬追蹤資料進行剖析導向最佳化,執行時間降至 469 ms(1.88x),記憶體用量為 5.48 MB,較原本減少 54%。
每次改動都會對照固定的基準測試結果,而不是憑估計行事。先證明正確無誤,再追求速度;這與 Agent 主導的改寫通常採取的做法恰恰相反:先移植,之後才在正式環境發現行為改變。
今天下午就能寫出的基準測試架構
要按照這個次序進行,並不需要 Antigravity。任何能使用終端機的 Agent 都會遵循腳本和規則檔案。兩個小型腳本就能建立同樣的架構。
首先,在作出任何改動之前,固定舊有行為:
# scripts/baseline.sh: record golden outputs before any agent touches src/
set -euo pipefail
mkdir -p baseline/outputs
for seed in $(seq 1 100); do
./build/bin/model --seed "$seed" --days 30 >"baseline/outputs/$seed.out"
done
( cd baseline/outputs && sha256sum *.out >../MANIFEST.sha256 )
git add baseline && git commit -m "baseline: golden outputs for 100 seeds"
然後,要求之後每次建置都符合基準:
# scripts/check-parity.sh: same seeds, new binary, compare hashes
set -euo pipefail
fail=0
for seed in $(seq 1 100); do
new=$( ./target/release/model --seed "$seed" --days 30 | sha256sum | cut -d' ' -f1 )
old=$( grep " $seed.out$" baseline/MANIFEST.sha256 | cut -d' ' -f1 )
[ "$new" = "$old" ] || { echo "seed $seed diverged"; fail=1; }
done
exit $fail
對解析器、建置工具,以及任何輸出必須逐位元保持一致的程式來説,精確雜湊比對很合適。對浮點程式碼而言,這種做法過於嚴格,因此應像一致性測試架構一樣,按容差比較輸出。如果程式沒有種子輸入,就盡可能固定其他條件:凍結時鐘、停用網絡、重播已記錄的請求串流,並對系統輸出的內容計算雜湊。
最後,在 Agent 開始工作前,先透過 AGENTS.md、CLAUDE.md 或測試架構讀取的其他檔案,告知它規則:
## Migration rules
- Do not edit anything under src/ until scripts/baseline.sh has passed once.
- After every batch of edits, run scripts/check-parity.sh and report which seeds diverged.
- If any seed diverges, stop editing and show the diff before touching more files.
- Never loosen a test or a tolerance to make parity pass.
如果你已為系統程式語言維護規則檔案,寫入限制和禁止使用 unsafe 的政策也應放在同一個檔案中;我為 Go 和 Rust 設定的規則便採用這種格式。
明確列出限制
這些都是 Google 的數據,測試目標也是 Google 選定,並刊登在 Google 介紹 Antigravity 和 Gemini 4 Argon 的產品網誌上。目前尚無獨立重現的結果。FVCOM 也是接近最佳情況的例子:它是確定性數值求解器,具備簡潔的命令列介面、穩定的輸入格式,以及明確無歧義的正確輸出標準——相同的數學運算、相同的隨機種子、相同的小數值。大多數商業程式碼都不具備這些條件,而建置黃金測試集才是真正的工作。在為期兩週的基準專案之上,用 45 分鐘完成移植仍然算是成功,但這和標題所宣稱的成果是兩回事。
也要留意這套流程無法提供甚麼:判斷力。42 個自動生成的測試全部通過,只能説明移植版與原版一致,包括原版的錯誤,但不包括它找出的六個錯誤。仍然需要有人判斷原有行為是否正確,而這個判斷無法自動化。如果你的目標語言是 Rust,而且想要比抽樣種子更有力的證據,透過 GitHub 的 PDD Kit 進行形式驗證,就是互補的途徑。
全文的精簡版:先建立基準,再梳理結構,接着移植,最後才提升速度。如果你的 Agent 移植計劃一開始就重寫模組,最後才執行測試,那麼無論是哪個模型寫的,計劃的次序都錯了。
來源:DEV Community · dev.to