概要
- async Rust のバイナリサイズ増大(bloat)の根本的な課題
- コンパイラ最適化 によるbloat解消の提案と実験結果
- Futureの状態管理 やpanic処理、状態機械の仕組み解説
- 手動最適化・inlining によるバイナリサイズ削減の可能性
- 今後の最適化案 やコミュニティへの協力呼びかけ
async Rustのバイナリ肥大化問題とコンパイラ改善提案
- async Rust は実行環境を選ばず、サーバーからマイコンまで幅広く利用可能な並行処理技術
- 特に 組み込み機器 などメモリ制約が厳しい環境では、asyncによるバイナリサイズ増大が深刻な問題
- 既存のワークアラウンド(回避策)では根本解決に至らず、 コンパイラレベルでの最適化 が望ましい状況
- Project Goal を提出し、開発資金の支援を募集中
async Futureの内部構造と生成コードの課題
- async関数は 状態機械(state machine) としてMIR(Mid-level IR)上で変換・管理
- 例としてbar関数では 2つのawaitポイント ごとに異なる状態が生成され、MIR出力は360行に及ぶ
- 状態は Unresumed, Returned, Panicked, Suspend0, Suspend1 など複数存在
- Returned/Panicked状態 はpoll後の再呼び出しやpanic後の再利用を防止するために設計
- パニック処理 はコストが高く、バイナリ肥大化や最適化阻害の要因
パニック処理の見直しによるバイナリ削減案
- poll後にパニックではなくPendingを返す ことで、2〜5%のバイナリサイズ削減を確認
- デバッグビルドでは従来通りパニック、リリースビルドではサイズ重視の挙動切替を提案
- panic=abort 時はPanicked状態自体を省略できる可能性も検討
シンプルなFutureの最適化例
- 例:
async { 5 }のような即時完了Futureは 状態管理不要- 手動実装では常に
Poll::Ready(5)を返すだけで十分
- 手動実装では常に
- 現状のコンパイラ生成コードは不要な状態管理・分岐が含まれ、最適化余地あり
- この単純最適化でも0.2%のバイナリ削減を確認
LLVM最適化の限界と入力品質の重要性
- LLVMは 最適化レベル3 であれば一部不要コードを除去できるが、複雑なFutureでは困難
- パニック分岐 があるとLLVMが最適化しきれず、呼び出しや状態管理が残存
- MIR段階での最適化 が不可欠
Futureのインライン化とさらなる最適化案
- Rustのasync Futureは 自動でインライン化されない ため、状態機械がネストしてしまいバイナリ肥大
- 例:
barが単にfoo().awaitする場合、bar独自の状態機械を持つのは非効率 - 前処理・後処理を含む場合 も、状態をうまく共有すれば状態数削減が可能
- Futureの 特性(即時Readyなど)をコンパイラが認識できれば、さらなる最適化が可能
状態の統合(state collapsing)による重複削減
- 複数のawaitが本質的に同じ状態を持つ場合、 状態を統合 することで重複コード削減
- 例:
match分岐ごとにawaitする場合、awaitの前に分岐し、1つのawaitにまとめることでMIRが大幅に短縮let response = match ...; send_response(response).await;のような書き換え
今後の展望とコミュニティへの協力要請
- async bloat の根本解決には、コンパイラの抜本的な最適化が不可欠
- 手動最適化やワークアラウンド だけでは限界があるため、ツールチェーン側の対応が重要
- 資金援助やアイデア提供 など、Rustコミュニティの協力を呼びかけ
この内容はasync Rustのバイナリサイズ最適化に関心のある開発者や、組み込み用途でRustを利用するエンジニアにとって有用な知見です。