Inference Performance Metrics(推論性能指標)

article technology high #inference#performance-metrics#ttft#tpot#goodput#slo#benchmarking#latency#throughput
Created: 2026-05-26 Updated:

LLM 推論の性能指標を TTFT/TPOT/ITL のレイテンシ、Throughput/Goodput のスループット、KV/MFU の利用率の三軸で整理し、SLO 設計と open-loop ベンチマーク手法まで体系化。

Inference Performance Metrics(推論性能指標)

LLM 推論の性能を「tokens/sec」一本で語ると本番で破綻する。Prefill と decode は計算プロファイルが正反対、出力長は heavy-tail、ストリーミング UX は first-token と inter-token の二相、SLO を割った throughput は意味を持たない。指標はレイテンシ (TTFT / TPOT / ITL)、スループット (tokens/sec / Goodput)、利用率 (KV / MFU / HBM) の三軸で組み、必ず p99 と SLO 条件付きで測る。本稿は連続バッチング (tech-43) を前提に定義・式・計測法・5 大ピットフォールを整理する。KV キャッシュ自体は tech-38、Transformer は tech-35 を参照。

なぜ “throughput だけ” では破綻するか

「合計 tokens/sec」を単一目標にすると 4 つの罠が同時に効く。

SLO 無視の throughput は goodput が崩れる。GPU 利用率を 95 % まで押し込めば peak throughput は伸び続けるが、その帯域では tail latency が TTFT / TPOT SLO を割り、ユーザから見れば「速いが応答しない」状態になる。意味があるのは SLO 遵守リクエストだけで測る Goodput で、throughput と goodput は飽和直前で乖離する (DistServe, arXiv:2401.09670)。

Prefill と decode の非対称。Prefill は compute-bound、decode は memory-bandwidth-bound。両者を同 tokens/sec に混ぜると、prefill-heavy (RAG の長プロンプト) と decode-heavy (対話の長返答) で全く違うシステムが「同性能」に見える。

Long-tail decode。出力長は Pareto / log-normal 分布で、p99 は中央値の 10 倍以上トークンを出すことが珍しくない。median TPOT では長 decode の劣化を見落とす。SLO は p99 で書く。

Batch=1 計測の罠。最小レイテンシは出るがバッチング効果ゼロで、本番の容量限界を反映しない。実効 batch 8〜64 の領域で計測する。

レイテンシ指標 — TTFT / TPOT / ITL / E2E

LLM 推論のレイテンシは 4 指標で分解する。

TTFT (Time to First Token)。リクエスト投入から最初の出力トークン受信までの wall-clock。

TTFT = T_queue + T_prefill

T_queue はキュー待ち、T_prefill はプロンプト処理 (compute-bound、prompt_len × model_size でスケール)。Chunked prefill (Sarathi-Serve, arXiv:2403.02310) は長 prefill を分割し TTFT スパイクを抑える代わりに総 prefill 時間が微増する。ストリーミング UX では TTFT が体感の初動を決める。

TPOT (Time Per Output Token)。デコード相のトークンあたり平均時間。

TPOT = (T_last_token - T_first_token) / (output_length - 1)

Decode は memory-bandwidth-bound で下限は model_size_bytes / HBM_bandwidth。7B FP16 を HBM 帯域 2 TB/s 級で回すと batch=1 で ~7 ms/token が帯域起因の下限 [VERIFY: 実装と量子化で変動]。バッチングは重み読みを償却し、batch=32 程度で実効 TPOT は概ね 30〜60 ms/token のオーダー (workload・HW でぶれる)。

ITL (Inter-Token Latency)。連続 2 トークン間の瞬時時間。全 step 平均で ITL ≈ TPOT だが、連続バッチング下で co-batched 長 prefill chunk が混ざった step で ITL がスパイクし、ストリーミングクライアントはこれを観測する。TPOT は均してしまうため UX 観点では ITL の p99 も別途見る。

