📏
概念 #システムパフォーマンス #Brendan Gregg #Linux #SRE #読書ノート #ベンチマーク 📚 詳解 システム・パフォーマンス

詳解 システム・パフォーマンス - 第12章:ベンチマーキング

ベンチマーク設計の原則・アンチパターン・マイクロベンチマークの落とし穴。wrk/fio/iperf3/sysbench の実践的な使い方。

ベンチマーキングの目的

目的
キャパシティプランニングこのサーバーは最大何 RPS 処理できるか
チューニング効果の検証設定変更前後でレイテンシが改善したか
システム比較HDD vs SSD、PostgreSQL vs MySQL
リグレッション検出デプロイ前後でパフォーマンスが劣化していないか

ベンチマーク設計の原則

やること

1. 一度に変更するのは1変数のみ
   → 複数の変更を同時にすると、どれが効いたか分からない

2. 十分な測定回数(最低5回、できれば10回以上)
   → 1回の測定は外れ値の可能性がある

3. ウォームアップ時間を設ける
   → JIT コンパイル・ページキャッシュの安定を待つ

4. 統計値はパーセンタイルで報告する
   → 平均だけでは外れ値を見逃す。p50/p95/p99/p999 で報告

5. 本番環境を模した条件で実施する
   → データ量・並行数・ネットワーク遅延を再現する

やってはいけないこと

アンチパターン問題点
キャッシュウォームアップなしでテストコールドスタートの結果は本番と乖離する
平均値だけで比較レイテンシの外れ値(テールレイテンシ)を見逃す
観測ツールで帯域を消費tcpdump 等が測定に影響を与える
ループ最適化で消えるコードをテストコンパイラが「使われない計算」を除去する
短すぎる測定時間ガベージコレクション・バッファフラッシュのタイミングに依存

ベンチマークツール一覧

HTTP / Web API

# wrk:高性能 HTTP ベンチマーク
wrk -t 4 -c 100 -d 30s http://localhost:8080/api/test
# -t 4: 4スレッド
# -c 100: 100接続(同時)
# -d 30s: 30秒間測定

# 出力例:
# Latency: Avg=5ms, Stdev=2ms, Max=50ms, +/-Stdev=90%
# Req/Sec: Avg=2000, Stdev=100, Max=2200
# Requests/sec: 8000.00
# Transfer/sec: 4.00MB

# wrk2:一定レートでのベンチマーク(コーディネーションオミッション問題を回避)
wrk2 -t 4 -c 100 -d 30s -R 5000 http://localhost:8080/api/test
# -R 5000: 5000 RPS で送信(キューが詰まっても止まらない)

# hey:シンプルな HTTP ベンチマーク
hey -n 10000 -c 100 http://localhost:8080/api/test
# -n: リクエスト総数
# -c: 並行数

ディスク I/O

# fio:汎用ストレージベンチマーク

# シーケンシャル書き込みのスループット
fio --name=seqwrite --rw=write --bs=1m --size=10g \
    --numjobs=1 --ioengine=libaio --direct=1 \
    --group_reporting

# ランダム読み込み IOPS(DB のワークロードに近い)
fio --name=randread --rw=randread --bs=4k --size=10g \
    --numjobs=4 --ioengine=libaio --direct=1 \
    --iodepth=32 --group_reporting

# 混合ワークロード(70% 読み込み、30% 書き込み)
fio --name=mixed --rw=randrw --rwmixread=70 --bs=4k --size=10g \
    --numjobs=4 --ioengine=libaio --direct=1 --iodepth=32

# オプションの意味:
# --ioengine=libaio: 非同期I/O(本番に近い)
# --direct=1: キャッシュを使わない(実際のディスク性能を測る)
# --iodepth=32: キューの深さ(並列I/O数)

ネットワーク

# iperf3:ネットワーク帯域の測定

# サーバー側
iperf3 -s

# クライアント側(TCP スループット)
iperf3 -c <server_ip> -t 30 -P 4
# -t 30: 30秒間
# -P 4: 4並列ストリーム

# UDP 帯域の測定
iperf3 -c <server_ip> -u -b 1G

# 出力例:
# [SUM] 0.00-30.00 sec  35.0 GBytes  9.97 Gbits/sec  sender
# [SUM] 0.00-30.00 sec  34.9 GBytes  9.94 Gbits/sec  receiver

CPU / システム全体

# sysbench:CPU・メモリ・ディスクのシステムベンチマーク

# CPU ベンチマーク(素数計算)
sysbench cpu --cpu-max-prime=20000 --num-threads=4 run

# メモリベンチマーク
sysbench memory --memory-block-size=1M --memory-total-size=10G run

# ディスクI/O
sysbench fileio --file-total-size=10G prepare
sysbench fileio --file-total-size=10G --file-test-mode=rndrw run
sysbench fileio --file-total-size=10G cleanup

# stress-ng:様々なストレステスト
stress-ng --cpu 4 --vm 2 --io 2 --timeout 60s

コーディネーションオミッション問題

最も重要な落とし穴。従来のベンチマークツールが見逃す問題。

通常のベンチマーク(問題あり):
リクエスト1 → [処理中...]
リクエスト2 → [リクエスト1が終わるまで待つ] ← この待ち時間が記録されない
リクエスト3 → [待つ]

→ 結果:レイテンシが過小評価される

wrk2 / HdrHistogram(問題なし):
厳密な一定レートでリクエストを送信
→ キューに積まれた時間も含めてレイテンシを記録
→ テールレイテンシ(p99, p999)が正確に測定できる
# HdrHistogram を使ったレイテンシ計測
# wrk2 + HdrHistogram
wrk2 -t 4 -c 100 -d 60s -R 1000 \
     --latency http://localhost:8080/api/test

# 出力(パーセンタイル分布):
# Latency Distribution
#    50.00%    5.00ms
#    75.00%    7.00ms
#    90.00%   10.00ms
#    99.00%   50.00ms  ← 1% のユーザーはこれだけ遅い
#    99.90%  200.00ms  ← 0.1% はもっと遅い
#    99.99%  500.00ms  ← まれに非常に遅い

チューニング前後の比較

# 変更前の測定(5回繰り返し)
for i in {1..5}; do
  wrk -t 4 -c 100 -d 30s http://localhost:8080/api/test 2>&1 | \
    grep "Requests/sec" | awk '{print $2}'
done

# チューニング実施

# 変更後の測定(同じ条件で5回)
for i in {1..5}; do
  wrk -t 4 -c 100 -d 30s http://localhost:8080/api/test 2>&1 | \
    grep "Requests/sec" | awk '{print $2}'
done

# 統計的に有意な差があるか確認
# - 平均・中央値・標準偏差で比較
# - 範囲が重なっている場合は誤差の可能性

ベンチマーク時の観測

ベンチマーク中は同時に観測ツールも動かして、ボトルネックを特定する。

# ウィンドウ1: ベンチマーク実行
wrk -t 4 -c 100 -d 60s http://localhost:8080/

# ウィンドウ2: リソース使用状況を監視
vmstat 1
mpstat -P ALL 1
iostat -xz 1

# ウィンドウ3: プロファイリング
perf record -F 99 -a -g sleep 30
# ベンチマーク完了後
perf report

出典: 詳解 システム・パフォーマンス 第2版 Brendan Gregg著