What the Fork - リアルタイム C/C++/Rust ビルド可視化ツール
(danielchasehooper.com)- What the Forkは、C/C++/Rust など多様なビルド過程をリアルタイムで可視化するクロスプラットフォームツール
- 既存のビルドシステムにおける並列処理不足や非効率なプロセスなどの構造的問題を簡単に把握できる
- すべてのビルドシステムおよびプログラミング言語で動作し、make、ninja、gradle、zig、cargo など多様なビルドツールをサポート可能
- システムコール監視によって各プロセスの実行時間、コマンド、依存関係をボックス形式で可視化
- ビルド最適化、ボトルネック分析、CI パフォーマンス改善などに非常に有用なツール
紹介と背景
- What the Forkは、ビルドが遅くなる原因を視覚的に診断するために開発されたリアルタイムビルド可視化ツール
- LLVM プロジェクトのようにコード量そのものが多く、コンパイルが遅くなることもあるが、多くのビルドは非効率な設定のせいで不必要に長時間かかっている場合が多い
- 従来はビルドの問題点を直接確認したり、構造的な問題をひと目で把握したりするのが難しかったため、このようなツールが必要だった
- このツールはクロスプラットフォーム向けに設計されており、あらゆるビルドシステムと言語に適用できる
主な機能と使い方
- What the Forkは単なるシステムプロファイラではなく、ビルドに特化した問題を診断するツール
- 例として、make 使用時の
-jフラグ未使用、特定ファイルまたはコンパイル段階への時間偏重、並列実行可能なのに逐次実行されているコマンドの検出などがある - 特に CI 環境における clean build の性能分析と最適化に効果的
- 使い方は
wtfコマンドをビルドコマンドの前に付けて実行するだけ(例:wtf make、wtf cargo build、wtf npm run buildなど) - ビルドが始まると UI が起動し、各プロセスの進行状況をリアルタイムで更新する
UI と可視化の方式
- 各ビルドプロセスはボックス形式でタイムライン上に表示され、色で種類を区別
- プロセスの親子関係はネスト構造で表現
- 下部パネルでは、選択したプロセスの実行時間、作業ディレクトリ、完全なコマンド引数情報を表示
動作原理
- ビルドは複数のプロセス(例:
bash、clang、ld)の組み合わせで成り立つ - 大規模ビルドでは
cargo、make、bazel、gradle、xcodebuildなど多様なビルドツールが使われ、これらは実際には多数のコマンド、依存関係、キャッシュ、スケジューリング処理を実行している - ターミナル出力だけでは、ネストされたプロセス(例: clang が内部で呼び出す ld など)や詳細なタイミング構造を把握できない
- そのため、OS ごとのプロセス開始・終了を検知するシステムコール(macOS: Endpoint Security API、Linux:
ptrace()、Windows: Event Tracing for Windows など)を活用する - この方式により、ビルド全体の過程とタイムラインを復元し、各段階の実行経路と所要時間を識別できる
- ビルド以外にも、さまざまなサブプロセスのトラッキングに活用できる
実例と観察結果
- 複数のエンジニア(Delta、Mozilla、Apple 所属)が実際にプロジェクトへ適用した結果、予想外の問題を発見
- 例 1: Cargo を使うオープンソースプロジェクトで、ファイルが逐次的にコンパイルされており、並列性不足を確認(10 コア CPU で 10 倍以上の高速化可能性を確認)
- 例 2: Ninja を使った LLVM ビルドでは、すべての CPU コアが効率よく並列処理を実行し、理想的なビルド効率を達成
- 例 3: CMake ベースのプロジェクトで、cmake/make/clang のネスト実行と Xcode/OS バージョン再確認が 85 回も繰り返される非効率な構造を発見(実際の作業はごく一部のみ)
- 例 4: xcodebuild を使う大規模 Objective-C プロジェクトでは、ビルド後半の並列処理不足と、ビルド開始前に 6 秒間のアイドル状態が存在することを確認(対して ninja は 0.4 秒後にすぐコンパイル開始)
- 例 5: Zig が Orca Project をコンパイルする際、依存関係のビルド順序がランダムに決まり、運によって並列処理効率が変化。一部の依存関係が最後に実行され、並列性が低下する現象を観察
- 例 6: make/go を使う GitHub CLI プロジェクトでは依存関係のダウンロード時間が長い。依存関係を減らせばビルド速度の改善が期待できる
活用効果と限界
- 視覚的なタイムライン分析により、予想外のボトルネック、不必要な依存関係の繰り返し、並列性が不足している領域を確認できる
- 依存関係の問題、不必要な再作業、特定ツールの非効率性といった構造的な改善点を素早く把握し、ビルド性能の最適化に直接活用できる
- プロセスの完全なコマンドを確認することで、より詳細な分析が可能
ベータプログラム
- What the Forkは Windows、Linux、macOS で動作
- フィードバックを希望する個人およびチームは、プライベートベータに申し込み可能(Google フォームへのリンクあり)
3件のコメント
コマンド名がめちゃくちゃ笑えますね(笑)
wtfだなんて…。オープンソースとして公開されるといいですね
Hacker Newsのコメント
現在は CMake、GCC、Unix Make しか使えない環境に閉じ込められていて、なぜビルドが遅いのかという詳細な情報を得るのがほとんど不可能です。ソースからビルドフォルダへのファイルコピーのような複雑なビルド手順、複数言語(C、C++、Fortran、Python)、カスタム CMake ステップなどが入り乱れたひどいビルドです。もしこのツールがこうした混沌とした環境でもうまく動くなら、本当に多くのことを学べそうです
tsoding が https://github.com/tsoding/nob.h でクロスプラットフォームビルド向けのシングルヘッダー C ライブラリを書いていました。必要なのは
ccだけで、GDB プロファイリングツールを使ってビルド手順を確認できます。素晴らしいアイデアだと思います。たぶんこの投稿者には合わないかもしれませんが、複数言語を扱うなら Nix は優れたビルドツールです私は GCC プラグインとしてコンパイル時間のトレーシング/プロファイリングツールを自作しました。興味があればどうぞ: https://github.com/royjacobson/externis
自分のゲームエンジンのコンパイル時間を減らそうとしていたとき、コンパイル結果のサイズを代替指標として使っていました。経過時間ベースの測定はあまりに不安定だったので、ビルドごと、さらには別マシンでも同じになるバイナリサイズの測定のほうが扱いやすかったです。100% 一致するわけではありませんが、実用上は役立ちました
同じような問題を経験しています。CMake が変更していないファイルまで再コンパイルするのを時々見ます。たとえばインターフェース変更なしに
.cppを少し直しただけで、まったく独立しているはずのオブジェクトまで再コンパイルされることがあります。CMake が実際のファイル以上に強い依存関係を作っているのではないかと時々疑問に思います。だからビルド時間が無駄に長くなっているのではないか、という話ですブログ執筆者への提案ですが、macOS アプリのビルドを記録した gif 画像をページ上部、ヘッダーのすぐ下に見せるとよいと思います。作ったものを先に見せて、その後に説明を置く流れのほうが見やすいです
このプロジェクトは本当に気に入りました。似たような試みを 2018 年に strace と dtruss、https://buildinfer.loopperfect.com/ を使って BUCK ファイル自動生成のような形でやったことがあります。graphviz や perfetto.dev などで可視化しました。正式な製品としてパッケージ化できなかったのは残念ですが、コンサルティングでは原因診断や BUCK/Bazel 移行に大いに役立ちました。最近も、より広い適用を考えながら見直しています。この方法には本質的な技術的課題もあります。システムコールログをディスクに記録すると数十〜数百 GB に膨れます(例: llvm は 50GB、場合によっては 100GB 超)。https や IPC のようなビルド手順もすべて適切に扱う必要があります(以前、ある顧客は Perl で Firebird DB から毎回コードを引っ張ってきてビルドしていました)。さらにランタイム解析という性質上、ビルド設定ごとに解析を繰り返さなければならない点もあります
すごいと思います。こうした可視化がないせいで見落とされる問題は本当に多いと感じます。10 年前に Mozilla のビルドシステムを最適化していたとき、こういうツールがあれば本当に助かったはずです。記事では実際にどんな問題を見つけたのかをもう少し教えてくれると、なお良かったと思います
CMake で管理している C++ プロジェクトでは、ビルドパフォーマンス可視化に ninjatracing と Clang の
-ftime-traceをうまく使えました。さらに ClangBuildAnalyzer を使うと、コンパイラがどこで時間を使っているのかをもっと細かく分析できます本当に素晴らしいと思います。オープンソース化の予定はありますか。似たものを作っているので協力したいです
Windows で Visual C++ コンパイラを使っているなら vcperf もおすすめです。VS2022 に標準で含まれているか、GitHub から直接ビルドして使えます。UBT や CMake で生成されたプロジェクトにも適用したことがあります。ビルド並列化の質を直接判断できるかは覚えていませんが、コンパイラフロントエンドの情報を簡単に見られます。特に、頻繁にインクルードされるヘッダや本質的に重いヘッダファイルを簡単に見つけられます
一般には見落とされがちですが、ビルドシステムに「焼き込まれた」ビルドロジックが、実際にどの変更の影響を受けるのか細かく追跡されていない、というのは重要な観察です。たとえば ninja ではビルドロジックの一部が事前入力されているので高速です。私は Xerces-C++ を ninja(CMake で設定)と build2(設定と変更追跡をビルドの中で扱うツール)でそれぞれフルビルドベンチマークしましたが、ninja は 3.23 秒、build2 は 3.54 秒でした。CMake が生成するファイルの一部を維持したまま繰り返しビルドすると 3.28 秒まで下がります。ちなみに CMake だけの設定ステップは 4.83 秒です。フルスタックの CMake+ninja ビルドは実際には 8 秒ほどかかり、ライブラリとして使う場合に通常体験する時間はそちらです
ビルド中に Instruments を走らせて、どのプロセスがいつ何をしているのか把握する、という似たような試みをしたことがあります。欠点は、ビルドが長引くと Instruments がもたつくことです。プロセスツリーのフィルタリングもできず不便ですが、Twitter の iOS コードのビルド時間を大幅に減らしたときには大いに役立ちました。最近は Instruments の “All Processes” トレースが壊れてしまい、もうこの方法は使えなくなっています
とても良さそうです。今すぐ試せる macOS 版はありますか。Rust や C++/Swift の作業でも使ってみたいです
バグ修正後、次のベータテストユーザー向けに macOS 版を配布する予定です。(記事の下部で)申し込む際にこのコメントに触れてくれれば、必ずベータグループに入れます
まだどの OS 向けにもパブリックリリースはなく、現状はアーリーアクセスの申し込みだけ可能なようです