0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ninda 23日目: パフォーマンスチューニング

Last updated at Posted at 2025-12-22

今日のゴール

nindaのパフォーマンスを最大限に引き出すためのチューニング手法を学びます。計測から始め、ボトルネックを特定し、段階的に改善するという原則を身につけましょう。


パフォーマンス指標

まず、何を計測すべきかを明確にします。

4つの重要な指標

指標 意味 nindaでの例
スループット 単位時間あたりの処理量 秒間10万write操作
レイテンシ 1操作の応答時間 P99で5ms以内
リソース使用率 CPU、メモリ、I/O CPU 70%以下
エラー率 失敗した操作の割合 0.01%未満

レイテンシのパーセンタイル

「平均レイテンシ」だけでは不十分です。ユーザー体験を正確に把握するにはパーセンタイルを見ます。

P50  (中央値): 50%のリクエストがこの時間以内
P95          : 95%のリクエストがこの時間以内
P99          : 99%のリクエストがこの時間以内
P99.9        : 1000回に1回の「最悪のケース」

💡 なぜP99が重要? ユーザーは複数回リクエストを行います。10回中1回でも遅いと、ユーザーは「このシステムは遅い」と感じます。


ボトルネックの特定

最適化の鉄則

❌ 推測で最適化する → 効果がない or 逆効果
✅ 計測してから最適化 → 確実に効果がある

よくあるボトルネック


メモリ最適化

3つのポイント

ポイント 問題 解決策
適切な型 数値を文字列で保存 intVal(12345) を使用
アロケーション 毎回new オブジェクトプール
GC 停止時間 循環参照を避ける

オブジェクトプールの考え方

# プールから取得 → 使用 → プールに返却
var t = pool.acquire()  # 再利用
t.add(?"data")
# 処理...
pool.release(t)         # GCを呼ばず再利用

効果: 高頻度操作でGC負荷を大幅に削減


インデックス最適化

nindaは第一フィールドでタプルをインデックス化します。この特性を活かした設計が重要です。

良い設計 vs 悪い設計

# ❌ 悪い例: 全て "data" で始まる
space.write(@[?"data", ?"user", ?1, ?"John"])
space.write(@[?"data", ?"order", ?100])
# → 同じバケットに集中、検索が遅い

# ✅ 良い例: タイプを第一フィールドに
space.write(@[?"user", ?1, ?"John"])
space.write(@[?"order", ?100])
# → タイプごとにバケット分散

# ✅✅ さらに良い: 検索キーを先頭に
space.write(@[?1, ?"user", ?"John"])  # ユーザーIDで検索が多い場合

並行処理の最適化

ロック競合の問題

全スレッドが1つのロックで待機 → スケールしない

解決策: シャーディング

# 第一フィールドのハッシュでシャードを決定
proc getShard(t: Tuple): int =
  hash(t[0]) and 0xF  # 16シャード

効果: 16シャードなら、ロック競合が1/16に


ネットワーク最適化

分散環境では、ネットワークがボトルネックになりがちです。

3つの手法

手法 効果 トレードオフ
圧縮 帯域削減 50-90% CPU負荷増
バッファリング syscall削減 レイテンシ増
パイプライニング RTT削減 実装複雑

パイプライニングの威力

従来方式(3回の往復):
  Request1 → Response1 → Request2 → Response2 → Request3 → Response3
  合計: 3 RTT

パイプライン(1回の往復):
  Request1, Request2, Request3 → Response1, Response2, Response3
  合計: 1 RTT

効果: RTT=10msなら、3リクエストで20ms節約


WAL最適化

永続化のボトルネックはディスクI/Oです。

バッチ書き込み

グループコミット

複数のトランザクションを1回のfsyncでまとめてコミットします。

設定 特性
コミット間隔 1ms 低レイテンシ、高fsync頻度
コミット間隔 10ms 高スループット、レイテンシ増
コミット間隔 100ms 最高スループット、レイテンシ大

💡 fsyncは遅い: SSDでも1回数ミリ秒。バッチ化で大幅に改善できます。


パフォーマンスチェックリスト

設計段階

  • 第一フィールドに検索キーを配置
  • 想定スループット・レイテンシを定義
  • キャパシティプランニング実施

実装段階

  • 不要なアロケーション削減
  • 適切なバッファサイズ設定
  • ロック競合の最小化

運用段階

  • 定期的なベンチマーク
  • メトリクス監視
  • WALコンパクション設定

チューニングの原則

4つのルール

  1. 計測から始める: 推測ではなくデータに基づく
  2. ボトルネックを特定: 最も遅い部分から改善
  3. トレードオフを理解: メモリ ↔ CPU、レイテンシ ↔ スループット
  4. 段階的に改善: 一度に複数変更しない

よくある間違い

間違い なぜ問題か
全てを最適化 80%の時間は20%のコードで消費される
計測なしで最適化 効果がない変更に時間を浪費
複数同時変更 どの変更が効いたか分からない

本日のまとめ

学んだこと

トピック ポイント
パフォーマンス指標 スループット、レイテンシ(P99)、リソース使用率
メモリ最適化 適切な型、オブジェクトプール、GC考慮
インデックス最適化 第一フィールドの重要性
並行処理最適化 シャーディングでロック競合削減
ネットワーク最適化 圧縮、バッファリング、パイプライニング
WAL最適化 バッチ書き込み、グループコミット

最重要ポイント

計測 → 特定 → 最適化 → 検証

推測ではなく、データに基づいて判断しましょう。


演習問題

問題23-1: ベンチマーク設計

write、read、takeの各操作について、スループットとP99レイテンシを計測するベンチマークを設計してください。何を変数として、何を固定すべきでしょうか。

問題23-2: ボトルネック分析

あるシステムで「平均レイテンシ 5ms、P99 レイテンシ 500ms」という結果が出ました。この状況から何が推測できますか?どのような調査を行うべきでしょうか。

問題23-3: シャード設計

1億件のユーザーデータを格納するシステムで、シャード数をいくつにすべきか検討してください。考慮すべき要素を列挙してください。

💡 完全な実装例は GitHubリポジトリsrc/ninda/ を参照してください。


次回は、nindaと他の技術(Redis、Kafka等)との比較を行い、適切な技術選択について学びます。

前回: 障害検知と復旧戦略 | 目次 | 次回: 他の技術との比較

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?