performance について
ソフトウェアの遅さやリソース使用を調査する開発者向けのパフォーマンス最適化リファレンスです。変更前のプロファイリングを重視し、キャッシュ、データベースクエリ、メモリ管理、測定手順を扱います。
主な機能
- 01プロファイリングを先行する最適化手順
- 02パフォーマンス予算とベンチマーク
- 03キャッシュ層と無効化
- 04データベースクエリの最適化
- 05メモリとリソースの管理
活用シーン
- 01CPU・メモリ・I/Oのボトルネック特定
- 02データベースクエリの重複削減
- 03キャッシュ無効化戦略の選択
- 04コード変更の効果検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ krzysztofsurdy/ code-virtuoso --skill performanceインストール後、Claude Code で/performanceを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/caching-strategies.mddatabase-optimization.mdprofiling-patterns.md詳細
パフォーマンス最適化
パフォーマンス改善には、何かを変更する前に測定するという原則があります。ボトルネックに関する直感は、当たるより外れることのほうが多いものです。最適化はすべて、プロファイリングから始め、仮説を立て、的を絞った修正を適用し、最後にもう一度測定します。
基本原則
| 原則 | 意味 |
|---|---|
| まず測定する | プロファイリングデータなしで最適化しないでください。ボトルネックに関する勘は頼りになりません |
| クリティカルパスを最適化する | 最も頻繁に実行されるコード、またはユーザーが体感するレイテンシーを引き起こすコードに注力します |
| 予算を設定する | 作業を始める前に、許容できるレイテンシー、スループット、リソース使用量を定義します |
| 早すぎる最適化を避ける | まず、読みやすく正しいコードを書きます。測定によって実際の問題が確認された場合にのみ最適化します |
| トレードオフを把握する | どの最適化にも代償があります(速度のためのメモリ、スループットのための複雑さ、レイテンシーのための鮮度) |
プロファイリングとベンチマーク
プロファイリングによって、時間とリソースがどこで使われているかが分かります。プロファイリングがなければ、推測するしかありません。
プロファイリングの種類
| 種類 | 明らかになること | 使用する状況 |
|---|---|---|
| CPU プロファイリング | ホット関数、呼び出し頻度、実行時間の分布 | リクエスト処理の遅延、CPU 使用率の高さ |
| メモリプロファイリング | 割り当て率、ヒープサイズ、オブジェクトの保持、メモリリーク | メモリ使用量の増加、OOM エラー、GC の負荷 |
| I/O プロファイリング | ディスクの読み書き、ネットワーク呼び出し、ブロッキング待機 | ファイル操作の遅延、外部サービスのレイテンシー |
| データベースプロファイリング | クエリの実行時間、リクエストあたりのクエリ数、低速なクエリ | DB 負荷の高さ、N+1 パターン、インデックスの不足 |
プロファイリングのワークフロー
- ベースライン:変更を加える前に、通常の条件下でメトリクスを取得します
- 特定:時間またはリソースを最も多く使っているホットスポットを見つけます
- 仮説:遅い原因について具体的な仮説を立てます
- 修正:的を絞った変更を一つ適用します
- 検証:再度測定して改善を確認し、リグレッションがないか調べます
パフォーマンス予算
超過した場合に対処を開始する上限を定義します。
- 応答時間:エンドポイントごとの P50、P95、P99 のレイテンシー目標
- スループット:想定負荷時に維持する最低リクエスト数/秒
- リソース使用量:サービスごとの CPU、メモリ、接続数の上限
- ページの容量:フロントエンドアセットの最大転送サイズ
プロファイリングのワークフロー、ボトルネックの兆候、負荷テストの戦略については、プロファイリングパターンのリファレンスを参照してください。
キャッシュ戦略
キャッシュは、結果を必要な場所の近くに保存することで、重複する計算やデータ取得をなくします。
キャッシュ層
| 層 | 場所 | レイテンシー | ユースケース |
|---|---|---|---|
| L1 - プロセス内 | アプリケーションメモリ(オブジェクトキャッシュ、メモ化) | ナノ秒 | 1 回のリクエスト中に何度もアクセスされるホットデータ |
| L2 - 分散型 | Redis、Memcached、共有キャッシュ | 1 ミリ秒未満から数ミリ秒 | アプリケーションインスタンス間で共有されるデータ |
| HTTP キャッシュ | ブラウザー、リバースプロキシ(Varnish、Nginx) | クライアントキャッシュではネットワーク往復なし | 静的アセット、キャッシュ可能な API レスポンス |
| CDN | 世界各地のエッジサーバー | 地理的な近さによる低レイテンシー | 静的ファイル、事前レンダリングされたページ、メディア |
| データベースキャッシュ | クエリ結果キャッシュ、バッファープール | さまざま | 同一クエリの繰り返し |
無効化の方式
| 戦略 | 仕組み | 最適な用途 |
|---|---|---|
| TTL ベース | 一定時間が経過するとキャッシュエントリが期限切れになります | 許容範囲内の古さを許容できるデータ |
| イベントベース | 元データが変更されたときにキャッシュをクリアします | 書き込み後も最新の状態を保つ必要があるデータ |
| ライトスルー | 書き込み時にキャッシュとバックエンドストアの両方を同時に更新します | 強い一貫性が必要な読み取り中心のワークロード |
| ライトビハインド | 書き込み時にキャッシュをすぐ更新し、バックエンドストアは非同期で更新します | 結果整合性を許容できる、書き込みスループットの高いワークロード |
キャッシュスタンピードの防止
人気のあるキャッシュキーの有効期限が切れると、多数の同時リクエストが一斉にデータを再生成しようとし、バックエンドに過度な負荷がかかることがあります。これを防ぐ方法は3つあります。
- ロック:1つのリクエストだけがデータを再生成し、ほかのリクエストは待機するか、古いデータを返します
- 確率的な早期再計算:リクエストがランダムに有効期限前のキャッシュを更新し、再生成を時間的に分散します
- リクエストの集約:実行中の重複リクエストを1つのバックエンド呼び出しにまとめます
複数の言語による実装パターンについては、キャッシュ戦略のリファレンスを参照してください。
データベースの最適化
データベースクエリは、ウェブアプリケーションで最も一般的なパフォーマンスのボトルネックです。
インデックス戦略
- WHERE、JOIN、ORDER BY 句で使用するカラムにインデックスを作成します
- 最も頻繁に使われるクエリパターンに合致する複合インデックスを使用します(左端プレフィックスルール)
- カバリングインデックスにはクエリに必要なすべてのカラムが含まれるため、テーブルを検索する必要がありません
- 未使用のインデックスを監視します。読み取りに役立たない一方で、書き込みを遅くするためです
N+1 クエリの防止
N+1 問題は、コードが N 件のレコード一覧を取得した後、関連データを読み込むためにレコードごとに追加のクエリを実行すると発生します。クエリは1回ではなく、N+1回実行されます。
検出の兆候:
- クエリ数が結果セットのサイズに比例して増加する
- 単一のパラメーターだけが異なる、ほぼ同一のクエリが多数ある
- プロファイラーで、1回のページ読み込みに対して数十件、数百件のクエリが表示される
防止策:
- Eager Loading(JOIN、または事前にまとめて実行する別のクエリ)
- バッチ読み込み(ID を集め、すべての関連レコードを1つのクエリで取得)
- DataLoader パターン(リクエスト内でバッチ処理と重複排除を行う)
コネクションプーリング
データベース接続の確立にはコストがかかります(TCP ハンドシェイク、認証、TLS ネゴシエーション)。コネクションプールには再利用可能な接続が保持されます。
- 想定される同時実行数に合わせてプールのサイズを調整します。小さすぎると待ち行列が発生し、大きすぎるとデータベースに過度な負荷がかかります
- 処理が終わったらすぐに接続をプールに返します。接続がリークするとプールが枯渇するためです
- アイドルタイムアウトを設定し、未使用の接続を回収します
- アプリケーションレベルのプーリングだけでは不十分な場合は、外部プーラー(PostgreSQL 用の PgBouncer など)を使用します
クエリパターン、実行計画の分析、複数の言語による例については、データベース最適化のリファレンスを参照してください。
メモリとリソースの管理
メモリ最適化パターン
| パターン | 説明 |
|---|---|
| オブジェクトプーリング | コストの高いオブジェクトを割り当てて破棄する代わりに再利用します |
| ストリーミング | 大規模なデータセットをすべてメモリに読み込まず、ストリームとして処理します |
| 遅延初期化 | コストの高いオブジェクトの作成を、必要になるまで遅らせます |
| 弱参照 | ガベージコレクションを妨げない参照を保持します |
| バッファーの再利用 | バッファーを一度だけ割り当て、複数の操作で再利用します |
遅延読み込み
遅延読み込みでは、結果が必要になるまで処理を先送りします。起動時間とメモリ使用量を削減できますが、複雑さが増し、後で予期しない遅延が発生することがあります。
遅延読み込みが有効な場合:
- 関連するデータベースレコードを、アクセスされたときにのみ読み込む
- コストの高いサービス接続を初回使用時に初期化する
- UI コンポーネントやアセットを、表示されるときにのみ読み込む
遅延読み込みが逆効果になる場合:
- 先送りした処理が結局必ず発生する場合(オーバーヘッドが増えるだけです)
- 予測可能な起動時の処理を、予測しにくいユーザー操作時の遅延に移してしまう場合
- N+1 クエリパターンが発生する場合(上記の「データベースの最適化」を参照)
バッチ操作
可能な場合は、個別の操作をバッチ処理に置き換えます。
- 1行ずつ挿入する代わりに、一括挿入を行う
- 外部サービスを N 回呼び出す代わりに、API 呼び出しをバッチ処理する
- ファイルを個別に処理する代わりに、ファイル操作を一括で行う
クイックリファレンス:よくあるボトルネックのパターン
| 症状 | 考えられる原因 | 最初の調査手順 |
|---|---|---|
| 応答時間が遅く、CPU 使用率が低い | I/O 待ち(データベース、ネットワーク、ディスク) | I/O をプロファイルし、クエリログを確認します |
| CPU 使用率が高く、応答時間は正常 | 非効率なアルゴリズム、または過剰な計算 | CPU プロファイルで負荷の高い関数を特定します |
| 時間の経過とともにメモリ使用量が増加する | メモリリーク(解放されない参照、上限のないキャッシュ) | 時間をおいてヒープダンプを比較します |
| 負荷時に断続的な遅延が発生する | リソース競合(ロック、コネクションプールの枯渇) | プールサイズとロック待ち時間を確認します |
| ローカルでは速いが、本番環境では遅い | ネットワーク遅延、キャッシュ不足、データ量の違い | 環境間でプロファイリングデータを比較します |
参考ファイル
| 参考資料 | 内容 |
|---|---|
| キャッシュ戦略 | キャッシュレイヤー、無効化パターン、スタンピードの防止策を、複数の言語で例示します |
| データベースの最適化 | クエリの最適化、N+1 の防止、コネクションプーリング、バッチ処理を、複数の言語で例示します |
| プロファイリングパターン | プロファイリングのワークフロー、ボトルネックの特徴、パフォーマンス予算、負荷テストの戦略 |
他のスキルとの連携
| 状況 | 推奨スキル |
|---|---|
| アーキテクチャの問題が原因でパフォーマンスに問題がある | clean-architecture |
| 遅いコードパスをリファクタリングする必要がある | refactoring |
| API の応答時間を最適化する | api-design |
| データベースのスキーマとクエリを設計する | database-design |