👨‍🎓

Rustのビルドオプション

2024/12/27に公開

Rustのビルドオプション

組み込み向けの開発ではリソースが限られているため、ビルド時の最適化やデバッグのしやすさが重要です。Rust でのビルドオプションを整理し、Release ビルドでバイナリサイズが小さくなる理由から、手軽に切り替えられる高度なビルド設定を解説します。


1. Releaseビルドでバイナリが小さくなる理由

1.1 最適化フラグとデバッグ情報の削除

  • opt-level: [profile.release]opt-level = 3(デフォルト)が指定されており、デッドコード除去・インライン展開・分岐予測最適化などの高度な最適化が行われます。
  • デバッグ情報の削除 (debug = false): リリース時はデフォルトでデバッグシンボルが含まれず(行番号や変数名の情報なし)、ビルド結果が大幅に小さくなります。

1.2 リンク時最適化(LTO)の活用

lto = "thin""fat" によって、コンパイル後のモジュールをリンク段階でさらに最適化できます。
バイナリサイズがさらに削減され、性能向上も期待できますが、そのぶんリンクに時間がかかる場合があります。

1.3 パニック処理の簡略化

panic = "abort" に設定すると、パニック発生時に即座に停止するため、巻き戻し処理(スタックアンワインド)が省略されます。
バイナリサイズが小さくなるほか、組み込み向けではこの簡潔な挙動が好まれます。

1.4 シンボル情報の削除(stripコマンド)

ビルド完了後に、strip target/release/your_project を実行すると不要なシンボル情報が削除され、バイナリサイズがさらに縮まります。
ただしソースコードとの対応関係が消えてしまい、デバッグが難しくなるので注意が必要です。


2. 主要なビルドオプションと設定項目

Rust のビルドオプションは、主に Cargo.toml[profile.release] セクションで制御できます。ここでは代表的な設定を紹介します。

2.1 opt-level (最適化レベル)

  • 概要: コンパイラ最適化の強度を指定
  • :
    • 0: 最適化なし(デバッグ向け)
    • 1: 高速ビルド&軽い最適化
    • 2: 通常のリリース最適化(デフォルト)
    • 3: 高度な最適化(最も実行速度を重視)
    • "s": サイズ優先
    • "z": サイズを極限まで最小化
[profile.release]
opt-level = 3

