どうも。鳩胸になりたい文鳥です。
アプリケーションのパフォーマンス問題って、地味に優先度高かったりするけれど、結構チームで対応できる人が固定されてたりしますよね。「どこから手をつけていいか分からん…」「ボトルネックの特定が難しすぎる…」って感じるジュニアエンジニアの方も多いんとちゃいますか。
読者とスコープ
- 1 → 10フェーズで顕在化したパフォーマンスの問題に直面している方
- アプリケーション側に原因があるケースを想定
- パフォーマンスイシューにこれから取り組んでみたいジュニアエンジニアの方
- チームメンバーにパフォーマンスISSUEを振りたいが手順を教えている余裕がないシニアエンジニア
大規模システムにおける複雑なパフォーマンスISSUEやインフラ構成の変更などの対応は、ここでは深く掘り下げません。あくまで、アプリケーション改善の基本的なアプローチとAIの活用に焦点を当てていきます。
そもそも筆者がそんな技術力もってない…
AIは、パフォーマンスチューニングのいろんなフェーズで力になってくれます。
- ボトルネックの特定補助: エラーログ、スロークエリログ、メトリクスなどをAIに分析させて、潜在的なボトルネックのヒントを得る
- 改善案のブレインストーミング: 特定されたボトルネックに対して、AIに複数の改善策を提案させる。例えば「N+1問題の解決策を5つ提案して」といった具体的な問いかけが可能
- コードの最適化提案: 特定の遅いコードブロックやクエリを指定して、より効率的な実装方法や改善点を提案してもらう
- テストシナリオの生成: パフォーマンス改善の前後比較を行うためのテストシナリオやデータ生成のアイデアを得る
- 学習と知識の深化: パフォーマンスチューニングに関する基礎知識や、特定の技術スタックにおけるベストプラクティスについてAIに質問して、理解を深める
パフォーマンスチューニングの戦略で一番大切なのは、大きい粒度でのボトルネック特定から徐々にスコープを狭めながら問題をクリアにして局所的に対応することです。
いきなり細かいコードレベルの最適化に走るんじゃなくて、まずは「フロントエンドが遅いのか、バックエンドが遅いのか」「データベースが原因なのか、アプリケーションロジックが原因なのか」といった大枠から攻めていくのが鉄則ですね。
AIに任せすぎると危険
ここで注意が必要なのが、AIに任せすぎると機能としての振る舞いが変わってしまう提案をしてくることがあるという点です。
例えば、「このAPIが遅いので改善して」みたいな漠然とした指示を出すと、AIは親切心で以下のような提案をしてくることがあります:
- 変数の代入方法などリスクも小さいがメリットも非常に小さい効率化(ユーザー体験にはほぼ関係ない)
- キャッシュ戦略を大幅に変更(データの整合性に影響する可能性)
- データベースのスキーマ変更(既存機能への影響が大きい)
これらの修正は確かに処理効率は上がるかもしれませんが、ユーザー体験としてはほぼ関係なかったり、最悪の場合は既存機能を壊してしまう可能性がありますので、よく検討する必要があります。
課題の認知からスコープの小さい具体的な指示を出すことが大事
だからこそ、課題の認知(≒ボトルネック特定)から、スコープの小さい具体的な指示を出すことが大事だと思います。
X :「このAPIが遅いので改善してください」
O :「このAPIで特定のSQLクエリに関してjoin先のレコードooで起きているN+1問題を、Eager Loadingして下さい」
具体的な指示を出すためにも、まずは自分でしっかりとボトルネックを特定する作業が必要になってきます。
パフォーマンスチューニングの第一歩は、漠然とした「遅い」という感覚を、具体的な数値と事象に落とし込むことです。
計測が全てです。と言っても過言ではないかも知れません
具体的な計測ポイント:
- ユーザー体感時間:
- WebブラウザのDeveloper Tools (Chrome DevToolsなど) の Networkタブ で各リソースのロード時間、APIのレスポンスタイムを確認
- Performanceタブ でレンダリングやスクリプトの実行状況をプロファイルする
- サーバーサイドの処理時間:
- ログに処理開始・終了時刻を記録して、特定処理にかかる時間を計測
- データベースの負荷:
- スロークエリログ を有効にして、実行に時間のかかるSQLクエリを特定
- データベースのCPU使用率、I/O待機時間、コネクション数などを監視
- EXPLAIN (SQL) コマンドでクエリの実行計画を分析
利用しているフレームワークによっては実行クエリの一覧とそのクエリの実行時間を出してくれるdebugツールがあるので調べて見ることをお勧めします。
これらの情報から、問題がフロントエンド、バックエンド、データベース、ネットワークのどこにあるのかを切り分けていきます。
計測データに基づいて、最もパフォーマンスを阻害している「ボトルネック」を見つけ出します。
-
フロントエンド vs. バックエンド:
- Chrome DevToolsのネットワークタブで、APIレスポンスタイムが遅い場合はバックエンド
- APIレスポンスは速いが、ページの描画が遅い場合はフロントエンド(大量のDOM操作、重いJavaScript、レンダリングブロックなど)
- バックエンド内部の切り分け:
- SQLの発行回数: 特定のAPIコールで不必要なSQLクエリが大量に発行されていないか(N+1問題など)
- 特定のSQLの実行時間: 特定のクエリが非常に遅い場合、そのクエリ自体が問題
- 外部サービス連携: 外部APIへのコールが遅延の原因になっていないか
- CPU負荷: 複雑な計算処理や大量データ処理によるCPUボトルネック
- メモリ使用*: メモリリークや非効率なデータ構造によるメモリ不足
問題の解像度を上げながら、対象を絞り込んでいくイメージです。AIにこれらのログや計測結果を渡して、「このデータからボトルネックを推測して」と質問するのもよいです。
特定プロセスとソリューション検討の記録
ボトルネックの特定や、それに対するソリューションの検討プロセスは、きちんと残しておくことがめちゃくちゃ大事です。もし行き詰まったときに、上司やテックリードに相談したくなる時あります。そういう時に、具体的な数値が整理されていて、AIが提案した代替案まで残しておけば、相談しながら解決の糸口を見つけられますからね。
逆にこれがないとこの一週間なにしてましたん?みたいになります
パフォーマンスISSUEは新規機能開発に比べて工数が読めないので、「え、サボってた?」みたいにならないためにも、「今日はここまで分かりました。」のように自分から出てきた情報をtimesチャンネルなどでつぶやいておくのも良いとおもいます。
バックエンドにボトルネックがあると特定できた場合、以下のようなプロンプトでAIを活用して、具体的な改善策を検討できます。
AIへのプロンプト例:
- 対象API:
/api/users
、レコード数: 10,000件、でレスポンスタイムが30秒かかっています。現在の実装では、関連するorders
テーブルから各ユーザーの注文履歴を個別に取得しています。ボトルネックを特定し、改善案を6つ提案してください- 考えた各ソリューション(例:インデックス追加、N+1解消、キャッシング導入、非同期処理化・Job並列化、DBシャーディング)に対して、ROI(投資対効果)とデグレリスクを比較して、真っ先に取り組むべき順番で並べてください。また、それぞれの具体的な実装方法の概要と、注意点についても簡潔に教えてください
AIが提案する可能性のある改善策 (例)
- データベースインデックスの最適化
- 対象:頻繁に検索されるカラム、JOIN条件に使用されるカラム
- ROI:高。デグレリスク:低~中(適切なインデックスであれば)
- 注意点:書き込み性能に影響
- N+1問題の解消:
- 対象:リレーションシップのあるデータを取得する際に、1つの親データごとに子データを個別取得している場合。Eager LoadingやJOINで一括取得
- ROI:高。デグレリスク:中
- 注意点:コード変更が必要、メモリ使用量増加の可能性
- キャッシングの導入:
- 対象:頻繁にアクセスされ、かつ更新頻度の低いデータ
- ROI:中~高。デグレリスク:中(キャッシュの無効化戦略が複雑になりがち)
- 注意点:キャッシュミス時のパフォーマンス劣化、データ鮮度の問題
- 同期処理/バッチ処理/並列化:
- 対象:リアルタイム性を要求されない重い処理
- ROI:中。デグレリスク:中
- 注意点:実装が複雑になる、最終的な結果の整合性、DB負荷が上がるかも知れない
- データベースの正規化/非正規化の見直し:
- 対象:データモデル自体がクエリに不向きな場合
- ROI:低~中(大規模な変更になるため)。デグレリスク:高
- 注意点:データの整合性、アプリケーションコードへの影響
重要な注意点: リスクの高い対応は、複数のソリューションを同時に行わず、一つずつ効果を検証しながら進めるのが鉄則です。
Jobの並列化やデータスキーマの変更は一般論として修正コストが大きいのでまずはSQLの最適化やN+1の解消でボトルネック解消できないか検討しましょう。大規模システムに関わってる方は、そんな簡単な修正でパフォーマンス改善はできないと思うかも知れないですが、1→10のフェーズだとこういった問題がボトルネックの原因であることも私の経験では多いです。
AIが生成したコードのレビューと理解
パフォーマンスISSUEに対する修正って、「なぜこの修正が必要なのか」「どんな意図があるのか」がレビュワーに伝わりにくいことが多いんですよね。「何でこの原因に対して修正がこうなったん?」ってなりがちです。
だから、まず自分がAIの上げてきたコードの内容をクリアに理解して、それをレビュー時に分かりやすくする努力をしましょう。挙動的には問題なさそうでパフォーマンスも改善はしているけど、何か不安な部分がある場合には、それを隠さず積極的にレビュー時に伝えるようにしましょうね。
AIとの対話でデグレリスクを確認する
AIの上げてきたコードの修正範囲が思ったより大きい場合、「これ、デグレしないかな?」「なんか挙動変わったりしないかな?」って心配になることがあります。
私なんかだと「この修正を行うことでデグレを懸念しています。修正前後で振る舞いが変わるリスクはありますか?具体的にどんなケースで影響が出そうですか?」などと質問するようにしています。そうすると、AIがだしたコードに対してリスクや影響範囲を教えてくれるので、リスクを理解した上で次のアクションを決められると思います。
いらない修正を入れてくることはあるので注意しておきましょう。
パフォーマンスチューニングは、変更を加えるたびにその効果を測定して、期待通りの改善が得られているかを確認することが不可欠だと思います。早くなってることと同じぐらいかそれ以上に大事といても過言ではありません。
パフォーマンスで影響が出てる機能はシステムの根幹部分だったりユーザーが多く使ってる影響の大きい機能であることが多いからです。
自動テストがない場合はそれを書くことからはじめます。修正に対してどんなテストが必要かAIに整理してもらうこともあります。
-
機能テストの実施: パフォーマンス改善によって、既存の機能がデグレードしていないか(特に、検索条件の変更やBatch処理の結果など)
- 検索機能なら、**同一検索条件で修正前後のJSONデータをスプレッドシートなどに吐き出して、if式で比較して全ての箇所で差異がないことを確認
- Batch処理であれば、実行後のDBの状態を比較して、期待通りのデータが生成・更新されているか検証
-
パフォーマンスメトリクスの再計測:
- 変更前と同じ条件でAPIレスポンスタイム、SQL実行時間、CPU/メモリ使用量などを再計測
生成AIは、パフォーマンスチューニングのさまざまな知識をジュニアエンジニアに与えてくれるため、使い方によっては、効率的に問題に取り組める強力なツールになりえます。重要なのは、AIを「答えを出すだけのツール」として使うんじゃなくて、「思考のパートナー」として活用することですね。最終的に、どの修正を入れるかや修正に責任を持つのは自分なのでよく理解して進めるようにしましょう。
ただし、AIに丸投げするんじゃなくて、まずは自分でしっかりとボトルネックを特定して、具体的で範囲の狭い指示を出すことが大切です。そうしないと、ユーザー体験に関係ない微細な最適化や、既存機能を壊しかねない大幅な変更を提案されちゃうかも知れないです。
ユーザー体験に関係ない微細な最適化も別にやってもいいのですが、プルリクエストレビュー時にノイズになるのでプルリクエストを分けた方がリスクを下げられます。
ISSUEにアサインされた時だけでなく「あれこの機能なんか遅いな」とかパフォーマンスの問題は認識されているが取り組めてない機能の改修にアサインされたとき「なんかパフォーマンス改善できることはないかな」と常にアンテナを貼っておくことも大事です。パフォーマンス改善は機能をよく知っていることが有利に働くので、特定の機能に向き合ったとき「ここなんか改善できそう」といった情報を残しておくと非常に喜ばれたりします。
なんだかんだ「仕様変更」が一番パフォーマンス改善に直結することもあるので、その機能がユーザーにとってなんの役割があるか把握しておくことをお勧めします。大掛かりなパフォーマンスチューニングになる場合はメリデメを踏まえて、ステークホルダーや上司に相談します。
計測と分析を徹底して、AIの提案を参考にしながら、一つずつ改善を進めていきましょう。パフォーマンスチューニングは奥深いですし筆者も特筆して能力が高いわけではないです。パフォーマンス改善の過程で考慮することが多く悩むこともありますが、ユーザーのポジティブな影響とシステムの安定性、エンジニア自身の成長につながると思います。特に近年はAIが使えるようになって取り組みやすくなったと感じます。
これを機にAIと一緒にパフォーマンスチューニングにチャレンジしてみてください。
よかったら拡散、いいねお願いします
Views: 0