バイナリ肥大化の呪縛を解く:Rustで「極限」を目指す最適化の作法
こんにちは。日々、Rustのバイナリと格闘し、ミリ秒の実行速度と数KBのメモリ消費を削り出すことに情熱を燃やすエンジニアです。
Rustは標準で非常に効率的なバイナリを生成しますが、何も考えずに `cargo build –release` を叩くと、実は驚くほど「太った」バイナリが生成されます。これはデバッグ情報や、あえて速度を優先したコードの展開(インライン化など)が詰め込まれているからです。
今回は、単なる設定の羅列ではなく、「なぜバイナリが肥大化するのか」「どこを削れば安全にサイズを縮小できるのか」という、アーキテクト視点の深淵を覗いてみましょう。
—
1. 「z」の深淵:最適化レベルの真実
通常、最適化レベルは `0, 1, 2, 3` が知られていますが、実はもう一つ、`z` という選択肢があります。
- opt-level = 3: 速度最優先。コードサイズは膨らむ。
- opt-level = z: サイズ最優先。 速度を少し犠牲にしてでも、バイナリを極限まで小さくします。
`Cargo.toml` に以下の設定を追加してください。
[profile.release]
実行速度よりもバイナリサイズを優先する「z」を選択
opt-level = “z”
リンク時の最適化(LTO)を有効化し、クロスモジュール最適化で不要なコードを削ぎ落とす
lto = true
複数のコード生成ユニットを1つに絞り、最適化の範囲を最大化する
codegen-units = 1
パニック時の動作を「即終了」にして、巻き戻し用のコードを削除する
panic = “abort”
なぜこれで小さくなるのか?
`codegen-units = 1` は、コンパイラが「プロジェクト全体を一つの塊」として最適化することを強制します。これにより、モジュールを跨いだ不要な関数や定数が厳密に特定され、リンク時に捨てられる確率が劇的に上がります。
—
2. 最後の砦:デバッグシンボルの完全削除
ビルド後、バイナリにはスタックトレースのために必要な「名前(シンボル)」が埋め込まれています。これらは配布用のバイナリには不要です。
`Cargo.toml` の `profile.release` セクションに以下を追記します。
デバッグシンボルを完全に破棄する
strip = true
もし、より細かく制御したい場合は、ビルド後にコマンドラインから `strip` ツールを使用することも可能です。
実行ファイルに対してシンボル削除を直接実行するコマンド
strip target/release/your_binary_name
—
3. 実践:HelloWorldのサイズを極限まで削る
では、実際にどれくらい変わるのか見てみましょう。まずはプロジェクトを作成します。
cargo new slim_project
cd slim_project
`src/main.rs` はそのまま(Hello World)で構いません。一度普通にビルドしてサイズを確認します。
cargo build –release
ls -lh target/release/slim_project
通常だと数百KB〜数MBになることもあります
次に、先ほどの `Cargo.toml` 設定を適用して再度ビルドします。
cargo clean
cargo build –release
ls -lh target/release/slim_project
驚くほど小さくなっているはずです。これが「不要なメタデータを削ぎ落とした純粋な機械語」の姿です。
—
4. さらに上を目指す:wasm-opt との対話
もしあなたがWebAssembly(Wasm)ターゲットで開発しているなら、`wasm-opt` というツールは必須です。これは、Rustのコンパイラである `rustc` が生成したコードを、さらに「意味論的に」最適化するツールです。
wasm-opt を使った更なる最適化の例
wasm-opt -Oz -o output.wasm input.wasm
`-Oz` オプションは、Wasmの世界における「究極のサイズ削減」フラグです。これは単なるシンボル削除ではなく、関数呼び出しの構造まで作り変える強力な最適化を行います。
—
アーキテクトからのアドバイス
「最適化」は、「失うもの」と「得るもの」のトレードオフです。
- `opt-level = “z”` にすると、実行速度はわずかに低下します。
- `panic = “abort”` にすると、クラッシュした際の詳細な情報が失われます。
しかし、組み込み環境やCLIツール、配布するバイナリにおいて「サイズ」はユーザー体験(ダウンロード時間やメモリ負荷)に直結します。
まずは上記の `Cargo.toml` 設定を試してみてください。もしコードが少し遅くなったと感じたら、`opt-level = “s”`(サイズ重視だが、パフォーマンスも少し考慮する)という中間地点を選択するのも賢い戦略です。
エンジニアリングとは、ただ動くものを作るだけでなく、「その環境で最も美しい形」を追求することです。あなたのRustバイナリが、羽のように軽く、光のように速くなることを応援しています。