RustコンパイラーとビルドツールCargoに早期メタデータを使う実験的な変更「Headstart」を適用すると、13の実プロジェクトでcargo checkが最大54%、cargo buildが最大42%速くなりました。 依存クレートの関数本体のチェックが終わる前に、下流のクレートが処理を始める仕組みです。CPUコアが空く時間を減らす一方、エラーが後から判明した場合のやり直しや、メモリー使用量の増加を伴います。

依存クレートの完了を待たずに解析を始める

Rustでは、クレートはライブラリーなどのコンパイル単位です。通常、依存先のクレートが関数本体まで含めた型チェックを終えてから、依存元のクレートがコンパイルを始めます。Headstartは、依存先のインターフェースを確認した時点で早期メタデータを出力し、Cargoがそれを受け取って下流の処理を開始できるようにします。

.rmetaはクレートのメタデータを保存するファイルです。 下流のクレートが依存先を利用する際に必要なのは、その公開インターフェースに関する情報であり、関数本体の解析結果ではありません。Headstartはこの違いを利用し、インターフェースを確定した後の関数本体のチェックと、下流クレートのコンパイルを並行させます。

cargo checkでは、下流クレートは早期メタデータを使って処理を完了できます。実行ファイルなどを生成するcargo buildでは、下流クレートは先に解析を進め、コード生成の段階で依存先の完全なメタデータを待ちます。待機中は実行枠をほかの処理に渡す設計です。

16コアで大きく、4コアでは効果が縮小

標準のフロントエンドを使った13プロジェクトの測定では、cargo checkが最大54%、cargo buildが最大42%短縮され、測定対象に遅くなったプロジェクトはありませんでした。並列フロントエンドを有効にした場合も、追加で最大25%の短縮が報告されています。これらの結果は16コアのマシンで測定されました。

効果はマシンのコア数にも左右されます。4コアでは、rust-analyzerのチェックが24%、ビルドが13〜15%速くなり、codex-rsのチェックは14%短縮しました。一方、広い範囲をビルドするケースでは差がありませんでした。Headstartはビルド中に空いているコアを使うため、小規模なマシンでは短縮幅が小さくなると説明されています。

個別例では、16コア環境でcodex-rsをクリーンビルドした時間が37%短縮しました。Headstartを使わない場合、ワークスペース内のクレートが順番にコンパイルされ、処理の重なりが少ない状態でした。早期メタデータを使うと、先行クレートのインターフェースが整った段階で次のクレートが動き始めます。

並列フロントエンドと組み合わせた測定

Rustの-Zthreadsは、型チェックや借用チェック、MIR最適化など、HIRの生成後からコード生成までの処理を並列化するnightly版の機能です。字句解析やHIRへの変換、マクロ展開は逐次処理とされています。 Headstartは同じクレート内の処理を並列化するのではなく、依存クレート間の待ち時間に下流クレートを動かします。 測定では、この並列フロントエンドを有効にした条件でもHeadstartによる追加の短縮がありました。

実験的なパッチとして公開

Headstartは、rustc向けの6つのパッチとCargo向けの3つのパッチからなる実験的な変更です。-Zearly-metadataと-Zheadstartを使い、プロジェクトごとのチェックやビルドで試せる形になっています。 パッチには、エラー時の診断や終了コード、増分ビルドでの挙動、完全なメタデータへの切り替えを検証するテストも含まれます。

下流の処理を先行させた後で関数本体のエラーが見つかれば、ビルドは従来と同じ診断と終了ステータスで失敗します。ただし、エラーの報告が少し遅れたり、エラーがある場合に下流の作業が無駄になったりするほか、同時に動く処理が増えることでメモリー使用量も増えます。 提案されているのはコンパイラーとCargoへの変更であり、公開内容ではパッチを上流プロジェクトへ送る準備が進められています。