📄
概念 📚 software-design-concepts

パフォーマンス最適化

アルゴリズム計算量・データ構造選択・プロファイリング・N+1問題解決など実装レベルのパフォーマンス改善手法

パフォーマンス最適化とは、システムが応答速度・スループット・リソース消費などの非機能要件を満たすために行う設計・実装上の改善活動である。最適化の出発点は、推測ではなく計測であり、まずプロファイリングによって実際のボトルネックを特定することが原則となる。闇雲な早期最適化は可読性を損なうだけで効果が薄い場合が多い。

アルゴリズムの計算量(時間計算量・空間計算量)はBig-O記法で表され、入力サイズが増大したときの挙動を予測する指標として機能する。O(n²)のネストループをハッシュマップを使ってO(n)に改善するなど、適切なデータ構造の選択は劇的な効果をもたらすことがある。Hash・Tree・Heap・Trieなど、それぞれのデータ構造が得意とする操作の特性を理解することがレビューの前提知識となる。

N+1問題は、ORMを利用する際に頻出するパフォーマンス問題であり、1件の親レコードに対して子レコードをN回個別にクエリすることでDBへのアクセス回数が線形増加する。データローダー(Facebook DataLoaderパターン)やEager Loading(JOINやpreload)によるバッチ化が解決策となる。また、メモ化(Memoization)は同一引数での関数呼び出し結果をキャッシュすることで再計算コストを排除する技術であり、純粋関数に適用することで安全に効果を得られる。

コードレビューで着目するポイント

  • ループ内でDBクエリが発行されていないか(N+1問題)
  • コレクションの検索に対してList(O(n))ではなくHashSet/HashMap(O(1))が使われているか
  • ソート処理がO(n log n)アルゴリズムで行われており、不要なソートが繰り返されていないか
  • プロファイリングによって裏付けられたボトルネックに対して最適化が行われているか
  • メモ化やキャッシュが参照透過な関数にのみ適用されているか(副作用がある関数への不正適用がないか)
  • 大量データのフィルタ・集計処理がアプリ側ではなくDB側で行われているか
  • DBクエリにEXPLAINを実行してインデックスが適切に利用されているか確認されているか
  • 画像・静的ファイルなど変更頻度の低いリソースにCDNキャッシュが設定されているか

典型的なアンチパターン

N+1クエリ: ORMでユーザー一覧を取得後、各ユーザーのプロフィールをループ内で個別にクエリする実装。ユーザーが100件あれば101回のSQLが実行される。includes / preload によるEager Loadingやバッチクエリで1〜2回に抑えることが正しい対応。

早期最適化による可読性の破壊: プロファイリングなしに、ビット演算や手動メモリ管理など低レベルな最適化を適用し、コードを難読化する。ほとんどの場合、ボトルネックはそこではなく、可読性を犠牲にする価値がない。測定結果が先、最適化は後。

インデックスなしの検索カラム: WHERE email = ? で頻繁に検索されるカラムにインデックスが張られておらず、テーブルのフルスキャンが発生している。EXPLAIN の結果で Seq Scan が現れている場合は要注意。

参考リソース

  • High Performance Browser Networking (Ilya Grigorik) — ネットワーク層からの包括的なWebパフォーマンス解説書
  • Use The Index, Luke (https://use-the-index-luke.com/) — SQLインデックス設計の無料オンラインガイド
  • Facebook DataLoader (GitHub: graphql/dataloader) — バッチ処理・キャッシュによるN+1問題解決ライブラリ
  • Google Lighthouse — Webアプリのパフォーマンス計測と改善提案を自動化するツール
  • Brendan Gregg: Systems Performance — プロファイリング・トレーシングによるシステム全体のパフォーマンス分析の定番書