TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

連続バッチ処理、chunked prefill、プレフィックスキャッシュを繰り返し測定で再計測する

ステータス: ドラフト(2026-10-27)、実測値。 8GB級ノートPC用GPU(Ada世代)1基、vLLM 0.31.0、Qwen3-Reranker-0.6B で2026-10-27に計測しました。同じ3機能の原理と1.7Bモデルの初回の計測は「LLM KV Cache Anatomy」の第2部と「Toss ML/AI Infra Preparation」の第14部にあるため、この記事ではそれらの記事で行っていないこと(繰り返し測定、機能をオフにしたベースライン、チャンクの無効化、cache_salt)のみを扱います。 2026-10-27 追加。 同日に vLLM 0.32.0 で同じテストを再実行した結果を6-1節に追加しました。0.31.0の数値はそのまま残してあります。 作成ルール: 実測データを基本とし、測定できなかったものは「未計測」、確認できなかったものは「未確認」と表記します。

目次

1. なぜ再計測するのか

PagedAttention、連続バッチ処理、プレフィックスキャッシュの原理はサービングエンジン内のKVキャッシュで、1.7Bモデルで最初に計測した値は小さなモデルでのvLLM直接計測で取り上げました。その計測には3つの隙がありました。

  1. 条件ごとに1回ずつしか計測せず、ばらつきが分かりませんでした。
  2. 連続バッチ処理はオフにできなかったため、「なければどうなるか」を示せませんでした。
  3. チャンクを完全にオフにした場合と、複数のユーザーがキャッシュを共有する際の分離(cache_salt)を確認していませんでした。

今回はすべての条件を3〜4回繰り返し、最初のバッチはウォームアップとして除外し、中央値から差し引きました。機能をオフにしたサーバーは同じ日の同じポッドで続けて立ち上げました。

2. 環境と方法

3. 連続バッチ処理: オフにするとスループットが1/10になる

連続バッチ処理自体をオフにするオプションはありません。代わりに --max-num-seqs 1 を用いて1ステップに1リクエストのみ処理させることで、「リクエストを1つずつ処理するサーバー」になります。入力256トークン、出力128トークンのリクエストを同時実行数 c で 2c 個ずつ送信しました。

サーバー 同時リクエスト スループット(tok/s) TTFT中央値 ITL中央値 ITL p99
1度に1つ(--max-num-seqs 1) 1 174.4 18.9ms 5.6ms 6.2ms
1度に1つ 16 174.6 11,008ms 5.6ms 6.5ms
デフォルト 1 174.2 18.8ms 5.6ms 6.7ms
デフォルト 4 593.8 50.7ms 6.4ms 7.5ms
デフォルト 16 1,683.1 131.0ms 8.2ms 9.4ms
デフォルト 64 2,899.3 272.2ms 16.6ms 90.6ms

3つのバッチのスループットは互いに0.3%の誤差範囲内で一致していました(同時16デフォルト: 1,681.3、1,683.1、1,685.4)。

4. chunked prefill: デコードが停止する時間はチャンクサイズに比例する

1つのデコードリクエスト(900トークン)が処理されている間に、約3,700トークンの入力を5回割り込ませ、割り込ませた区間におけるデコードトークン間隔の最大値を計測しました。

サーバー 1ステップあたりの入力トークン上限 割り込み区間の最大ITL(5回) その他のITL中央値 割り込ませた入力のTTFT中央値
--max-num-batched-tokens 512 512 35.6ms(35.5, 35.5, 35.6, 35.6, 35.6) 5.7ms 232.0ms
デフォルト 2,048 109.8ms(108.3, 108.6, 109.1, 109.2, 109.8) 5.7ms 216.2ms
--no-enable-chunked-prefill --max-num-batched-tokens 4096 4,096(チャンクなし) 197.6ms(191.8, 195.7, 197.1, 197.4, 197.6) 5.8ms 214.1ms

このGPUにおけるプレフィックス速度は、3,200トークンを174.7msで処理する程度、すなわち秒間約18,000トークンです。2,048トークンのチャンク1つには約111ms、3,700トークン全体には約200msかかるという計算は、表の最大ITLと一致します。5回の測定値が1ms前後の誤差に収まっており、偶然ではありません。

1.7Bモデルで計測した以前の値(512トークン 69ms、2,048トークン 227ms)と傾向は同じで、サイズはモデルの計算量分だけ小さくなりました。

