ストーリー
高橋アーキテクトは首を振った。
4つのボトルネック
1. CPU バウンド
CPUの計算能力が制約となるケース。暗号化処理、画像変換、複雑なビジネスロジックなどが該当します。
// CPU バウンドの例:大量データの集計処理
function aggregateReport(records: Record[]): Report {
// 数百万レコードのフィルタリング・集計
return records
.filter(r => r.isActive)
.map(r => transformRecord(r)) // 重い変換処理
.reduce((acc, r) => merge(acc, r), initialReport);
}
// 対策例:Worker Thread で並列化
import { Worker } from 'worker_threads';
async function parallelAggregate(records: Record[]): Promise<Report> {
const chunkSize = Math.ceil(records.length / 4); // 4スレッドに分割
const chunks = splitIntoChunks(records, chunkSize);
const results = await Promise.all(
chunks.map(chunk => runInWorker(chunk))
);
return mergeReports(results);
}
兆候: CPU使用率が常に90%以上、処理待ちキューの増大
2. I/O バウンド
ディスクI/Oやネットワーク通信の待ち時間が制約となるケース。
// I/O バウンドの例:逐次的なAPI呼び出し
async function fetchAllData(): Promise<Data[]> {
// 悪い例:1つずつ順番に待つ
const users = await fetchUsers(); // 200ms
const orders = await fetchOrders(); // 300ms
const products = await fetchProducts(); // 150ms
// 合計: 650ms
return combineData(users, orders, products);
}
// 改善:並列実行
async function fetchAllDataOptimized(): Promise<Data[]> {
const [users, orders, products] = await Promise.all([
fetchUsers(), // 200ms
fetchOrders(), // 300ms
fetchProducts(), // 150ms
]);
// 合計: 300ms(最も遅い処理の時間)
return combineData(users, orders, products);
}
兆候: CPU使用率は低いのに処理が遅い、I/O wait が高い
3. メモリバウンド
メモリ不足やGC(ガベージコレクション)が制約となるケース。
// メモリバウンドの例:大量データの一括読み込み
async function processLargeFile(path: string): Promise<void> {
// 悪い例:全データをメモリに読み込む
const allData = await fs.readFile(path, 'utf-8'); // 数GBのファイル
const lines = allData.split('\n');
lines.forEach(line => process(line));
// 改善:ストリーム処理
}
// 改善:ストリームで少しずつ処理
async function processLargeFileStream(path: string): Promise<void> {
const stream = fs.createReadStream(path, { encoding: 'utf-8' });
const rl = readline.createInterface({ input: stream });
for await (const line of rl) {
await process(line); // 1行ずつ処理
}
}
兆候: GC停止が頻発、スワップ発生、OOM(Out of Memory)エラー
4. ネットワークバウンド
ネットワーク帯域や接続数が制約となるケース。
// ネットワークバウンドの対策例
class NetworkOptimizer {
// 1. レスポンスの圧縮
enableCompression(): void {
// gzip/brotli 圧縮で転送量を50-80%削減
}
// 2. バッチリクエスト
async batchFetch(ids: string[]): Promise<Item[]> {
// N回のAPI呼び出しを1回にまとめる
return fetch('/api/items/batch', {
method: 'POST',
body: JSON.stringify({ ids }),
}).then(r => r.json());
}
// 3. コネクションプーリング
createPool(): Pool {
return new Pool({
max: 20, // 最大接続数
idleTimeout: 30000,
});
}
}
兆候: 帯域使用率が上限に近い、接続タイムアウトの頻発
ボトルネック特定のフローチャート
レスポンスが遅い
│
├─ CPU使用率は高い?
│ ├─ Yes → CPU バウンド
│ │ → プロファイリングでホットスポット特定
│ └─ No ─┐
│ │
├─ I/O wait は高い?
│ ├─ Yes → I/O バウンド
│ │ → 非同期化、キャッシュ導入を検討
│ └─ No ─┐
│ │
├─ メモリ使用量は高い?
│ ├─ Yes → メモリバウンド
│ │ → メモリリーク調査、ストリーム化
│ └─ No ─┐
│ │
└─ ネットワーク遅延は大きい?
├─ Yes → ネットワークバウンド
│ → 圧縮、バッチ化、CDN導入
└─ No → アプリケーションロジック見直し
計測ツール
| カテゴリ | ツール | 用途 |
|---|---|---|
| CPU | top, htop, perf | CPU使用率、プロセス別負荷 |
| メモリ | free, vmstat | メモリ使用量、スワップ状況 |
| I/O | iostat, iotop | ディスクI/O統計 |
| ネットワーク | netstat, ss, tcpdump | 接続状況、トラフィック分析 |
| APM | Datadog, New Relic | アプリケーション全体の可視化 |
まとめ
| ポイント | 内容 |
|---|---|
| CPUバウンド | 計算処理が制約 → 並列化、アルゴリズム改善 |
| I/Oバウンド | ディスク/ネットワーク待ちが制約 → 非同期、キャッシュ |
| メモリバウンド | メモリ不足/GCが制約 → ストリーム処理、メモリリーク修正 |
| ネットワークバウンド | 帯域/接続数が制約 → 圧縮、バッチ、CDN |
| 原則 | まず計測してボトルネックを特定してから最適化する |
チェックリスト
- 4つのボトルネックの種類と兆候を理解した
- ボトルネック特定の手順を説明できる
- 各ボトルネックへの対策方針を把握した
- 主要な計測ツールを知った
次のステップへ
次は「パフォーマンスバジェット」を学びます。どの程度の速度が必要かを定量的に定義する方法です。
推定読了時間: 25分