🏋️‍♂️

Rust | cargo build --release のバイナリサイズを削減する!

2024/09/17に公開

バイナリサイズを削減したい

cargo build --release では、リリースモードでバイナリをビルドすることができます。

cargo build は開発時の dev 仕様なので、最適化はされていません。
また、デバッグに関する情報も含まれています。

今回は、mocks という CLI ツールをビルドしてバイナリサイズを計測してみます。

https://github.com/mocks-rs/mocks

What is mocks?

mocks について、自分のブログでも紹介しているので、気になる方は見てくれると嬉しいです!

使用している外部クレートは以下です。

  • axum
    • Web フレームワーク
  • clap
    • CLI ツールのインターフェースを構築
  • serde_json
    • JSON オブジェクトの操作
  • tokio
    • 非同期ランタイム

debug と release で比較

まずは debug と release で比較してみます!

cargo build では 9.2 M でした。

% du -h mocks
9.2M    mocks

そして、cargo build --release では 2.4 MB でした。

バイナリサイズにかなりの差があることがわかります🤔🤔

% du -h mocks
2.4M    mocks

リリースプロファイルでビルドをカスタマイズする

Rust では、リリースプロファイルでビルドをカスタマイズすることで、
さらにビルドの最適化をすることができます。

結論から言うと 1.4 MB 以上 もバイナリサイズを削減することができました🥳🎉

半分以下のサイズに落ちています。

単位が M から K に変わっています!! 嬉しい!!

normal
% du -h mocks
2.4M    mocks
customized
% du -h mocks      
948K    mocks

[profile.release] を定義する

今回のカスタマイズの内容を紹介します!

Cargo.toml に以下の [profile.release] を定義しています。

Cargo.toml
[profile.release]
lto = true
opt-level = "s"
codegen-units = 1
panic = "abort"
strip = "symbols"

lto

Link Time Optimization (LOT) を有効化することができます。

デフォルトでは、Cargo はコンパイル・ユニットを個別にコンパイルし、最適化するよう指示します。

LTO は、リンク段階で最適化するようリンカーに指示します。
これにより、例えばデッドコードが削除され、バイナリサイズが削減されることがよくあるようです。

[profile.release]
lto = true

opt-level

サイズの最適化レベルを指定できます。

[profile.release]
opt-level = "s"
opt-level プロファイルのデフォルト
0 最適化なし dev, test
1 基本的な最適化
2 いくつかの最適化
3 全ての最適化 release, bench
"s" バイナリサイズの最適化
"z" バイナリサイズの最適化(ループのベクタライズをオフにする)

codegen-units

Cargo はリリースビルドに 16 個の並列コード生成ユニットを指定します。
これによりコンパイル時間は短縮されますが、一部の最適化は行われません。

コード生成ユニットの数を 1 に限定することで、バイナリサイズの最適化を図ります。

ただ、この設定によりコンパイル時間が長くなる可能性があります。

[profile.release]
codegen-units = 1

デフォルトは codegen-units = 16 です。

panic

パニックが発生した際にバックトレースが生成されますが、これを強制終了させるように指定します。

[profile.release]
panic = "abort"

デフォルトは panic = "unwind" です。

strip

Linux および macOS では、デフォルトでコンパイルされたファイルにシンボル情報が含まれています。
このシンボル情報は、バイナリを適切に実行するためには必要ありません。

以下のように指定することで、シンボル情報を削除することができます。

[profile.release]
strip = "symbols"

文字列でなく、真偽値 true or false で指定することも可能です。

String Boolean
"symbols" true
"debuginfo"
"none" false

まとめ

公開・配布する CLI ツールのバイナリサイズを少しでも小さくしたくて、検証してみました🤝✨

無事に削減することができて、良かったです!!!!

ぜひ手元の Rust プロジェクトでサイズが削減される様子を確認しながら、
リリースプロファイルをカスタマイズしてみてください!

また、ツールの公開については以下の記事で紹介いるので、良ければ見ていってください💁‍♂️💁‍♂️

https://zenn.dev/collabostyle/articles/c59ce7e2fad2a3

参考

コラボスタイル Developers

Discussion