コンパイル時間

(原文)

この本は主に Rust プログラムのパフォーマンスを向上させることを対象としていますが、ここでは Rust プログラムのコンパイル時間を削減することを取り扱います。それは、多くの人が関心を持っているトピックであるためです。

リンク

コンパイル時間の大部分は実際にはリンク時間です。特に小さな変更を施した後にコンパイルし直すときは顕著です。プラットフォーム・ユースケース次第でデフォルトのものより高速なリンカを選択できます。

1つの選択肢は lld で、これは Linux 及び Windows 上で利用できます。

コマンドラインから lld を指定するには、ビルドコマンドの先頭に RUSTFLAGS="-C link-arg=-fuse-ld=lld" を付け加えます。

(複数のプロジェクトのために)config.toml ファイルから lld を指定するには、次の行を追記します:

[build]
rustflags = ["-C", "link-arg=-fuse-ld=lld"]

Rust は lld の利用を完全にサポートしているわけではありませんが、Linux や Windows 上での殆どのユースケースで動作するはずです。lld の完全なサポートについてはこの GitHub Issue を参照してください。

もう1つの選択肢は mold で、これは現在 Linux 上でのみ利用できます。使用するには上記の lld の手順を mold に置き換えるだけです。

mold は多くの場合 lld よりも高速です。しかし比較的新しくすべての環境で動作するとは限りません。

インクリメンタルコンパイル

Rust コンパイラはインクリメンタルコンパイルをサポートしています。これはクレートをコンパイルし直すときの重複した作業を避けるものです。代償として、生成される実行可能ファイルの動作が少しだけ遅くなる場合があります。そのため、これはデフォルトではデバッグビルドでのみ有効になっています。リリースビルドでも同様に有効化したい場合には Cargo.toml に次の行を追記してください:

[profile.release]
incremental = true

incremental 設定や異なるプロファイラについて特定の設定を有効化する方法など、詳細については Cargo のドキュメントを参照してください。

視覚化

Cargo はプログラムのコンパイルを視覚化する機能を持っています。timings フラグを渡すことで有効化できます(Rust 1.60 以上の場合):

cargo build --timings

1.59 以下の場合はこちら:

cargo +nightly build -Ztimings

完了すると、HTML ファイルの名前が表示されます。そのファイルの web ブラウザで開いてください。HTML ファイルはプログラムに使われている様々なクレート間での依存関係を示すガントチャートを持っています。これはクレートグラフ中にどのくらいの並行性があるかを示し、コンパイルを直列化している大きなクレート群を分割すべきかを教えてくれます。詳細なグラフの読み方についてはこのドキュメントを参照してください。

LLVM IR

Rust はバックエンドに LLVM を採用しています。LLVM の実行は時としてコンパイル時間の大部分を占めることがあります。特に、Rust コンパイラのフロントエンドが中間表現 (IR) を大量に生成し LLVM がそれを最適化するのに時間を要している場合には顕著です。

これらの問題は cargo llvm-lines により診断できます。このコマンドはどの Rust 関数が最も LLVM IR を生成しているかを表示するものです。巨大なプログラム中で何十回、あるいは何百回とインスタンス化されるため、ジェネリックな関数が重要なものとなることが多いです。

もしジェネリックな関数が中間表現を膨大にしている場合、いくつかの修正方法があります。もっともシンプルなものは関数を小さくすることです。

もう1つの方法は関数のジェネリックでない部分を、一度しかインスタンス化されない個別のジェネリックでない関数に移動することです。これが可能かどうかはジェネリックな関数の実装詳細に依存します。コード中での露出を最小化するため、ジェネリックでない関数はジェネリックな関数のインナー関数として書かれることが多いです。

時々、Option::mapResult::map_err のようなよくあるユーティリティ関数が何度もインスタンス化されることがあります。そのような場合、同等の match 式に置き換えることでコンパイル時間を削減できます。

コンパイル時間における、上記のような変更の効果は通常小さいものですが、場合によっては大きな改善/改悪につながることもあります。