Blazing Rust(高速化Rust) は何をしますか?
動作を維持しながら計測されたパフォーマンスを向上させます。代表的な入力に対して再現可能な改善をもたらす、最もシンプルな変更を維持します。
編集前に計測する
- ワークロード、ターゲットメトリック、正しさの制約を特定します。ホットパスとその呼び出し元を読みます。プロジェクトのツールチェーン、最小サポートRustバージョン、機能、デプロイターゲットを尊重します。説明リクエストは計測とプロファイリングを許可しますが、編集は許可しません。
- プロジェクトの最適化プロダクションプロファイル(通常は
release)でベースラインを取得します。入力、正確なコマンド、ツールチェーン、フラグ、CPU、スレッド数を記録します。現実的な入力サイズと分布(小規模で不利なケースを含む)を使用します。 - パフォーマンスを制限している要因を見つけます。CPU時間、アロケーション、I/O、ロック待機を適宜プロファイルします。async停滞の場合は、ランタイム対応トレーシングを使用してタスクスケジューリングとキュー成長を検査します。既存のツールを優先して使用し、プロファイリングセクションには一般的なツールと、リリースビルドでシンボルを維持する方法が記載されています。プロファイリングが利用できない場合は、ターゲットを絞った計測を行い、証拠の限界を明記します。
- 仮説を立て、1つの計測可能な変更を行います。フォーカスした正しさのテストを実行し、同じベンチマークを繰り返します。ユーザーに見える改善を主張する前に、フルワークロードで結果を検証します。
- 裏付けられた改善のみを維持します。失敗した編集のみを取り消し、関係のない作業は保持します。目標が達成されたか、さらなる変更に証拠がない場合は停止します。
ベンチマークを信頼できるものにする
- セットアップ、コンパイル、入力生成、プロセス起動を、それらがターゲットメトリックに含まれない限り、操作から分離します。結果を消費します。マイクロベンチマークでは、ベンチマークライブラリのオプティマイザバリアまたは
std::hint::black_boxを入力と出力に使用します。これらのバリアはベストエフォートであるため、疑わしい結果を検査します。 - 同等の作業と出力を比較します。ツールチェーン、プロファイル、機能、アロケータ、CPU設定、スレッド数を、テスト対象の変数でない限り固定します。定常状態を計測する場合はウォームアップします。必要な場合はコールドスタートを別途計測します。
- 実行を繰り返し、サンプル数、中央値、ばらつきを報告します。ドリフトが問題になる場合は、ベースラインと候補の実行を交互に行います。ノイズ範囲内の差は改善と見なしません。
- メトリックをタスクに合わせます。サービスの場合、明示された負荷におけるp95/p99レイテンシ、スループット、エラー、リソース使用量を計測します。レイテンシの低下が作業の破棄やキュー成長によるものではないことを確認します。
- 既存のベンチマークを再利用します。新しいロジックが必要な場合は、小さな回帰テストケースを追加します。タスクの許可なくツールをインストールしたり、依存関係を追加したり、ビルド設定を変更したりしないでください。決して計測を捏造してはいけません。
証拠に基づいて変更を選択する
不要な作業とアルゴリズムから始めます。残りのボトルネックが低レベルの変更を必要とする場合にのみ検討します。計測されたボトルネックについてさらに詳細が必要な場合は、テクニックガイドの該当セクションを参照します。
| 観測されたコスト | 候補となる変更とチェック項目 |
|---|---|
| 繰り返しのスキャンや計算 | アルゴリズムやコレクションの選択を改善する。重複するパース、ソート、シリアライズ、ルックアップを除去する。キャッシュが有効な場合は、キャッシュの無効化とメモリを考慮する。 |
| アロケーションやコピー | &str とスライスを借用し、所有権のある値を移動し、バッファを再利用し、現実的な容量を予約する。所有権のある値を置き換える場合は clone_from を試す。中間コレクションを避ける。共有ハンドルでは clone が安価な場合があるので、そのコストを計測する。 |
| コレクションのルックアップ | 借用キーとエントリAPIを使用して、繰り返し作業を回避する。アクセスパターンが合う場合は、密なベクトルやソートされたデータを比較する。ハッシャーを変更する場合は、信頼できないキーに対する衝突攻撃耐性を維持する。 |
| ソートや削除 | 選択には select_nth_unstable、等しい要素の順序が重要でない場合は sort_unstable、高コストなキーには sort_by_cached_key を使用する。要素の順序が重要でない場合のみ swap_remove を使用する。 |
| キャッシュミスや大きな値 | 構造体の配列と配列の構造体を比較する。size_of、align_of、キャッシュミスを確認する。インラインストレージはコピーを増加させる可能性がある。まれに使用される大きなenumペイロードはボックス化を検討し、アロケーションと間接参照を考慮する。 |
| 小さなホットな呼び出しやループ | 通常のイテレーションを優先する。スライス、イテレーション、または範囲アサーションを使用して、コンパイラが境界チェックを除去できるようにする。インライン化やアンロールの前に、最適化されたアセンブリを検査する。空の入力、末尾、オーバーフロー、コードサイズをテストする。 |
| テキスト検索 | コンパイル済み正規表現を再利用し、必要なマッチ情報のみを要求する。エンコーディングのセマンティクスが許せば、特殊なバイト検索を比較する。ホットループ内で検索状態を再構築しない。 |
| ベクトル化可能な計算 | 明示的なSIMDの前に自動ベクトル化を確認する。生成されたコードを検査する。必要な場合はターゲット固有の命令をガードし、スカラーフォールバックを保持する。末尾と代表的なCPUをテストする。アラインメント、境界、数値的な振る舞いを維持する。 |
| 独立したCPU作業 | 逐次実行とバウンドされた並列性を比較する。スケジューリングコスト、競合、帯域幅、スレッド数、総CPU消費量を計測する。オーバーヘッドが問題になる場合のみタスクサイズを調整する。リダクションのセマンティクスを維持する。 |
| I/Oやasync停滞 | ラインバッファを再利用し、小さいI/Oをバッチ処理し、フラッシュエラーを処理する。レイテンシ、改行処理、耐久性を維持する。ブロッキングまたは高負荷のCPU作業をasyncワーカーから分離する。並行性とキューを制限する。順序、バックプレッシャー、キャンセルを維持する。 |
| ビルドやコード生成の制限 | 既存の設定と薄いLTOを比較する。太いLTOと codegen-units = 1 を個別にテストしてから組み合わせる。ビルド時間とバイナリサイズを含める。デプロイハードウェアで許可されている場合のみ、CPU固有のフラグを使用する。 |
正しさを維持する
公開API、エラー処理、バリデーション、順序、シリアライゼーション、FFIコントラクトをそのまま維持します。整数オーバーフローと浮動小数点の動作を維持します。enumの表現やバリアント順序はレイアウトと判別子を変更する可能性があり、どちらもより小さく高速なenumを保証しません。
安全なRustと既存の依存関係を優先します。unsafeコードを導入するには、計測された必要性、明示的な安全性不変条件、フォーカスされたテストが必要です。サポートされていて関連する場合はMiriまたはサニタイザーを使用します。ベンチマーク速度のためにバリデーションや健全性を犠牲にしてはならず、テストの合格を健全性の証明として扱ってはいけません。
結果を報告する
ボトルネック、変更、維持/却下の判断を明記します。再現可能なコマンド、ばらつきを含む前後の結果、関連するアロケーションやメモリの数値、正しさのチェック、トレードオフを含めます。マルチコアの壁時計時間のゲインとシングルコアの効率性を区別します。未計測の主張や未チェックのプラットフォームを明記します。