Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

コンパイラのプロファイリング

このセクションでは、コンパイラをプロファイルし、どこで時間を費やしているかを調べる方法について説明します。

何を計測したいかに応じて、いくつかの異なるアプローチがあります。

  • PR がコンパイラのパフォーマンスを改善するか、低下させるかを確認したい場合は、 ベンチマーク実行を依頼する方法について rustc-perf の章を参照してください。

  • rustc がどこで時間を費やしているかについて中〜高レベルの概要を把握したい場合:

    • -Z self-profile フラグと measureme ツールは、プロファイリングへのクエリベースのアプローチを提供します。 詳細については、それらのドキュメントを参照してください。
  • 関数レベルのパフォーマンスデータ、または単に上記のアプローチよりも詳しい情報が欲しい場合:

    • perf のようなネイティブコードプロファイラの使用を検討してください
    • または、ナノ秒精度でフル機能のグラフィカルインターフェイスを備えた tracy を使用してください。
  • クレートグラフのコンパイル時間を見やすく視覚的に表示したい場合は、 cargo の --timings フラグを使用できます。 例: cargo build --timingsCARGOFLAGS="--timings" ./x build を使って、このフラグをコンパイラ自体に対して使用できます

  • メモリ使用量をプロファイルしたい場合は、使用しているオペレーティングシステムに応じて さまざまなツールを使用できます。

    • Windows の場合は、WPA ガイドを読んでください。

cargo-llvm-lines による rustc のブートストラップ時間の最適化

cargo-llvm-lines を使用すると、ジェネリック関数の すべてのインスタンス化にわたる LLVM IR の行数を数えることができます。 rustc のコンパイル時間の大部分は LLVM で費やされるため、LLVM に渡すコード量を 減らすことで rustc のコンパイルが速くなる、という考え方です。

やや独自の rustc ビルドプロセスと一緒に cargo-llvm-lines を使用するには、必要な LLVM IR を取得するために -C save-temps を使用できます。 このオプションは、コンパイル中に作成された一時的な作業生成物を保持します。 その中には、最適化パイプラインへの入力を表す LLVM IR が含まれており、 私たちの目的に理想的です。 これは LLVM ビットコード形式で *.no-opt.bc 拡張子のファイルに保存されます。

使用例:

cargo install cargo-llvm-lines
# 通常のクレートではここで `cargo llvm-lines` を実行できますが、`x` は普通ではありません :P

# 前回の実行結果が混ざらないように、毎回実行前にクリーンします。
./x clean
env RUSTFLAGS=-Csave-temps ./x build --stage 0 compiler/rustc

# 単一クレート、例: rustc_middle。(シェルの glob サポートに依存します。)
# 未最適化の LLVM ビットコードを、cargo-llvm-lines が受け付ける人間が読める LLVM アセンブリに変換します。
for f in build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/rustc_middle-*.no-opt.bc; do
  ./build/x86_64-unknown-linux-gnu/llvm/bin/llvm-dis "$f"
done
cargo llvm-lines --files ./build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/rustc_middle-*.ll > llvm-lines-middle.txt

# コンパイラのすべてのクレートを指定します。
for f in build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/*.no-opt.bc; do
  ./build/x86_64-unknown-linux-gnu/llvm/bin/llvm-dis "$f"
done
cargo llvm-lines --files ./build/x86_64-unknown-linux-gnu/stage0-rustc/x86_64-unknown-linux-gnu/release/deps/*.ll > llvm-lines.txt

コンパイラでの出力例:

  Lines            Copies          Function name
  -----            ------          -------------
  45207720 (100%)  1583774 (100%)  (TOTAL)
   2102350 (4.7%)   146650 (9.3%)  core::ptr::drop_in_place
    615080 (1.4%)     8392 (0.5%)  std::thread::local::LocalKey<T>::try_with
    594296 (1.3%)     1780 (0.1%)  hashbrown::raw::RawTable<T>::rehash_in_place
    592071 (1.3%)     9691 (0.6%)  core::option::Option<T>::map
    528172 (1.2%)     5741 (0.4%)  core::alloc::layout::Layout::array
    466854 (1.0%)     8863 (0.6%)  core::ptr::swap_nonoverlapping_one
    412736 (0.9%)     1780 (0.1%)  hashbrown::raw::RawTable<T>::resize
    367776 (0.8%)     2554 (0.2%)  alloc::raw_vec::RawVec<T,A>::grow_amortized
    367507 (0.8%)      643 (0.0%)  rustc_query_system::dep_graph::graph::DepGraph<K>::with_task_impl
    355882 (0.8%)     6332 (0.4%)  alloc::alloc::box_free
    354556 (0.8%)    14213 (0.9%)  core::ptr::write
    354361 (0.8%)     3590 (0.2%)  core::iter::traits::iterator::Iterator::fold
    347761 (0.8%)     3873 (0.2%)  rustc_middle::ty::context::tls::set_tlv
    337534 (0.7%)     2377 (0.2%)  alloc::raw_vec::RawVec<T,A>::allocate_in
    331690 (0.7%)     3192 (0.2%)  hashbrown::raw::RawTable<T>::find
    328756 (0.7%)     3978 (0.3%)  rustc_middle::ty::context::tls::with_context_opt
    326903 (0.7%)      642 (0.0%)  rustc_query_system::query::plumbing::try_execute_query

これはインクリメンタルコンパイルや ./x check では動作しないようなので、 rustc を 何度も コンパイルすることになります。 耐えられるようにするため、bootstrap.toml でいくつかの設定を変更することをお勧めします:

# 私のマシンではデバッグビルドの時間は _3 分の 1_ ですが、
# stage0 rustc より先をコンパイルすると耐えがたいほど遅くなります。
rust.optimize = false

# どうせ incremental は使えないので、少し速度を上げるために無効化します。
rust.incremental = false
# 実行はしないので、デバッグチェックをコンパイルしても意味がありません。
rust.debug = false

# 単一の codegen unit を使用すると出力は少なくなりますが、コンパイルは遅くなります。
rust.codegen-units = 0  # num_cpus

llvm-lines の出力はいくつかのオプションの影響を受けます。 rust.optimize = false にすると 2.1GB から 3.5GB に増え、codegen-units = 0 にすると 4.1GB になります。

MIR 最適化の影響は小さいです。 デフォルトの RUSTFLAGS="-Z mir-opt-level=1" と比較すると、レベル 0 では 0.3GB 増え、レベル 2 では 0.2GB 減ります。

2022 年 7 月時点では、

インライン化は LLVM と GCC のコード生成バックエンドで行われており、 Cranelift のものだけが欠けています。