目次

読了時間: 約9分 | 文字数: 約3,300字

「推測するな、計測せよ」——Donald Knuth のこの言葉はパフォーマンス最適化の大原則だ。感覚で「ここが遅そう」と最適化しても、真のボトルネックは別の場所にあることが多い。本稿では、計測に基づくパフォーマンス最適化の方法論を、プロファイリングツールの実践とともに解説する。

最適化の大原則

1. まず正しく動かす

パフォーマンスのことは後回しにして、正しく動くコードを書く。「早すぎる最適化は諸悪の根源」(Knuth)。

2. 計測する

プロファイラやベンチマークで現状を正確に把握する。「遅い」ではなく「P99 レイテンシが 2.3 秒」のように数値で把握する。

3. ボトルネックを特定する

パレートの法則——コードの 20% が 80% の実行時間を占める。その 20% を見つけることが最適化の核心だ。

4. 改善して計測する

変更前後のベンチマーク結果を比較する。改善したつもりが悪化していたケースは珍しくない。

バックエンドのプロファイリング

CPU プロファイリング

関数ごとの CPU 使用時間を計測する。

# Python: cProfile
import cProfile
cProfile.run('process_data()', sort='cumulative')

# 結果例
#    ncalls  tottime  cumtime  filename:lineno(function)
#     1000    0.002    5.123  data.py:45(process_data)
#     1000    4.890    4.890  data.py:67(parse_json)   ← ボトルネック

フレームグラフ は CPU プロファイリング結果を視覚的に表示する。横幅が実行時間の割合を示し、一目でボトルネックが分かる。

メモリプロファイリング

メモリの使用量と割り当てパターンを分析する。メモリリーク(解放されないメモリが蓄積する)の検出に使う。

// Go: pprof
import _ "net/http/pprof"
// http://localhost:6060/debug/pprof/heap でヒーププロファイルを取得

データベースクエリの最適化

バックエンドの遅延原因の大部分はデータベースクエリだ。

-- EXPLAIN ANALYZE でクエリプランを確認
EXPLAIN ANALYZE
SELECT u.name, COUNT(o.id) as order_count
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE u.created_at > '2025-01-01'
GROUP BY u.id;

-- Seq Scan が表示されたらインデックスを検討

スロークエリログ を有効にし、一定時間以上かかるクエリを記録する。定期的にレビューし、改善する。

フロントエンドの最適化

Core Web Vitals

Google が定義したユーザー体験の指標。SEO にも影響する。

指標測定内容良好
LCP(Largest Contentful Paint)最大コンテンツの表示時間≤ 2.5秒
INP(Interaction to Next Paint)インタラクションの応答速度≤ 200ms
CLS(Cumulative Layout Shift)レイアウトのずれ≤ 0.1

バンドルサイズの削減

# webpack-bundle-analyzer で依存関係を可視化
npx webpack-bundle-analyzer stats.json

不要なライブラリの除去、Tree Shaking、コード分割(Dynamic Import)でバンドルサイズを削減する。

画像の最適化

画像はページサイズの 50% 以上を占めることが多い。

  • 次世代フォーマット: WebP / AVIF を使う(JPEG より 30-50% 軽量)
  • レスポンシブ画像: srcset で画面サイズに応じた画像を配信
  • 遅延読み込み: loading="lazy" でビューポート外の画像を遅延読み込み

ベンチマークの設計

マイクロベンチマーク

特定の関数やアルゴリズムの性能を測定する。

import timeit
result = timeit.timeit('sort_algorithm(data)', globals=globals(), number=10000)

注意点: マイクロベンチマークの結果が本番環境の性能を反映するとは限らない。キャッシュの効果、GC の影響、コンカレンシーなど、実環境固有の要因がある。

ロードテスト

本番に近い条件で負荷をかけ、システム全体のスループットとレイテンシを測定する。

  • k6: JavaScript でテストシナリオを記述
  • Locust: Python ベースの負荷テストツール
  • Apache Bench(ab): シンプルな HTTP ベンチマーク

よくある最適化パターン

パターン効果適用場面
キャッシュの追加劇的同じデータの繰り返し読み取り
N+1 クエリの解消大きいORM を使っている場合
インデックスの追加大きいWHERE, JOIN の遅いクエリ
非同期処理への移行中程度時間のかかる処理をバックグラウンドに
アルゴリズムの改善場合によるO(n²) → O(n log n)

So What?——実務への応用

  • 本番環境のプロファイリングを行う: 開発環境のプロファイリングだけでは不十分。APM ツール(Datadog, New Relic)で本番の実データを分析する
  • パフォーマンスバジェットを設定する: 「LCP は 2.5秒以下」「API レスポンスは P99 で 500ms 以下」のように目標値を決め、CI で自動チェックする
  • 最適化は ROI で判断する: P99 を 200ms から 150ms に改善する労力と、データベースクエリを 3秒から 0.3秒にする労力は同じではない。効果の大きい箇所から着手する
  • リグレッションを防ぐ: パフォーマンステストを CI/CD パイプラインに組み込み、デグレードを早期に検出する

パフォーマンス最適化は芸術ではなく科学だ。計測し、仮説を立て、変更し、再計測する。このサイクルを回し続けることが、高速なシステムを維持する唯一の方法だ。

参考リンク

免責事項 — 掲載情報は執筆時点のものです。料金・機能は変更される場合があります。最新情報は各公式サイトをご確認ください。