世界を動かす技術を、日本語で。

リアルタイムのC/C++/Rustビルドビジュアライザーを作成しました

概要

  • ビルド時間が長い原因の多くは単純で修正可能な問題
  • ビルド工程の可視化ツール「What the Fork」の紹介
  • 並列処理の最適化やビルドシステムごとの問題点の発見
  • CIビルドや依存関係の解析にも有効
  • Windows、Linux、macOSで動作し、早期アクセスが可能

ビルド時間が長くなる理由と現状

  • 多くのソフトウェアプロジェクトで ビルド時間の長さ が問題
  • 大規模なプロジェクト(例: LLVM)ではコード量が主因
  • しかし、 非効率なビルド設定 が原因の場合も多い
  • 例: make -j フラグ未使用や、並列化されていないビルド処理
  • ビルドのどこが遅いのか 可視化する手段 がこれまで不足

What the Forkの概要と使い方

  • クロスプラットフォーム対応 のビルド可視化ツール
  • どのビルドシステム・言語でも利用可能
  • ビルドコマンドの前に wtf を付けて実行
    • 例: wtf make, wtf cargo build, wtf gradle build, wtf npm run build
  • Xcodeのビルドにも対応wtf -x

仕組みと技術的背景

  • ビルドは本質的に 一連のコマンド実行
    • シェルスクリプトや make, cargo, bazel, gradle, xcodebuild など
  • ビルドツールは 依存関係解析やキャッシュ も担当
  • 標準出力では 全てのサブプロセスや詳細なタイミング は分からない
  • fork, exec, exit などのシステムコールを監視し、 全コマンドのタイムライン を再構成
    • macOS: Endpoint Security API
    • Linux: ptrace()
    • Windows: Event Tracing for Windows
  • あらゆるサブプロセス起動型プログラムに応用可能

可視化で発見できる問題例

  • Cargo ビルドで並列化されておらず、 1ファイルずつコンパイル されていた事例
  • Ninja ビルドでは全コアをフル活用し、 理想的な並列性 を実現
  • CMake ビルドで、 無駄な環境チェックや再帰的な呼び出し が多数発生
    • 例: xcode-selectsw_vers の85回再実行
  • Xcodebuild で終盤に プロセス数が減少し、非効率なビルド となる事例
  • Zig ビルドで依存関係のビルド順序がランダムになり、 並列性が運任せ
  • Go プロジェクトでは依存パッケージのダウンロードが ビルドのボトルネック

ビルド最適化のヒント

  • 並列処理の最大化 によるビルド高速化
  • 依存関係の見直し無駄な処理の削減 が重要
  • CI環境でのクリーンビルド 最適化にも有効
  • 可視化による問題箇所の特定 が効率的な改善に直結

早期アクセスと今後の展望

  • What the ForkWindows、Linux、macOS で動作
  • 早期アクセスグループで フィードバック募集中
  • ビルド最適化以外の 新たな用途提案も歓迎

What the Fork は、ビルド工程のボトルネックを直感的に可視化し、エンジニアが効率的な改善を行うための強力なツールです。ビルド時間短縮やCI最適化を目指す現場で、ぜひ活用を検討してください。

Hackerたちの意見

それ、めっちゃクールだね!こういうビジュアライゼーションがないせいで見逃される問題がたくさんあるって考えると興味深い。10年前にMozillaのビルドシステムを改善するためにたくさん作業したけど、こんなツールがあったらよかったな。どんな問題が見つかったのか、教えてほしいな。

(ここでOP)ありがとう!Mozillaのエンジニアとの電話が短くなっちゃって、彼が見つけたことについて詳しく話す時間がなかったんだ。自分でも調べてみたいと思ってる。

これめっちゃ興味ある!CMake、GCC、Unix Makeの環境に閉じ込められてて(clangもninjaもなし)、ビルドが遅い理由を詳しく知るのがほぼ不可能なんだ。ソースからビルドフォルダにファイルをコピーするみたいな面倒なステップもあるし、言語も複数(C、C++、Fortran、Python)使ってるし、カスタムCMakeステップもある。もしこのツールがそんなごちゃごちゃしたのを扱えるなら、どんなことが学べるかすごく楽しみ!

コンパイル時のトレース/プロファイリング用のちょっとしたGCCプラグインを書いたんだけど、興味あったら見てみてね: https://github.com/royjacobson/externis

ゲームエンジンのコンパイル時間を改善しようとしたとき、最終的にはコンパイルサイズを代理指標として使ったんだ。完全な相関関係ではないけど、コンパイルサイズはビルドの実行間で決定論的で、異なるマシンでのビルド間でも同じだから、壁時計時間より扱いやすかったよ。

straceがあれば役立つかもね。

CC=time gccって設定できる?

「ほぼ不可能」ってわけじゃなくて、実際には組み込まれてるよ: https://cmake.org/cmake/help/latest/manual/cmake.1.html#cmdo... 実際のコンパイル時間には、簡単にラッパースクリプトを挿入できる。正直、4年以上やってないけど、みんなやってるし簡単だよ。CMake自体がボトルネックになることもあるけど、ほとんどの場合は依存関係とかの問題だと思う。CMakeにはコンパイルやリンク時間を短縮するための機能もたくさんあるし、どうやって速くするかを説明するにはブログ記事がいくつか必要だね。