5. プレフィックスキャッシュ: 繰り返しても18.9ms

3,000単語(約3,200トークン)の共通プレフィックスに対し、リクエストごとに異なる32単語を付与して送信しました。プレフィックスを変更して5つのバッチを実行しました。

サーバー コールドな初回のリクエストTTFT(5個のプレフィックス) ウォームなリクエストTTFT(20回)
デフォルト(プレフィックスキャッシュ有効) 中央値 174.7ms(174.6〜175.3) 中央値 18.9ms(最小 18.0、最大 19.9)
--no-enable-prefix-caching 中央値 173.9ms、サーバーの初回のリクエストのみ 236.0ms 中央値 173.8ms(最小 173.0、最大 178.2)

ウォームなリクエストは3,200トークンのうち最後のブロックとテールのみを計算するため、9.2倍高速化されました。プレフィックスキャッシュをオフにしたサーバーの最初の1回目のリクエストが236msとなり、他のコールドなリクエストより60ms遅くなりましたが、これがサーバー起動後のウォームアップ分です。計測において最初の値を切り離さなければならない理由はここにあります。

キャッシュがブロック単位である点も覚えておきましょう。vLLM は埋まったブロック(デフォルト16トークン)のみをハッシュ化して保存するため、共通部分がブロック境界で途切れた場合、最後の断片は再計算されます。ハッシュは0.11からデフォルトが sha256 であり、 --prefix-caching-hash-algo に変更できます。

6. cache_salt: 同じプレフィックスでもテナント間で共有しない

プレフィックスキャッシュにはセキュリティ上の問題が1つあります。他のユーザーが送信した文書と同じ前半部分を送信してみて、最初のトークンが早ければ「誰かがこの文書をすでに送信した」ことがわかります。時間差を利用して情報を盗み出すタイミング攻撃です。vLLM ではリクエストに cache_salt を付与すると、その値を最初のブロックのハッシュに混ぜ、同じソルト同士でのみキャッシュを共有させることができます。

--enable-prompt-tokens-details を用いてレスポンスにキャッシュされたトークン数が含まれるようにし、同じ3,000単語のプレフィックスに対してソルトを変更しながら5つのバッチを送信しました。

1つのバッチは同じプレフィックスに対する5つのリクエストです。ソルト a で2回、ソルト b で2回、ソルトなしで1回送信しました。

順序 cache_salt キャッシュされたトークン(レスポンスの cached_tokens) TTFT中央値(2〜5番目のバッチ)
1 tenant-a 0 173.9ms
2 tenant-a 3,184 または 3,200 17.9ms
3 tenant-b 0 173.7ms
4 tenant-b 3,184 または 3,200 18.3ms
5 なし 0 173.9ms

cached_tokens はサーバーを --enable-prompt-tokens-details で起動しなければレスポンスに表示されません。オフにするとこのフィールドが空になり、キャッシュヒットを時間のみで推測する必要があります。サーバー全体のヒット率は /metrics の vllm:prefix_cache_hits_total と vllm:prefix_cache_queries_total で確認します。

6-1. 2026-10-27 再計測: 0.32.0でも同じか

同じGPU、同じリランカーモデル、同じクライアントを使用して vLLM 0.32.0公式コンテナで再計測しました(「2026-10-27 0.31.0 計測 → 0.32.0 再計測」)。

項目 0.31.0 0.32.0
スループット、同時1 174.2 tok/s 174.2 tok/s
スループット、同時16(デフォルト) 1,683.1 tok/s 1,683.6 tok/s
スループット、同時16(一度に1つ) 174.6 tok/s 174.3 tok/s
スループット、同時64 2,899.3 tok/s 2,899.7 tok/s
3,700トークンが割り込む際のデコード最大停止(デフォルトチャンク) 109.8ms 108.2ms
3,200トークンプレフィックス最初のトークン(コールド → ウォーム) 174.7 → 18.9ms 175.2 → 18.9ms

すべての値が1.5%の範囲内で同じでした。0.32.0リリースノートの大きな変更(Model Runner V2の拡大、カスケードアテンションの削除、 max_tokens をコンテキストから事前に除外しないなど)は、この小規模モデルのデフォルト負荷では数値を変えませんでした。チャット用のQwen3-0.6B(同構造)も同時1・16・64で174.1、1,678.0、2,898.7 tok/s と同じでした。

7. 運用における要点

8. 検証できなかったこと

参考

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

コメント

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

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