TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

小さなモデルで vLLM を実際に測定してみる: 負荷、KV キャッシュ容量、プレフィックスキャッシュ、チャンクサイズ

ステータス: ドラフト (2026-10-07)、実測記事です。 すべての数値は筆者が下記の環境で直接測定した値であり、計算によるものは「計算」、測定できなかったものは「未測定」と表記しました。ひとつの環境、ひとつのモデル、ひとたびのテストの束であるため、絶対値よりも 傾向と原理を持ち帰っていただければ幸いです。 作成ルール: 実測データを基本とし、測定できなかったものは「未測定」と表記します。

目次

1. 何を知りたかったのか

vLLM のドキュメントにはオプションが多く、面接や運用では「このオプションを有効にすると何がどれほど変わるのか」が問われます。第 4 篇では原理(KV キャッシュの計算式、プレフィックスキャッシュ、スケジューラー)をドキュメントベースで整理し、この記事では同じ質問に数値で答えます。

  1. 同時リクエストが増えると、スループットとレイテンシはどこで低下するのか。
  2. KV キャッシュ容量は計算式による値と一致するか、そしてその容量が低下する地点とどうつながっているか。
  3. プレフィックスキャッシュ、KV 8ビット、CUDA グラフ、チャンクサイズ、最大同時シーケンス数はそれぞれ何を変化させるか。

2. 環境と方法

項目 値
GPU 8GB 級ノートPC 用 GPU 1枚(メモリ約 7.6GiB)、他の作業なし
サーバー vLLM 0.31.0 公式イメージ、PyTorch 2.13(CUDA 13)
モデル Qwen3-1.7B(bf16、サーバーがロードした重み 3.22GiB)、モデル設定は 28層、KV ヘッド 8、head_dim 128
共通オプション --max-model-len 4096, --gpu-memory-utilization 0.85, --max-num-seqs 64(比較テストのみ 16)
クライアント サーバーと同じコンテナ内の非同期クライアント、ストリーミングでトークンごとにタイムスタンプを記録し、TTFT(Time to First Token)と ITL(Inter-Token Latency)を計算
入力 リクエストごとに異なるランダムな単語列(プレフィックスキャッシュが偶然ヒットしないように)、出力 128 トークン(ignore_eos)、温度 0
繰り返し 同時実行数 c に対して 3c 個のリクエスト(長い入力のテストは 2c 個)、最初の 2つのリクエストはウォームアップとして除外

サーバーはテストごとに新しく起動しました(KV キャッシュ容量が初回の起動と再起動で異なるため、セクション 3 参照)。表の値は 1回のテストの束から読み取ったものであり、繰り返しの変動は別途測定しました。 同じサーバーで同じテストを 5回繰り返した場合(同時実行数 32、入力 512、出力 128)、スループットは 831、833、831、832、831 tok/s、TTFT の中央値は 666〜670ms、TTFT の p95 は 1,606〜1,608ms、ITL の p99 は 223〜224ms であり、 0.5% 以内でした。サーバーを 3回再起動しても、同時実行数 1 は 66 tok/s、同時実行数 32 は 831〜832 tok/s と同じでした。プレフィックスキャッシュ(3回)や割り込み(3回、最大 ITL 227、226、226ms)も同様でした。この環境(GPU 1枚、他の作業なし)ではテスト自体の変動は小さいという意味であり、異なるハードウェアや別の隣接プロセスがある環境に移行すると変化する可能性があります。

3. KV キャッシュ容量は計算式と一致する

トークンあたりの KV サイズは 2 × 층 수 × KV 헤드 수 × head_dim × 자료형 바이트です。このモデルは 28層、KV ヘッド 8個、head_dim 128 であり、KV を bf16(2バイト)として保持するため、以下のようになります(計算)。

2 × 28 × 8 × 128 × 2 = 114,688 바이트/토큰 (112KiB)
設定 サーバーログの KV キャッシュメモリ サーバーログのトークン数 トークンあたりのバイト数(計算) 計算式の値
bf16 KV(デフォルト) 2.36GiB 22,048 約 114,900 114,688
fp8 KV(--kv-cache-dtype fp8) 2.24GiB 41,920 約 57,400 57,344

4. 同時リクエストを増やすと: スループットは鈍化し、KV 容量の限界でキューが爆発する

入力 512、出力 128、 --max-num-seqs 64:

同時リクエスト スループット (tok/s) TTFT 中央値 TTFT p95 ITL 中央値 ITL p99
1 66 73ms 73ms 15ms 16ms
8 383 351ms 474ms 17ms 19ms
32 831 666ms 1.6s 24ms 223ms
64 787 5.7s 6.7s 25ms 228ms

