Bun 1.4: 使用 AI 將 Runtime 重寫為 Rust

Bun 1.4: 使用 AI 將 Runtime 重寫為 Rust

Bun 在 v1.4.0 版本中將其核心 runtime 重寫為 Rust,以系統性地消除記憶體安全漏洞並提高穩定性。透過利用 Claude Fable 5 的預發布版本以及自動化的「對抗性審查」工作流,團隊在 11 天內將超過 500,000 行 Zig 程式碼移植到了 Rust,實現了二進位檔案大小減少 20%,以及效能提升 2% 至 5%。

為什麼 Bun 從 Zig 轉向 Rust

Bun 最初是使用 Zig 建構的,以利用其底層控制與高效能。然而,該專案龐大的規模——包括轉譯器、套件管理器、測試執行器以及 Node.js API 實作——帶來了顯著的穩定性挑戰。

系統性記憶體安全問題

重寫的主要驅動力是那一類難以透過風格指南或手動審查來解決的循環性記憶體錯誤。在 Bun v1.3.14 中,團隊發現了許多關鍵問題,包括:

  • Use-after-free 崩潰:在 node:zlibnode:http2 中,由非同步操作或重入式 JS 回調觸發。
  • Double-free 崩潰:發生在 CSS 解析器中。
  • 記憶體洩漏:在 crypto.scrypttlsSocket.setSession()fs.watch() 中,由於引用計數下溢或缺少清理呼叫。

雖然團隊使用了 Address Sanitizer (ASAN) 並透過 Fuzzilli 進行 24/7 的 fuzzing,但 Zig 缺乏自動記憶體管理,這意味著每一次分配都需要細緻的手動審查。選擇 Rust 是因為其借用檢查器 (borrow checker) 和 Drop trait 可以將這些記憶體安全錯誤轉化為編譯器錯誤,提供比手動程式碼審查更快且更可靠的反饋迴圈。

AI 驅動的重寫過程

Bun 團隊並非採用傳統的增量重寫(這通常會引入暫時性的「膠水」程式碼),而是對整個程式碼庫進行了機械式的移植。這是透過 Claude Code 和一系列動態工作流實現的。

「對抗性審查」工作流

為了確保 LLM 生成的程式碼品質,團隊實施了分層上下文審查系統:

  1. 實作者 (The Implementer):一個 Claude 實例,根據原始 Zig 源碼和移植指南編寫 Rust 程式碼。
  2. 對抗性審查者 (The Adversarial Reviewers):兩個獨立的 Claude 實例,僅接收 diff。其唯一目標是找出錯誤並證明程式碼是錯誤的。
  3. 修復者 (The Fixer):最後一個實例,負責應用審查者的反饋。

這個過程捕捉到了那些即使通過標準編譯器檢查也會發生的關鍵錯誤,例如在 js_bun_spawn_bindings.rs 中,由 libuv 的 uv_close 非同步特性所引起的 use-after-free/double-free 錯誤。

執行與規模

  • 時間線:11 天(5 月 3 日至 5 月 14 日)。
  • 數量:6,502 次提交 (commits),峰值輸出為每分鐘 1,300 行程式碼。
  • 基礎設施:64 個並行運行的 Claude 實例,分布在 4 個工作樹 (worktrees) 中。
  • 成本:API token 費用約為 165,000 美元。

Bun v1.4.0 的技術改進

記憶體使用量與穩定性

轉向 Rust 的 Drop trait 消除了數個可檢測的記憶體洩漏。例如,在 Bun.build() 中,v1.3.14 的記憶體使用量會隨著建置次數線性增長(在 2,000 次建置後達到 6.7 GB),而在 v1.4.0 中,記憶體使用量在約 609 MB 處趨於平緩。

二進位檔案大小與效能

  • 二進位檔案大小:在 Linux 和 Windows 上減少了約 20%(例如,Windows 二進位檔案從 94 MB 縮小至 76 MB)。這歸功於減少了 comptime 的使用以及實施了如 Identical Code Folding 等連結器優化。
  • 吞吐量:在各種框架(Bun.serve, Elysia, Fastify)中,HTTP 吞吐量提升了 2.8% 至 4.8%。
  • CLI 速度:應用工作負載如 next buildtsc -b 的效能提升了 2.2% 至 4.7%。
  • 堆疊空間:由於 LLVM 的 llvm.lifetime.startllvm.lifetime.end 指令,遞迴下降解析器(JSON, TOML 等)現在使用更少的堆疊空間。

移植挑戰與退化 (Regressions)

儘管過程是自動化的,但重寫引入了 19 個已知的退化問題,主要源於 Zig 與 Rust 的語義差異:

  • 巨集消除 (Macro Erasure):發生了一個錯誤,即 Rust 中的 debug_assert! 在發布版本 (release builds) 中會消除副作用呼叫 (insert_stale),而 Zig 的 assert 是一個總是會執行的函數。這導致某些 React 專案的 HMR 失效。
  • 切片處理 (Slice Handling):Rust 的 bytemuck::cast_slice 在處理奇數長度的切片時會發生 panic,而原始的 Zig 程式碼則忽略了末尾的位元組。這導致 Blob.text() 在特定的 UTF-16 案例中發生 panic。
  • 邊界檢查 (Bounds Checking):Rust 的發布版本會維持邊界檢查,這揭示了一個先前被 Zig 的 ReleaseFast 模式所隱藏的模組解析器中的 off-by-one 錯誤。

社群與產業觀點

來自 Hacker News 的討論洞察突顯了軟體工程範式的轉變:

「設計並實作完善的測試框架是真正的核心工作,一旦你有了它,就讓 LLM 去大顯身手吧。」

批評者指出,高昂的 token 成本(16.5 萬美元)可能會抵消與人工團隊相比的部分人力節省,而其他人則指出,重寫的成功很大程度上取決於 Bun 現有的、與語言無關的 TypeScript 測試套件,這為 AI 進行有效迭代提供了必要的「可驗證獎勵」。

Sources