加速開發週期:Zig 的全新 ELF Linker 與建置系統大改版
系統語言的開發體驗通常取決於效能與迭代速度之間的權衡。多年來,「編譯-連結-執行」的週期一直是 C 與 C++ 開發中的重大瓶頸。Zig 正積極地攻擊這個瓶頸,不僅是透過編譯器優化,更是透過重新思考整個工具鏈——從建置圖(build graph)的執行方式到最終二進位檔的連結方式。
Zig main branch 的近期更新顯示,開發團隊正致力於將「腳本語言的速度」帶入系統程式設計,主要透過全新的 ELF linker 以及對 zig build 流程的根本性重構。
全新的 ELF Linker:毫秒級迭代
近期開發日誌中最顯著的里程碑之一是新 ELF linker 的進展(透過 -fnew-linker 啟用)。雖然最初僅限於 Zig-only 代碼,但該 linker 已演進到可以支援外部函式庫、C 原始碼,甚至是使用 LLVM 與 LLD 編譯的 self-hosted Zig 編譯器本身。
此實作的核心特點是快速增量編譯。在 x86_64 Linux 上,開發者現在可以在數十毫秒內完成增量重新建置。例如,對專案進行微小的更改可以在大約 30ms 內完成重新建置,這種速度將開發工作流從一系列的停頓轉變為持續的流動。
目前的限制與路線圖
儘管速度大幅提升,新 linker 並非目前預設工具鏈的完全替代品。最顯著的缺失功能是無法為 Zig 代碼生成 DWARF 除錯資訊。在實作此功能之前,linker 主要用於快速迭代與「print 除錯」,而非使用除錯器進行深度的 session 除錯。
重構建置系統
為了配合更快的 linker,Andrew Kelley 引入了建置系統的重大架構變更。先前,build.zig 檔案與建置系統的實作是在 Debug 模式下被編譯成單個臃腫的程序。新架構將其拆分為兩個不同的角色:
- 配置器 (The Configurer):
build.zig邏輯會被編譯成一個小型程序,用於在記憶體中建構建置圖並將其序列化為二進位配置檔案。 - 製造器 (The Maker):一個使用 Release 模式編譯的獨立程序,負責執行序列化後的建置圖。
這種分離提供了三個主要的效能優勢:
- 減少編譯量:變更時僅需編譯使用者的
build.zig邏輯,而非整個建置系統。 - 快取配置:如果建置參數沒有變動,Zig 可以完全跳過執行
build.zig邏輯,直接使用快取的二進位配置。 - 優化的執行:實際執行建置圖的程序現在經過了優化,導致簡單指令(例如
zig build -h)的實際執行時間大幅減少(從 150ms 降至 14.3ms)。
深入探討:編譯器與標準函式庫的精進
除了建置系統與 linker,Zig 也在精進其內部邏輯以提升穩定性與效能。
型別解析與延遲分析
型別解析邏輯的重新設計使編譯器變得更加「懶惰」。Zig 現在會避免分析一個型別的欄位,如果該型別從未被初始化。這對於被用作命名空間(namespaces)的型別特別有益,能防止編譯器在從未被實際存取的欄位中觸發 @compileError 調用。
Windows 原生 API 偏好
為了減少臃腫與不可預測的行為,Zig 標準函式庫正轉向偏好使用原生 API 而非 Win32 封裝。透過繞過 kernel32.dll 並改用 ntdll.dll,Zig 避免了不必要的堆積(heap)配置與隱藏的失敗模式。
例如,在熵值(entropy)生成方面,Zig 現在可以透過直接與 \Device\CNG 互動來避免 advapi32.dll 與 bcryptprimitives.dll 的開銷,從而消除在 CI 環境中觀察到的非決定性延遲與潛在的載入失敗。
「zig libc」專案
Zig 正在逐步將內建的 C 原始碼檔案替換為 Zig 標準函式庫的封裝。這減少了專案對 C 語言與第三方專案的依賴,同時提升了編譯速度與二進位檔大小。由於這些函式共享 Zig Compilation Unit (ZCU),編譯器可以跨越 libc 邊界進行優化——有效地提供了類似 LTO 的好處,而無需傳統 linker 在後期階段的開銷。
社群觀點與展望
社群的回應凸顯了 Zig 被視為高效率領域中 C 語言可行替代方案的觀念日益增強。正如一位貢獻者所言,原生 linker 與增量編譯的結合,能讓開發者「以 JS 或 Python 的速度進行迭代,同時擁有 C 或 Rust 的效能」。
雖然部分使用者表達了對穩定 1.0 版本發布的渴望,以鼓勵企業採用,但目前的發展軌跡顯示,Zig 正專注於「管路工程(plumbing)」——linker、建置系統以及 libc 的實作——以確保當 1.0 到來時,基礎設施是絕對快速且不妥協的。