入力を 2,048 トークンに増やすと、1つのリクエストが 2,176 トークンとなるため、キャッシュが保持できるリクエスト数は約 10個になります(計算)。

同時リクエスト スループット (tok/s) TTFT 中央値 TTFT p95
4 151 0.73s 1.0s
16 207 4.0s 6.7s
32 213 12.5s 16.7s

同時実行数を 4個から 16個へと 4倍に増やしたにもかかわらず、スループットは 37% しか増加せず、TTFT は 5.4倍になりました。長い入力においては、 スループットが低下し始める同時実行数が大幅に低くなります。 運用において同時実行数の上限を「GPU が耐えられる限界まで」ではなく、「平均入力長に対して KV が保持できるリクエスト数」に基づいて設定すべき理由がここにあります。

KV が不足する場合: 待機するだけか、それともプリエムプティブ(強制排除)されるか

サーバーのメトリクス(vllm:num_preemptions_total)からプリエムプティブ(優先度による強制排除)の発生回数を読み取りました。入力 512、同時実行数 32 のテストを 5回繰り返した間(KV が保持するリクエスト数が約 34個以内)、プリエムプティブの発生は 0件でした。入力 2,048、同時実行数 32(リクエスト 64個)を送信すると 7件となり、続いて入力 1,500、出力 256、同時実行数 64(リクエスト 128個)を送信すると累計 29件(この負荷では 22件)となりました。このときの TTFT 中央値はそれぞれ 12.5秒、35.4秒でした。KV が不足すると、リクエストがキューで待機するにとどまらず、 実行中だったリクエストが KV キャッシュを手放して強制排除され、最初から再度処理されることになります(プリエムプティブ処理は一時中断したリクエストを再計算するため、入力処理がもう一度発生します。ドキュメント準拠)。プリエムプティブの発生回数が 0ではない区間は、スループットが増加せずレイテンシが急増する区間であるため、アラートの指標として活用する価値があります。

最大同時シーケンス数(--max-num-seqs)はキューの位置を変えるだけである

同時実行数 64、入力 512:

設定 スループット TTFT 中央値
--max-num-seqs 64 787 tok/s 5.7s(初回起動サーバー、上記の繰り返しテストにおける値は 842 tok/s、2.1〜2.2s)
--max-num-seqs 16 608 tok/s 10.3s

上限を16に下げたところ、スループットが23%低下し、TTFTが1.8倍になりました。上限が容量(約34個)より低いと、容量が余っているにもかかわらず待ち行列が発生するため損であり、容量より高くてもKVが不足していれば利得はありません。 上限はKV容量が収容するリクエスト数付近に合わせる値です。

5. KV 8ビット:容量が増えると長い入力の遅延が減る

テスト bf16 KV fp8 KV
同時1、入力512(スループット、ITL) 66 tok/s, 15ms 66 tok/s, 15ms
同時16、入力2048(スループット) 207 tok/s 303 tok/s (+46%)
同時16、入力2048(TTFT中央値) 4.0s 0.94s (−76%)
同時32、入力2048(スループット) 213 tok/s 299 tok/s (+40%)
同時32、入力2048(TTFT中央値) 12.5s 7.0s (−44%)
KV精度 アテンションバックエンド パープレキシティ bf16比
bf16 自動選択 (FLASH_ATTN) 17.7775 基準
bf16 FLASH_ATTN(固定、2回繰り返し) 17.7796, 17.7796 +0.01%
bf16 FLASHINFER 17.7750 -0.01%
bf16 TRITON_ATTN 17.7732 -0.02%
fp8 FLASHINFER 17.4228 -2.0%
fp8 TRITON_ATTN 17.0340 -4.2%

6. プレフィックスキャッシュ:共通のプレフィックスがあると最初のトークンが14倍速くなる

3,000トークンの共通プレフィックスに、リクエストごとに異なる32トークンのテールを付与して1つずつ順番に送信しました(出力1トークン)。

設定 初回リクエストTTFT 以降のリクエストTTFT中央値 プレフィックスが全くない3,032トークン
プレフィックスキャッシュ有効(デフォルト) 394ms 28ms 395ms
--no-enable-prefix-caching 395ms 393ms 393ms

7. チャンクプリフィル:長い入力がデコーディングを一時停止する時間はチャンクサイズで決まる

デコーディング中のリクエスト1つ(300トークン)が実行されている間に、3,500トークンの入力リクエスト3つを割り込ませ、割り込ませた区間のトークン間隔を測定しました。