E2E latencyE2E = TTFT + TPOT × (output_length - 1)。非ストリーミングは E2E 全長を待つ。TTFT=2s / TPOT=5msTTFT=100ms / TPOT=200ms は 20 トークン応答で同じ E2E でも UX も最適化対象も別物。報告は p50 / p95 / p99 の三点で行う。出力長が heavy-tail である以上 p50 は実挙動を隠す。

スループット指標 — Tokens/sec と Goodput

スループットは粒度を分けて測らなければ意味を失う。

Tokens/sec の 3 区分Output throughput = Σ output_tokens / wall_s (decode、最頻出)、Input throughput = Σ input_tokens / wall_s (prefill、RAG で重要)、Total = Σ (input + output) / wall_s (コスト試算)。論文が単に「X tokens/sec」と書けば通常 output。Input/output を分けない計測は prefill-decode 非対称を隠す。

RPS の限界。RPS は出力長ばらつきを吸収しない。10 RPS × 10 トークン応答と 10 RPS × 1000 トークン応答は GPU 負荷が 100 倍違うのに RPS は同値。token throughput を主、RPS を副に扱う。「Concurrent users」は in-flight 数で、連続バッチング下では実効 batch size に近い。

Goodput。DistServe (arXiv:2401.09670) が提唱した SLO 遵守条件付きスループット。

Goodput = Σ output_tokens (SLO-compliant only) / wall_clock_s

SLO 遵守は TTFT < T_ttft AND TPOT < T_tpot at p99 の同時成立。throughput と goodput は低負荷では一致するが、saturation 近傍 (peak の 90〜95 %) で goodput が崩落し throughput だけが伸び続ける — この乖離が本番の天井。SLO 無し raw throughput 報告はベンチの最大 anti-pattern。Goodput と併記する。

利用率指標 — KV / MFU / HBM bandwidth

レイテンシとスループットだけ見ていても、なぜ詰まっているか説明できない。利用率指標が補助線になる。

KV cache utilization。連続バッチングの実効 batch を支配するのは KV メモリで、バイト数は tech-43 と同式。

KV_bytes = 2 × layers × heads × head_dim × seq_len × batch × dtype_bytes

KV_util = KV_blocks_in_use / total_KV_blocks。目標帯 75〜85 %。70 % 未満は underloaded、90 % 超は PagedAttention (Kwon et al., arXiv:2309.06180) でも preempt / swap リスクが上がる。vLLM は gpu_cache_usage_perc で露出。

MFU (Model FLOPS Utilization) = 実測 FLOP/s / peak FLOP/s。Transformer 推論の理論 FLOP/トークンは prefill で 2 × n_params、decode で 2 × n_params / batch 程度 [VERIFY: アーキで微変動]。Decode は memory-bandwidth-bound なので MFU 30〜40 % で頭打ちが普通、60〜70 % まで上がれば prefill 支配か大バッチで compute-bound 側に入った合図。

HBM 帯域利用率 = 実測 byte/s / peak HBM byte/s。Decode で重み読みが支配的で batch=1 ではほぼ peak に張りつく — これが TPOT 下限の物理理由。バッチングで重み読みを償却し、十分大バッチで初めて compute-bound に倒れる。SM occupancy は memory-bound 演算で高くても FLOP/s は低いままになり得るため MFU とセットで読む。

投機的デコード関連指標

投機的デコードは K 個のドラフトトークンを軽量モデルが提案、ターゲットが 1 forward で並列検証、最初の不一致まで accept する。指標は通常のレイテンシ/スループットに acceptance 系を加える。

Acceptance rate と accept length

acceptance_rate = avg(accepted_tokens / K) accept_length = avg(accepted_tokens per step) = acceptance_rate × K

タスクとドラフトモデルの整合度で決まり、ドメインが合えば 0.7〜0.9 程度 [VERIFY: タスク依存で変動]。

理論 speedupc_draft << c_target のとき近似 speedup ≈ accept_length + 1。K=5・rate=0.8 なら accept_length=4 で speedup ≈ 5 倍が理論上限。ドラフトコストが無視できないと speedup ≈ (accept_length + 1) / (K × c_draft/c_target + 1) に緩む。

