開発サイクルの加速:Zigの新しいELFリンカーとビルドシステムの刷新

システムプログラミング言語の開発体験は、多くの場合、パフォーマンスとイテレーション速度の間の緊張関係によって定義されます。長年、「コンパイル・リンク・実行」のサイクルは、CやC++の開発における大きなボトルネックとなってきました。Zigは、単なるコンパイラ最適化だけでなく、ビルドグラフの実行方法から最終的なバイナリのリンク方法に至るまで、ツールチェーン全体を再考することで、このボトルネックを積極的に攻撃しています。

Zigのmainブランチへの最近のアップデートは、主に新しいELFリンカーとzig buildプロセスの根本的な作り直しを通じて、システムプログラミングに「スクリプト言語の速度」をもたらそうとする協調的な取り組みを明らかにしています。

新しいELFリンカー:ミリ秒単位のイテレーション

最近のデブログにおける最も重要なマイルストーンの一つは、新しいELFリンカー(-fnew-linkerで有効化)の進捗です。当初はZig専用のコードに限定されていましたが、リンカーは外部ライブラリ、Cソース、さらにはLLVMとLLDを使用してビルドされた自己ホスト型のZigコンパイラ自体もサポートするように進化しました。

この実装の目玉となる機能は、高速な増分コンパイルです。x86_64 Linuxにおいて、開発者は現在、数十ミリ秒単位で増分リビルドを実行できます。例えば、プロジェクトへの小さな変更は、約30msでリビルドが可能であり、この速度は開発ワークフローを、一連の停止時間から連続的な流れへと変貌させます。

現在の制限とロードマップ

速度の向上にもかかわらず、新しいリンカーはまだデフォルトのツールチェーンの完全な置き換えには至っていません。最も顕著な欠けている機能は、Zigコードに対するDWARFデバッグ情報の生成です。これが実装されるまでは、リンカーはディープなセッションデバッグではなく、迅速なイテレーションや「プリントデバッグ」に主に役立ちます。

ビルドシステムの再設計

より高速なリンカーを補完するために、Andrew Kelleyはビルドシステムに大きなアーキテクチャの変更を導入しました。以前は、build.zigファイルとビルドシステムの構成要素は、Debugモードにおいて単一の肥大化したプロセスとしてコンパイルされていました。新しいアーキテクチャでは、これを2つの異なる役割に分離しています:

  1. 設定者 (The Configurer): build.zigのロジックは、メモリ内でビルドグラフを構築し、それをバイナリ構成ファイルにシリアル化する小さなプロセスとしてコンパイルされます。
  2. 作成者 (The Maker): Releaseモードでコンパイルされた別のプロセスが、シリアル化されたビルドグラフを実行します。

この分離により、主に3つのパフォーマンス上の利点が得られます:

  • コンパイルの削減: 変更があった際、ビルドシステム全体ではなく、ユーザーのbuild.zigロジックのみがコンパイルされます。
  • キャッシュされた構成: ビルド引数が変更されていない場合、Zigはbuild.zigのロジックの実行を完全にスキップし、キャッシュされたバイナリ構成を使用できます。
  • 最適化された実行: ビルドグラフを実際に実行するプロセスが最適化されているため、単純なコマンド(例:zig build -hが150msから14.3msに短縮)において、実測時間が劇的に減少します。

深掘り:コンパイラと標準ライブラリの洗練

ビルドシステムとリンカーに加えて、Zigは安定性とパフォーマンスの両方を向上させるために内部ロジックを洗練させています。

型解決と遅延解析

型解決ロジックの再設計により、コンパイラはより「遅延(lazy)」になりました。Zigは現在、もしその型が一度も初期化されないのであれば、その型のフィールドを解析することを避けます。これは、特に名前空間として使用される型にとって有益であり、コンパイラが、実際にはアクセスされないフィールド内で@compileErrorをトリガーすることを防ぎます。

Windows Native APIの優先

肥大化と予測不能な動作を減らすために、Zigの標準ライブラリは、Win32ラッパーよりもNative APIを優先する方向にシフトしています。kernel32.dllをバイパスしてntdll.dllを使用することで、Zigは不要なヒープ割り当てや隠れた失敗モードを回避します。

例えば、エントロピー生成において、Zigはadvapi32.dllbcryptprimitives.dllとやり取りする代わりに、\Device\CNGと直接やり取りすることで、advapi32.dllのオーバーヘッドを回避し、CI環境で見られる非決定論的なレイテンシや潜在的なロード失敗を排除できます。

「zig libc」プロジェクト

Zigは、ベンダー提供のCソースファイルを、Zig標準ライブラリのラッパーに少しずつ置き換えています。これにより、プロジェクトのC言語への依存を減らし、サードパーティプロジェクトへの依存を減らしつつ、コンパイル速度とバイナリサイズを改善します。これらの関数がZig Compilation Unit (ZCU)を共有しているため、コンパイラはlibcの境界を越えて最適化を実行でき、従来のリンカーによる後期のオーバーヘッドなしに、実質的にLTOのような利点を提供します。

コミュニティの視点と展望

コミュニティの反応は、Zigがハイパフォーマンス・ドメインにおいてCの実行可能な代替手段であるという認識が高まっていることを示しています。あるコントリビューターが指摘したように、ネイティブリンカーと増分コンパイルの組み合わせにより、開発者は「JSやPythonの速度でイテレーションし、CやRustのパフォーマンスを得る」ことが可能になるかもしれません。

一部のユーザーは、企業の導入を促進するために安定した1.0リリースを求めていますが、現在の軌道は、Zigが「配管(plumbing)」、つまりリンカー、ビルドシステム、およびlibcの実装に焦点を当てていることを示唆しています。これは、1.0が到来したとき、その基盤が妥協のない速さを備えていることを確実にするためです。

Sources