--max-num-batched-tokens 割り込み区間ITL最大値 割り込み区間ITL中央値 長い入力自体のTTFT
512 69ms 53ms 481〜494ms
デフォルト(確認された値2,048) 227ms(3回繰り返し:227、226、226) 14ms 466〜480ms
8192(入力が3,500トークンのため実効チャンクは3,500) 445ms 14ms 461〜474ms

8. CUDAグラフを無効にすると(--enforce-eager)

設定 サーバー準備時間 同時1スループット 同時32スループット ITL中央値(同時1)
デフォルト(CUDAグラフあり) 71秒(初回起動、コンパイル19秒含む)、以降の再起動は35〜55秒 66 tok/s 831 tok/s 15ms
--enforce-eager 30秒 64 tok/s 814 tok/s 15ms

このサイズのモデルでは、CUDAグラフが速度にもたらす利得は約3%と小さく(同時1で66対64)、サーバーの準備は約25〜40秒早くなりました。トークンあたり14〜15msかかるモデルでは、カーネルの起動間隔が全体に占める割合が小さいためだと推測されます。より小さなモデルでは利得が大きくなると記載しましたが、Qwen3-0.6Bで同じテストを3回繰り返したところ、実際にその通りになりました。同時1においてCUDAグラフのデフォルトが168 tok/s(ITL 6ms)に対し、 --enforce-eager は108 tok/s(ITL 9ms)で、 グラフ有効時の方が55%速く, 同時32では1,557対1,503 tok/sと約3.6%でした。サーバー準備は55秒対30秒でした。モデルが小さくトークンあたりの時間が6msであれば、カーネルを起動する間隔が全体の大きな割合を占めるためだと推測されます。より高速なGPUはテストできませんでした(未測定)。さらにモデルを大きくしたQwen3-4B-FP8では、同時1においてグラフのデフォルトが49 tok/s対 --enforce-eager 47 tok/sと、利得は約4%でした(ITL 19ms対20ms)。グラフによる利得は1トークンが短い非常に小さなモデルでのみ大きく、1.7B以上では3〜4%と同程度でした。

9. サーバーの初回起動にかかる時間

サーバーログのタイムスタンプから読み取った値です。

段階 値
プロセスの起動とモジュールの読み込み(エンジン初期化開始まで) 約15〜17秒
重みのロード(ディスクからGPUへ) 4〜6秒(モデルファイルがすでに存在する場合)
エンジンの初期化(コンパイル、プロファイリング、KVキャッシュの割り当て、CUDAグラフのキャプチャ、ウォームアップ) 初回起動29秒(コンパイル19秒含む)、コンパイルキャッシュがある場合は7.5秒
準備完了までの合計 初回起動71秒、再起動35〜55秒(LoRAを有効にしたサーバーは70秒)

モデルのダウンロード時間は上記から除外しています(別途)。オートスケーリングにおいて新しいポッドがトラフィックを受信するまでの時間は、この数値にイメージの取得とモデルのダウンロードが加算されるため、 事前温めされたレプリカと モデルキャッシュボリュームの必要性が数値として表れます。

10. 運用において採用すべき事項

  1. 同時実行数の上限 = KV容量 ÷ リクエストあたりのトークン数。 式は第3節、根拠は第4節にあります。上限を超えるとスループットは増加せず、TTFTのみが増加します。
  2. TTFT p95とITL p99を個別に確認する。 中央値が正常でも、テールが大きい(ITL p99 223ms)。
  3. 共通のプレフィックスを設計によって作成する。 システムプロンプトとドキュメントを先頭に置き、リクエスト別の内容を後ろに配置すると、プレフィックスキャッシュが14倍の利得をもたらします。
  4. KV 8ビットはスループットのオプションではなく、容量のオプションである。 長い入力において待ち行列が長い場合に効果が大きい。品質評価を先に行う。
  5. チャンクサイズは、途切れにくさと入力処理速度のトレードオフである。 対話サービスには小さく、バッチ処理には大きくする。
  6. パフォーマンス測定は毎回異なる入力を作成し、ハードウェアの限界と比較する。

11. 面接で出題されそうな質問

外部的根拠:論文と公式資料で確認した内容

この記事の数値はすべて実測値です。同じ現象を報告している外部資料を参照して数値と条件を確認し、私たちの結果との関係を記載しました。外部の数値はモデル、GPU、負荷が異なるため、 直接比較せず、傾向と構造の一致のみを判定しました。