みんなが提案を出してるのが面白いよね。だって、ここにいるのはまさに必要なことをやってくれるツールを発表する投稿があったからなのに。

これ素晴らしいね!!今、MacOS用のバージョンはあるのかな??試してみたいな… RustやC++、Swiftとかで。ありがとう!

未解決の問題を直したら、macOSバージョンを次のベータユーザーに送る予定だよ。記事の下の方でサインアップして、このコメントを言ってくれれば、その波に入れるようにするよ。

現時点ではどのOSにも公開リリースはないみたいだけど、早期アクセスに応募する方法はあるみたい。

有用な作業を始める前に6秒の非アクティブ時間がある。比較のために言うと、ninjaは2,468,083行のllvmプロジェクトをコンパイルするのに0.4秒かかる。ninjaは他のツールと100%公平な比較ではないけど、ninjaファイルを作成したツールによる「組み込み」のビルドロジックの恩恵を受けているから、ビルドシステムの「光の速さ」のパフォーマンスベンチマークとしては妥当だと思う。これはよく見落とされる重要な観察だよ。それに、この「組み込み」のビルドロジックが基づいている情報の変更は、あまり正確に追跡されていない。こういう「組み込み」なしで「光の速さ」にどれだけ近づけるかな?少しベンチマークを実行したんだけど(いろんな理由で100%正確ではないけど、一般的な指標としては十分)、同じプロジェクト(Xerces-C++)をCMakeで設定されたninjaと、別のステップが不要でビルドの一部として構成管理を行うbuild2でビルドした。ninjaはこのプロジェクトを3.23秒でゼロからビルドするのに対し、build2は3.54秒かかる。CMakeが行ういくつかのステップ(config.hの生成など)をスキップすれば、時間は3.28秒に短縮される。参考までに、CMakeのステップは4.83秒かかる。だから、完全にゼロからのCMake+ninjaビルドは実際には8秒かかる。これは、このプロジェクトを依存関係として使う場合に通常支払う時間だね。

さらに、この「組み込み」のビルドロジックが基づいている情報の変更は、あまり正確に追跡されていない。kbuildは、各ターゲットがCFLAGSが変更されたときに更新されるダミーファイルに依存することで、Makeの上にこれを処理している。また、Makeをninjaのように扱う(例えば、全ビルドグラフをすべてのMakeプロセスに入れないようにする)ので、どんな比較になるか興味あるな。

いいね!2018年にhttps://buildinfer.loopperfect.com/を使ってstrace/dtrussで似たようなことやったことがあるよ。グラフを生成して(例えばgraphvizやperfetto.devを使って)、その結果をもとにBUCKファイルも作ってたんだ。残念ながら、ちゃんとした製品としてパッケージ化することはできなかったけど、コンサルティングの仕事ではすごく役立ったよ。問題を特定したり、BUCK/Bazelへの移行を手助けしたりね。graphvizやhttps://perfetto.dev/、他のツールを使って可視化もしてた。最近もこのアプローチに戻ってきたけど、もっと広い用途を考えてる。いくつかの技術的な課題もあるよね。- syscallログがすごく大きくなることがある - 特にディスクに保存すると。私たちのstraceログは、プロジェクトによっては100GBを超えることもあったよ(llvmは約50GBくらい)。- 一部のプロジェクトはhttpsやプロセス間通信を使っていて、それもちゃんと処理しないといけない。 (実際、ある顧客はコンパイルステップの一部としてperlを使ってfirebirdデータベースからコードを取得してたこともあった!)- これはランタイム分析だから、各構成ごとに分析を繰り返す必要があるかもしれないね。

WindowsでVisual C++コンパイラを使ってるなら、vcperfをチェックする価値があるよ: https://github.com/microsoft/vcperf - VS2022に付属してるし、GitHubからビルドすることもできる。UBTやCMakeで生成したプロジェクトで使ったことがあるけど、ビルドの並列性の質を評価するための情報が提供されるかは覚えてないな。ただ、読みやすいコンパイラのフロントエンド情報はあるよ。特に高コストなヘッダー(本質的にそうであるか、単に多く含まれているかに関わらず)を見つけるのは簡単だよ。

ブログの著者への提案 - 「> ここではmacOSアプリのビルドを記録しています:」をページの一番上、ヘッダーのすぐ下に置いた方がいいよ。作ったものなんだから、ちゃんと見せてあげて。後でいろいろ話せばいいから、まずは見せて。

これってcargo check --timingsと比べてどうなの?各クレートのビルドを可視化して、依存関係を示して、初期コンパイルが完了した時に依存関係を解除するタイミングを示して、もうすぐリンク情報も出る予定なんだよね。

10/10 これめっちゃクールだし、俺がここに来る理由のハッキングだね。

それで、隠しちゃった。これは広告であって、役に立つお知らせじゃないよ。