Rejection rate = 1 - acceptance_rate。0.4 超ならドラフトが target 分布と合わず、K を下げるか別ドラフトに替える判断材料。Medusa (複数予測ヘッド + tree attention) や EAGLE (hidden state 条件付きドラフト) は accept_length を高く保つが、リクエストあたりメモリ圧が増えバッチ時の不均衡をベンチで別途確認する必要がある。連続バッチング下で accept 数が系列ごとに違って KV 更新が不揃いになるため、batch=1 の speedup は本番で再現しない。

ベンチマーク方法論

何で測るかと同じくらい、どう測るかが結論を決める。

Closed-loop vs open-loop。closed-loop は前リクエスト完了後に次を投げる方式で、アイドル時間がレイテンシに含まれず本番のキュー深さを系統的に過小評価する。open-loop は固定到着率 (Poisson か deterministic) で投入し完了に依存せず、saturation 直前のキュー詰まりを正しく拾う。SLO 検証は必ず open-loop。

Saturation regime と warmup/cool-down。warmup は最初の 10〜20 % を捨てる (JIT・CUDA graph capture・KV warm state 安定化待ち)。saturation regime は「負荷を上げても goodput が増えない」点で本番の天井。cool-down は in-flight 完了を待たないと切り捨てバイアスが p99 に乗る。

主要ツール

ツール提供元特徴
vllm/benchmarks/benchmark_serving.pyvLLM projectopen-loop、到着率と dataset (ShareGPT) を指定、TTFT/TPOT を percentile で報告。https://github.com/vllm-project/vllm/tree/main/benchmarks
GenAI-PerfNVIDIA (Triton 系)Triton + TensorRT-LLM 向け、TTFT / TPOT / ITL / throughput を統合報告
LLMPerfRay / Anyscale複数推論バックエンドを横並びで open-loop ベンチ。https://github.com/ray-project/llmperf
MLPerf InferenceMLCommons標準化ベンチ、Llama-2 70B Server シナリオは open-loop + TTFT/TPOT SLO 遵守判定。https://mlcommons.org/benchmarks/inference/

SLO 表記TTFT < T_ttft AND TPOT < T_tpot at p99 の同時条件として書く。代表的に conversational AI / code completion / RAG ではそれぞれ異なる帯域を要求するが、数値はワークロード・モデルサイズ・SLA で大きく変動するため、本記事では具体数値を断定しない。重要なのは「median ではなく p99」「TTFT と TPOT を別 SLO で同時に課す」という枠組み。

5 大ピットフォール

(a) SLO 条件なしで throughput を報告。saturation 直前で raw tokens/sec は伸び続ける一方 goodput は崩落するため、SLO 無しの数値はビジネス価値とほぼ無相関。Goodput を併記する。

(b) Closed-loop で本番待ち時間を過小評価。closed-loop はキュー深さゼロで測るため、同 GPU 負荷の open-loop Poisson で 2〜5 倍の p99 TTFT 差が出る [VERIFY: 倍率は workload 依存]。SLO 検証は open-loop 必須。

(c) Prefill-heavy と decode-heavy を単一指標で混ぜる。10-token と 10K-token のプロンプトを同じ tokens/sec で平均すると compute-bound 相と memory-bound 相がブレンドされる。Input/output を分け prompt 長バケットで disaggregate する。

(d) Batch=1 計測でバッチング効果を見ない。最小レイテンシは出るが本番の容量限界とは無関係。実効 batch 8〜64 の領域でテストし、KV 利用率 75〜85 % を維持しながら p99 を観測する。

(e) Per-token と E2E を区別しない。E2E だけ見ると TTFT=2s / TPOT=5msTTFT=100ms / TPOT=200ms が 20 トークン応答で同値になる。UX も最適化対象 (前者は prefill / queueing、後者は decode 帯域) も別物なので TTFT と TPOT を分けて報告し、トークナイザ overhead (CPU 側 text→ID 変換、実装で 1〜20 ms 差) を TTFT に含めるかも明示する。

参考文献

Local graph