- zig build の内部が configurer と maker のプロセスに分かれ、
build.zig が作るビルドグラフはバイナリ設定ファイルとしてシリアライズされる
- 親プロセスは設定ファイルをキャッシュし、maker を Release モードで非同期コンパイルする。maker は Zig のバージョンごとに グローバルキャッシュ へ一度だけコンパイルされる
- ユーザーの
build.zig を変更しても、もはや ビルドシステム全体 を一緒にコンパイルしないため、--watch、--fuzz、--webui のような機能追加による時間的負担を減らせる
zig build --help の平均実行時間は 150ms から 14.3ms に短縮され、CPU サイクル・命令数・キャッシュ参照も 94〜96% 台で減少した
- API の大半は互換性を保つが、
b.args の直接監視は addPassthruArgs() に置き換えられ、Zig 0.17.0 は数週間以内に予定されている
ビルドシステム構造の変更
- 大型ブランチ がマージされ、
zig build の内部が configurer プロセス と maker プロセス に分離された
- 従来の構造では、
build.zig ファイルとビルドシステム実装全体が 1 つの大きな Debug モードのプロセスとしてコンパイルされ、build.zig がメモリ上にビルドグラフを作成した後、「build runner」がそれを実行していた
- 新しい構造では、
build.zig は小さな Debug モードの configurer プロセスとしてコンパイルされ、ビルドグラフは バイナリ設定ファイル としてシリアライズされる
- 親の
zig build プロセスは設定ファイルを検出して次回実行のためにキャッシュし、ビルドグラフ実行を担う maker を Release モードで非同期コンパイルする
- 設定ファイルと maker のコンパイル準備が整うと、maker が設定ファイルを受け取って実行し、maker は グローバルキャッシュ のおかげで
zig version ごとに一度だけコンパイルすればよい
速度改善の効果
- 中核目標は
zig build の高速化であり、ユーザーの build.zig 変更時にも、もはや ビルドシステム全体 を一緒にコンパイルしない
--watch、--fuzz、--webui が導入される中でビルドシステム機能が増えても、zig build の所要時間が一緒に増えないようにする意味は大きい
- 変更がないと判断された場合は、
build.zig ロジックを再実行せず、以前の設定を再利用できる
- たとえば
zig build のコマンドラインに -freference-trace を追加しても、build.zig ロジックを不要に再実行しない
- 実際にビルドグラフを実行するプロセスが 最適化有効 の状態でコンパイルされるため、実行段階も高速になる
ベンチマーク結果
zig build --help の平均実行時間は、従来構造の 150ms から新構造の 14.3ms へ短縮された
- 実時間は
150ms から 14.3ms へ 90.4% 減少 し、CPU サイクルは 593M から 24.1M へ 95.9% 減少 した
- 命令数は
995M から 43.7M へ 95.6% 減少 し、キャッシュ参照は 25.8M から 1.46M へ 94.3% 減少 した
- ピーク RSS は
84.8MB から 78.5MB へ 7.4% 減少 した
- 最大の差は、すべての
zig build コマンドごとに build.zig ロジックを実行していた方式から、キャッシュ済みのシリアライズ設定を再利用する方式へ変わった点で生じている
ツールと互換性への影響
- ZLS のような サードパーティーツール は、build runner をフォークして維持する代わりに、シリアライズ済み設定ファイルを消費する方式で恩恵を受けられる
- 内部メカニズムは大きく変わったが、API の観点では大半が 互換性維持 となっている
- 例外事項は、マージされた PR に整理された変更点に該当する
主な破壊的変更
- 多くのユーザーが遭遇する可能性の高い変更は、
b.args を直接監視していたパターンの削除である
- 既存コード:
if (b.args) |args| {
run_cmd.addArgs(args);
}
run_cmd.addPassthruArgs();
- この変更により、ビルドスクリプトはそれらの引数をもはや監視できなくなり、1 つの機能が削除 された
- その代わり、それらの引数を変えてもビルドスクリプトをソースから再ビルドする必要がなくなった
テストとリリース日程
- Zig の方向性に影響を与えたいユーザーは、プロジェクトを 開発版 に上げて新しい変更を試し、フィードバックを送ることができる
- Zig
0.17.0 は数週間以内にリリース予定である
- 時間がなく事前テストはできておらず、
0.17.0 でビルドが壊れたとしても、0.17.1 タグで修正が入る機会は十分にある
1件のコメント
Hacker Newsのコメント
Zig 0.16.0に一部のコードを移行してみたが、結果にはかなり満足している
影響を受けた部分はかなり多かったが、変更自体は良いもので、言語の将来を明るくする方向に見える
とくに新しい入出力メカニズムは、シングルスレッド、マルチスレッド、イベントループ実装のいずれでも、見通しがよく効率的なコードを可能にしている
0.16.0以降のZigをまだ試していないなら、ぜひ見てみることを勧めたい。今回のリリースノートは非常に長かった
https://ziglang.org/download/0.16.0/release-notes.html
私の知る限り、以前より遅い
今後のリリースで、「ディスパッチ先はコンパイル時に分かっているのに依然として動的」という問題が解決され、効率低下が減ることを期待している
イベントループベースのIO実装で
io.asyncを呼ぶと、内部的に「タスク」のようなものを開始し、futureはそのハンドルなのだろうと推測している分からないのは
future.await(io)を呼ぶときだ。IO実装が現在の関数を何らかの形で中断し、futureが解決したら再開するのだろうか? だとすると、Zigのすべての関数はスタックレスコルーチンだということなのか?.use_llvm = true**を使うことになるZigを数か月使ってみて、すばらしいツール向け言語だと確信するようになった
アイデアを自由にラフに形にしたいときに手に取りやすく、詰まりがちなところには開発者たちがすでに考え抜いた快適な解決策がある
だからといって「正しい」言語の使い方を強制してくるわけでもない
今では自分にとって「ガレージでいじる」ための標準言語になっている
私にとってはかなり大きな生産性の問題だ
私の「ガレージでいじる」標準言語はPythonだ。軽い文法、邪魔しない使い心地、豊富な標準ライブラリ、足りないものはパッケージで大体そろう
Zigの利点は何だろう?
Andrew Kelleyのインタビュー動画を見て、Zigを学んでみたくなった: https://www.youtube.com/watch?v=iqddnwKF8HQ
Andrewの受け答えはよかったのだが、全体の雰囲気があまりにも持ち上げすぎだった
Zigを使ってみたかったが、言語の変化がまだ速すぎる
リリースごとにAPIが破壊されるので、言語を学び、ビルドシステムをデバッグし、実際に作りたいものを実装することを同時に追いかけるのが難しかった
JetBrainsのインタビューを見てまた試したくはなったが、たぶん1.0が出るまで待つと思う
かなり前から、二重プログラミングと呼んでいるアイデアを考えてきた
スタックをちょうど二つの言語、つまり高水準言語一つと低水準言語一つで構成するやり方だ
できる限り高水準言語で多くを書き、必要なときだけ低水準言語に降りる構造である
問題は、低水準言語をすでに十分に使いこなしているのでなければ、低水準の作業をする前にその言語に再び慣れ直す必要がある可能性が高いことだ
そのため、C++やRustはCより扱いにくくなり、私にとってはCが基本の選択肢になる。だがCにはよく知られた問題がある
Zigは、長い空白のあとでも再び手に取りやすいほど単純でありながら、プログラミングを楽にする現代的なツールも備えていて、そのちょうどよい地点をうまく埋められそうだ
できる限り低水準言語で多くを行い、利便性がコストに見合うときにだけ高水準言語へ上がるというやり方だ
Rocはこれを可能にしている。すべてのプログラムは低水準言語で書かれたプラットフォームを持ち、Rocプログラムはそのプラットフォームが公開するAPIを使う
https://www.roc-lang.org/
高水準と低水準のバランスをどう取るかは、もちろん各自で選べばよい
モデルはCythonで実装し、ユーザー向けAPIはPythonで提供している
今はRustですべてやっていて、とくにOCamlのような型システムのおかげで、まだできないことが見つかっていない
Luaは埋め込み言語として意図されているため相互運用しやすく、同時に高水準なので使いやすい
Factorioがスクリプト言語としてLuaを使っているのには理由がある
Zig開発で気に入っているのは、言語機能を追加することよりも、ツールと開発者フィードバックループに驚くほど多くの努力を注いでいる点だ
新しい言語はしばらくの間、機能が一つ欠けていても生き残れる
しかし、コンパイル、リンク、依存関係の更新が毎回遅く感じられるなら、生き残るのはずっと難しい
開発サイクルを秒単位ではなくミリ秒単位にしようとする集中は、長期的に見て良い賭けに思える
「数週間以内に0.17.0をリリース予定」とは驚きだ
0.16は1年以上かからなかったか?
こんなに早い0.17リリースは予想していなかったので、今日知れてとてもうれしい
良いニュースのように聞こえる。Zig のコンパイル時間はすでに優秀だが、この変更でもっと良くなりそう
明らかに重要な目標であり、達成方法のマイルストーンも明確だが、実際には空のプロジェクトの初回コンパイルや、
direnv allowの後に ZLS が再ビルドされるときのつらい待ち時間を「優秀」と表現するのは難しいzig test file.zig -OReleaseSafeを実行するだけでも、私のマシンでは数秒かかる。ファイルを修正するたびに同じ時間がまたかかる。0.16 または master を使っているのでツールチェーンが古いわけでもなく、Linux 環境でもある。
Zig 言語自体は本当に書きやすいが、コンパイラと標準ライブラリは十分に保守的に開発されていないと思う。
hello world から始めるとこういう問題に遭遇しないかもしれないが、ファズテストやベンチマークをするなら最適化済みバイナリを実行したくなる。
そうなると、比較的小さなコード量をコンパイルするだけでもかなりいらいらする。
比較として、Zig は言語としては Rust/C++/C よりはるかに優れていると思うが、Rust/C++/C ではこの種の問題は実際ほとんど起きない。C/C++ では clang/gcc/ninja などを使う前提。
同じマシンで Ninja/Python/clang により、約 1 万行の C++ プロジェクトの設定、ビルド(-O2 または -O3)、テストまで200msでできる
Zig に Linux ライブラリのスタブをエクスポートする公式メカニズムが備わると本当にうれしい。
Zig のクロスコンパイルと任意の glibc バージョンをターゲットにできる能力はまさに魔法そのものだ。
この魔法を別の C++ ビルドシステムで活用しているが、Zig からそのライブラリスタブを得るには回り道をしなければならない。
公式出力として提供されるとよい
わざわざ Node.js や TypeScript の代わりにこれを使いたくなる理由は何?
パフォーマンスの最後の一滴や、メモリ配置と制御が必要でないなら、Zig を使うことのほうがデメリットが多い。
CRUD や「エンタープライズ」系の作業、Web サイトでは Zig の利点はほとんどない
コンパイルされた Zig プログラムは依存関係なしで数 KB に収まることもある。
低水準言語で書いた配列アクセスは SIMD や並列化で最適化でき、同じことを JavaScript で行うより桁違いに速いことがある。
テキスト処理、画像操作、動画処理、ハッシュ化などで差は大きい。
JavaScript を使わない理由は、実質的に無限にある