2.2 debug (デバッグ情報の有無)

  • 概要: バイナリにデバッグ情報(行番号や変数名など)を含めるか
  • : true / false(デフォルトは false
  • リリース時に true にするとサイズは増えますが、GDB 等でのステップ実行がしやすくなります。
[profile.release]
debug = false

2.3 lto (リンク時最適化)

  • 概要: ビルド後に、複数モジュール(クレート)をまとめてさらに最適化
  • :
    • false: 無効 (デフォルト)
    • "thin": 比較的高速なリンク時最適化
    • "fat": リンク時間は長いが、より強力に最適化
  • バイナリサイズ圧縮や性能向上が期待できます。
[profile.release]
lto = "thin"

2.4 panic (パニック処理の挙動)

  • 概要: パニック時の動作を設定
  • :
    • "unwind": パニック時にスタックを巻き戻す
    • "abort": 即座に停止(バイナリサイズ削減に有効)
[profile.release]
panic = "abort"

2.5 codegen-units (コード生成ユニット数)

  • 概要: 並列コンパイルの単位数
  • : 1 以上の整数(デフォルトは 16
  • 数字を小さくすると最適化の見通しが良くなりますが、ビルド時間は延びます。
[profile.release]
codegen-units = 1

2.6 strip (シンボル情報の削除)

  • 概要: Rust自体の設定ではなく、ビルド後に実行ファイルからシンボル情報を除去する
  • :
    strip target/release/your_project
    
  • 軽量化に有効ですがデバッグ時の情報が失われます。

2.7 incremental (インクリメンタルビルド)

  • 概要: 一部変更があった場合のみ再ビルドする機能
  • : true / false(デフォルトはリリース時に false
  • 開発時のビルド高速化に有用ですが、最適化効率が下がる場合があります。

3. ARM Cortex-M 特有の最適化

組み込み向けに thumbv7em-none-eabihf などのターゲットを使用すると、ARM Cortex-MシリーズのThumb命令セットを活用できます。さらに #![no_std] 環境で不要なランタイムを排除することで、バイナリをより小さくできます。

rustup target add thumbv7em-none-eabihf
cargo build --release --target thumbv7em-none-eabihf

このようにターゲットを指定すると、Cortex-M4F向けに最適化されたバイナリを得られます。


4. ビルド設定の切り替え方法いろいろ

プロジェクトのフェーズによって「サイズを優先」「性能を最大化」「デバッグをしやすい状態」など設定を切り替えたい場面は多々あります。ここでは、主な方法を紹介します。

4.1 Cargo のフィーチャーを利用

features セクションを使い、設定を柔軟に切り替える例です。

[features]
size_optimized = []
performance_optimized = []

[profile.release.package.<package-name>.opt-level]
size_optimized = "z"
performance_optimized = "3"

[profile.release.package.<package-name>.lto]
size_optimized = "thin"
performance_optimized = "fat"

[profile.release.package.<package-name>.panic]
size_optimized = "abort"
performance_optimized = "unwind"
  • 切り替えコマンド例:
    # サイズ優先
    cargo build --release --features size_optimized
    
    # 性能優先
    cargo build --release --features performance_optimized
    

4.2 .cargo/config.toml を使う

.cargo/config.toml に複数のプロファイルを定義し、
cargo build --profile <profile_name> で切り替える方法です。

[profile.size]
opt-level = "z"
lto = "thin"
debug = false
panic = "abort"
codegen-units = 1

[profile.performance]
opt-level = 3
lto = "fat"
panic = "unwind"
codegen-units = 16
incremental = false
  • 切り替えコマンド例:
    # サイズ優先
    cargo build --release --profile size
    
    # 性能優先
    cargo build --release --profile performance
    

5. 運用のまとめと推奨フロー

  1. 開発初期~中期

    • デバッグをしやすくするために、debug = trueopt-level = 0 or 1 などを用いてブレークポイントやステップ実行を活用。
    • RTT 等でリアルタイムログを取りながら、実装の動作を把握する。
  2. 最終的な性能検証

    • release プロファイル(opt-level = 3, lto = "thin"/"fat", など)でコンパイルし、必要に応じて strip でサイズを削減。
    • 組み込み向けに panic = "abort" を設定し、不要なパニックメッセージを省いてバイナリをさらに軽量化。
  3. 本番運用

    • パフォーマンスとサイズを最優先にし、debug = falseopt-level = 3lto 有効化などを行う。
    • 万が一のトラブル時は probe-rs を使ってソースコードレベルのデバッグができる体制を整えておく。
  4. ビルドプロファイルや features を使った柔軟な切り替え

    • .cargo/config.toml やスクリプトを駆使して、シチュエーションに応じたビルド設定をワンタッチで切り替えできるようにするのが理想です。

結論

  • Release ビルドでサイズが小さくなる理由
    Rust が提供する高度な最適化(opt-level)、デバッグ情報削除(debug = false)、リンク時最適化(lto)、パニック処理簡略化(panic = "abort")などが組み合わさっているためです。

  • ビルドオプションの活用
    [profile.release] の設定を調整するだけで、バイナリサイズや実行性能が大きく変化します。組み込み向けなら、さらに no_std やターゲット指定で深い最適化が可能。

  • デバッグ手法
    デバッグの際は RTT や probe-rs を使い、ステップ実行や変数監視などを行えます。開発中は debug = true と適度な最適化、リリース時は debug = false と最大限の最適化で使い分けるのがおすすめです。

  • 運用フロー

    1. 開発中はデバッグ重視
    2. 最終段階でリリース用設定を適用しサイズや性能を追求
    3. 何か問題が起きたら再度デバッグ重視の構成に切り替えて解析

以上が、Release ビルドでバイナリサイズが小さくなる理由から、各ビルドオプションの概要、さらに運用時の切り替え手法までをまとめた内容です。

Discussion