汎用的なアドバイス
(原文)
この本のこれより前のセクションでは Rust 固有のテクニックについて解説してきました。このセクションでは一般的なパフォーマンスの基礎事項の簡潔な概要をいくつか紹介していきます。
Rust 自体のパフォーマンス
リリースビルドを使わないといったような明白な落とし穴を避ければ、Rust は一般的に良いパフォーマンスを発揮します。ときに、Python や Ruby のような動的型付け言語に慣れている場合にはこれが分かりやすいでしょう。
最適化する部分を見極める
最適化されたコードはそうでないコードよりも複雑で書くのに労力を要することも多いです。このような理由からホットなコードのみを最適化していく方が良いでしょう。
最も大きなパフォーマンス改善というのは、低レベルでの最適化よりもアルゴリズムやデータ構造の変更などによってもたらされることが多いものです。
現代的なハードウェア上での最適化
現代的なハードウェアでうまく動作するコードを書くというのは常に簡単というわけではありませんが、やってみる価値はあります。例えば、キャッシュミスや分岐予測の失敗を出来る部分で最小化してみてください。
小さな最適化を積み重ねる
ほとんどの最適化は小さなスピード改善という結果になりがちです。単一の改善では目を引くものでなくとも、それを十分な数積み重ねていけば大きなものになります。
いろんなプロファイラーを使ってみる
プロファイラーにはそれぞれ長所があります。複数使ってみるのが良いでしょう。
ホットな関数の最適化
もしプロファイリングで関数がホットであると分かった場合には、スピードを改善できる2つの一般的なやり方があります。
- (a): 関数を高速化する
- (b): 可能な限り呼び出しを避ける
愚直で遅い実装を潰していく
賢いスピードアップを図るよりも愚直な実装で遅い部分を潰していく方が多くの場合簡単です。
必要なときだけ評価する
必要でない限り何かを計算するのは避けましょう。遅延評価/オンデマンド評価 (on-demand computations) は改善につながることが多いです。
特殊・固有のケースについて処理する
複雑で一般的な実装は、共通する特別なケースに対する、よりシンプルなチェックにより避けられることが多いです。
Complex general cases can often be avoided by optimistically checking for common special cases that are simpler.
特に、小さなサイズが優位を占める場合、0~2個の要素を持つコレクションの処理はパフォーマンス改善につながることが多いです。
同様に、繰り返しのあるデータを処理する場合、共通の値についてコンパクトな表現を用いてまれな値についてはサブテーブルへとフォールバックするような実装をすることで、単純な形式のデータ圧縮を使用できます。
最もよくあるケースを優先する
コードが複数のケースに対処している場合、それぞれのケースの頻度を計測し、最もよくあるものを一番最初に処理しましょう。
局所性の高い探索 (lookup) を処理する場合、データ構造の先頭に小さなキャッシュを用意するとパフォーマンス改善につながることがあります。
コメントを書く
最適化されたコードは明白でない構造をしていることも多く、それを説明する、特にプロファイリングの測定結果を示すようなコメントを書くことが重要になります。例えば、「このベクタは99%の割合で0か1を要素として持つのでそれらのケースを先に処理してください」といったコメントは役に立つでしょう。