主題 資料と確認した箇所 確認した内容と条件 この記事との関係
KVサイズの数式 Kwon他, PagedAttention, SOSP 2023, arXiv 2309.06180, セクション3および表1 OPT-13BのトークンあたりKVは 2(キー・バリュー) × 5,120(隠れ層サイズ) × 40(層) × 2(バイト) = 800KB。A100 40GBから重み26GBを差し引き、12GBをKVに使用すると15.7Kトークンとなり、12GiB ÷ 800KiB の計算結果と一致 支持。 この記事の数式(セクション3)と同じ式
既存システムのKVの無駄 同論文 セクション1および図2 実際のトークン状態で使用されるKVの割合は20.4〜38.2%。著者らが独自に実装したOrcaの3つのバリエーションの平均であり、FasterTransformerは含まれない 関連。16トークン単位でのブロック管理(この記事で確認したデフォルト値)がこの無駄を削減する方法
チャンクサイズとトークン間の遅延 Agrawal他, Sarathi-Serve, OSDI 2024, arXiv 2403.02310, セクション4.3および図6、セクション5.4.1 チャンク(トークン予算)が小さいほど反復時間が短くなり、トークン間遅延が減少する。演算限界領域において反復時間はトークン数に線形比例する。チャンク512では全体の入力処理時間が最大約25%増加し(Yi-34B、テンソル並列2)、2,048ではほぼ無視できるレベル 支持。 セクション7の傾向と一致。私たちのTTFT増加(3〜5%)はモデルやGPUが異なるためサイズの比較は不可
同時性とスループットの傾向 同論文 セクション3.1および図3(Mistral-7B、A100 1基、入力1,024) バッチ処理によりデコードのスループットがほぼ線形に向上し、入力処理にはほとんど影響がない 限定的に支持。 21本の短いコンテキスト(同時実行16まで14〜15倍)と同じ傾向。セクション4の入力512の表はより早い段階で頭打ちになる
容量を超過した際の遅延の急増 PagedAttention セクション6.2 リクエスト率がシステムの容量を超えると、キューが果てしなく長くなり遅延が急増する 支持。 セクション4のTTFT 8.6倍と同じ現象
容量に達する前にスループットが頭打ちになるケース Recasens他, 「Mind the Memory Gap」, arXiv 2503.08311, セクション3〜5 H100上のOPT-2.7B等において、大きなバッチでもメモリの限界に留まり、バッチ1の 225 tok/s がバッチ256で 7,607 tok/s(約33.8倍であり、期待された256倍ではない)にとどまる 限定的に支持。 セクション4の頭打ちがKV容量だけの問題ではない可能性を示す根拠
KV 8ビットの品質 vLLM公式ブログ 「The State of FP8 KV-Cache and Attention Quantization in vLLM」(2026-04-22) 補正を行わないFP8(スケール1.0)における推論タスクの精度低下は、Qwen3-30B-A3B-Thinking-2507で最大1〜2ポイント、Qwen3.5-27Bで最大0.7ポイント(H100) 支持。 文献上の変化は小さいが、モデルと補正に依存する。私たちのモデル(1.7B)と評価についてはセクション5を参照
KV 8ビット(整数) LMDeployドキュメント(INT8 KV, OpenCompass) llama2-7b-chatのMMLUが35.64から35.58へ、internlm2.5-chat-7bのMMLUが72.30から72.27へ変化。FP8ではなくINT8であるためそのまま適用することはできない 関連。vLLMの --kv-cache-dtype fp8 とは異なる方式
プリemption(先取り)方式 vLLM公式ドキュメント(最適化ページ)、v0.30.0 scheduler.py、PagedAttention セクション4.5および7.3 V1におけるプリemptionはスワップではなく再計算が基本であり、犠牲となるリクエストのブロックをすべて返却し、計算されたトークン数を0に戻してキューの先頭に戻す。論文では小さなブロックにおいて再計算が効率的であると説明されている 支持。 セクション4のプリemptionに関する記述と一致(プリemption後のプレフィックスキャッシュヒットは未確認)
デフォルト値 v0.30.0 config/cache.py プレフィックスキャッシュ有効(デフォルト)、ブロックサイズ16 支持。 0.31.0で直接確認した値(有効、16、チャンク予算2,048)と一致

ベンダーおよびプロジェクトのブログ(vLLM、LMDeploy)の数値は、条件が本文に記載されているもののみを転記しました。ACMのPagedAttentionページにアクセスできず、arXivの抽象ページの表記に従いました。

確認できなかった事項

確認したバージョンと日付

2026-10-07基準。vLLM 0.31.0, PyTorch 2.13 (CUDA 13), Qwen3-1.7B (bf16)。

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます