vLLM のルーティングと KV キャッシュに関する解説
vLLM の実環境における混合トラフィック評価により、単一グローバルプールの非効率性が明らかになり、クラス別ルーティングとリソース分離が最適なデプロイ戦略として提唱された。
キーポイント
混合トラフィックにおける単一プールの限界
インタラクティブなチャットからバッチ処理まで多様な負荷を一つの vLLM プールで扱うと、TTFT(First Token Latency)やスロークライアントの隔離において性能が著しく低下することが実証された。
クラス別ルーティングの最適解
'vllm-v1/class-aware-router' が最良の結果を示し、インタラクティブ、RAG、長文コンテキスト、ツールループなどのリクエストクラスを分離・優先処理することで、総合的なトレードオフが最大化された。
実装パラメータの調整指針
インタラクティブトラフィックには小さな max_num_batched_tokens を、長文コンテキストやバッチ処理には大きなトークン予算を持つ別プールを割り当てる「レーンの分割」が推奨された。
ハイブリッド KV とビルド環境の知見
PagedAttention の再設計(ハイブリッド KV)による論理スパンの効率化と、Fedora 44/GCC 16 環境でのビルド成功事例、および tcmalloc の導入によるランタイム性能向上が報告された。
混合トラフィックへの最適化戦略
単一のグローバルプールは混合された負荷に不適切であり、インタラクティブなトラフィックと長文コンテキスト/バッチ処理を別々のプールに分割し、クラス認識型ルーティング(class-aware routing)を採用することが推奨されます。
チャンクドプレフィルの制御
短プロンプトがスケジューラに入場できるよう、max_long_partial_prefills を max_num_partial_prefills よりも低い値に設定し、トークン予算やストリーム間隔をワークロード制御パラメータとして扱う必要があります。
ハイブリッド KV の正則性検証
PagedAttention 再実装パスにおける新しいレイアウト(virtual-contiguous や hybrid-prefix-shared)は、30/30の複雑なケースで正しく動作し、ハードウェアでのプロファイリング対象として最有力候補となりました。
影響分析・編集コメントを表示
影響分析
この分析は、LLM サービングの現場において「万能な設定」が存在しないことを明確にし、運用担当者がワークロード特性に基づいたインフラ設計(クラス別ルーティングやプール分離)を行うよう促す重要な指針となる。特に vLLM のような高速推論フレームワークが本番環境で混合トラフィックに直面した際の具体的なチューニング手法を提供することで、システム全体の安定性とスループット向上に直結する実用的な知見である。
編集コメント
単なるベンチマーク数値の羅列ではなく、実際の運用現場で遭遇する「混合トラフィック」の問題を解決するための具体的なアーキテクチャ変更案(クラス別ルーティング)が示されており、インフラエンジニアにとって即戦力となる内容です。
vLLM ルーティングと KV
vLLM の実世界ラボでは、単一の処理能力数値ではなく、混合された本番トラフィックをモデルに流しました。FCK がリクエストのミックスを生成し、スケジューラとルーティングプロファイルを実行してビルド結果をキャプチャし、以下のグラフで使用される証拠を出力しました。このラボでは 6 つのリクエストクラスをカバーしています:インタラクティブなチャット、繰り返しプレフィックスを持つ RAG(Retrieval-Augmented Generation)、長いプリフィルリクエスト、エージェントのツールループ、バッチ要約、そして低速ストリーミングクライアントです。
アダプターマトリックスでは、vLLM V1、SGLang、llama.cpp、TGI を、同じ OpenAI 互換契約を通じて比較します。サービングスウィープは、オペレーターが実際に調整するプロファイル、すなわち 1 つのバランス型プール、大トークン数プール、小規模インタラクティブプール、プレフィックスキャッシュルーティング、低速クライアントの分離、クラス認識型ルーティングを網羅しています。
主な結果:混合トラフィックに対して 1 つのグローバル vLLM プールは不適切なデフォルトです。 最良のプロファイルは vllm-v1/class-aware-router で、最初のトークンレイテンシ、トークンの間隔、低速リーダーの分離、そして有用なスループットにわたる最も強力な総合的なトレードオフを提供しました。
実践的なアドバイスとしては、カーネルを変更する前にレーンを分割することです。インタラクティブトラフィックは、中程度の max_num_seqs でより小さい max_num_batched_tokens バジェットに維持し、長いコンテキストとバッチ処理のワークロードは、より大きなトークンバジェットを持つ別のプールへ移動させます。
チャンク事前処理も同様の扱いが必要です。max_long_partial_prefills を max_num_partial_prefills より下に保ち、長いプロンプトが処理されている間も短いプロンプトがスケジューラに入場できるようにします。トークン予算、シーケンス数、部分事前処理制限、ストリーム間隔をワークロード制御として扱います。
スケーリング実行によりルーティング結果がより明確になりました。単一の共有プールは負荷が大きくなると TTFT/ITL の閾値を満たせず失敗しましたが、クラス認識型ルーティングはほぼすべてのリクエストを受け入れました。プレフィックスキャッシュ専用および低速クライアント専用のプロファイルはそれぞれのレーンでは効果を発揮しましたが、システム全体としての戦略としては機能しませんでした。
ソースビルド実行により有用なデプロイメントチェックが追加されました。Debian ではより大きな rootfs が必要となり、その後 GCC のバージョン制限に抵触しました。Fedora 44 (GCC 16, Python 3.14) で VLLM_TARGET_DEVICE=cpu、MAX_JOBS=4、numactl-devel を指定してビルドすると、拡張モジュールは正常に構築されました。また、本ビルドではデプロイメント時の注意点を浮き彫りにしました:より良いランタイムパフォーマンスのために tcmalloc をインストールする必要があります。
ハイブリッド KV ラボは PagedAttention の書き換えパスをカバーしています。vLLM のブロック所有権、プレフィックス共有、参照カウント、部分ブロック、_eviction_(淘汰)、コピーオンライトのセマンティクスを維持しつつ、長いトークンごとのブロックテーブルの代わりに、将来のカーネルに対してコンパクトな論理スパンを公開します。
正しさのオラクルは、奇数長の長さ、MQA/GQA、ALiBI、スライディングウィンドウ、プレフィックス再利用、コピーオンライト、部分的な最終ブロック、FP8 形式のスケーリングなどに対して、各レイアウトを単純なリファレンスと比較します。最初のランでは 30/30 のケースで合格し、仮想連続型とハイブリッドプレフィックス共有型がハードウェア上でプロファイルする最初のレイアウトとしてランク付けされました。
プロファイルストレステスト結果
60 万リクエストストレステスト結果
120k リクエスト/プロファイル、30 分間のシミュレーションされた生産環境ウィンドウ
class-aware-router: 66.48 good rps, 99.72% 受理 | p99 TTFT 387.94 ms | p99 queue 310.07 ms
prefix-cache-rag: 26.89 good rps, 40.34% 受理 | p99 TTFT 1766.03 ms | p99 queue 1475.72 ms
slow-client-isolation: 7.04 good rps, 10.56% 受理 | p99 TTFT 2293.08 ms | p99 queue 2001.69 ms
global-large-token-budget: 4.31 good rps, 6.46% 受理 | p99 TTFT 1071.02 ms | p99 queue 888.14 ms
single-pool-balanced: 0.00 good rps, 0.00% 受理 | p99 TTFT 2455.03 ms | p99 queue 2203.19 ms
プロファイルマトリックス
| Profile | Goodput | TTFT | ITL |
|---|---|---|---|
| class-aware-router | 66.48 | 387.94 | 47.33 |
| prefix-cache-rag | 26.89 | 1766.03 | 105.07 |
| slow-client-isolation | 7.04 | 2293.08 | 85.29 |
| global-large-token-budget | 4.31 | 1071.02 | 97.34 |
| single-pool-balanced | 0.00 | 2455.03 | 120.47 |
チューニング読み込み
- グローバルプール 1 つを混合トラフィックの安全なプロダクションデフォルトとするのは危険です。60 万リクエストという負荷テスト下では、ゲートされた有効スループットはゼロでした。
- より大きなグローバルトークン予算はルーティング機能を持ちません。単一プールの制御には改善をもたらしましたが、p99 TTFT が 1 秒を超えたため依然としてゲートを通過できませんでした。
- プレフィックスキャッシュによるルーティングは価値がありますが不十分です。RAG およびエージェントツールループのリクエストを保護する一方で、通常のインタラクティブ、バッチ、ロングプレフィル、低速クライアントのクラスが飢餓状態に陥りました。
- 低速クライアントの分離は「スローリーダーペナルティ」を解消しましたが、ロングプレフィルによるキューイングの問題は解決していません。これはより広範なルーティング機能内のエッジ/プロキシレーンとして使用すべきです。
- 勝利する形状はクラス認識型です:短いインタラクティブ、プレフィックス重視、ロングプレフィル、バッチ、低速ストリームの各レーンを設け、それぞれに異なるトークン予算とシーケンス制限を割り当てます。
Source Build Lab
Build Outcomes
バー長 = ビルドコマンドの壁面時間; 失敗したセットアップケースは診断実行として表示されます
Debian 12 デフォルトのルートファイルシステム 1.5 GiB カタログ ルートファイルシステム、torch-2.11.0+cpu.setup の使用量が 150.5/190.2 MB で尽きる
Debian 12 + ディスク 12 GiB CMake が x86 バックエンドに到達し、GCC/G++ 12.2.3.00 を拒否。00 秒
Fedora 44、NUMA ヘッダーなし csrc/cpu/utils.cpp で numactl-devel が必要になる前に 428 ターゲットをコンパイル。712.00 秒
Fedora 44 + numactl-devel MAX_JOBS=4 で _C、_C_AVX512、および_C_AVX2 の共有モジュールをビルド。853.00 秒
Fedora CUDA ネガティブコントロール VLLM_TARGET_DEVICE=cuda を設定したが、CUDA ランタイムメタデータなしでは不明なランタイム環境で失敗。1.26 秒
Fedora ソースビルドレシピ
これがラボでのパスした経路です: Fedora 44、GCC 16、Python 3.14、12 GiB ルートファイルシステム、numactl-devel、および並列処理の制限。
dnf install -y git gcc gcc-c++ cmake ninja-build python3.14 python3.14-devel numactl-devel
git clone https://github.com/vllm-project/vllm.git
cd vllm
git checkout 1c607d7b2cd4fb572b919c6053f19d0577203495
python3.14 -m venv .venv
. .venv/bin/activate
python -m pip install -U pip setuptools wheel
VLLM_TARGET_DEVICE=cpu MAX_JOBS=4 python -m pip install -v -e .
Debian ソースビルド事前チェック
Debian 12 では、成功したビルドの前に 2 つのチェックが露呈しました: PyTorch ウィンドウ用のディスク容量と GCC/G++ のバージョンです。より大きなルートファイルシステムを使用し、x86 バックエンドのゲート以上(または同等)のコンパイラを用意してください。
apt-get update
apt-get install -y git cmake ninja-build python3 python3-venv python3-dev libnuma-dev
Debian 12 の GCC/G++ 12.2 は、現在の x86 バックエンドのゲートを通過できませんでした。
まずより新しい GCC/G++ ツールチェーンをインストールし、その後 CC/CXX を設定してください。
git clone https://github.com/vllm-project/vllm.git
cd vllm
git checkout 1c607d7b2cd4fb572b919c6053f19d0577203495
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -U pip setuptools wheel
CC=gcc-13 CXX=g++-13 VLLM_TARGET_DEVICE=cpu MAX_JOBS=4 python -m pip install -v -e .
## Hybrid KV Rewrite Lab
Paged Blocks To Virtual Spans
Lower estimated decode-read cost is better; all rows are simulator/reference evidencevirtual-contiguousread amp 1.08 | locality 90.43 | prefix reuse 15.63%2649.08hybrid-prefix-sharedread amp 1.12 | locality 86.41 | prefix reuse 16.99%3173.11hot-tail-contiguousread amp 1.28 | locality 71.89 | prefix reuse 14.27%5493.56packed-block-tableread amp 1.42 | locality 62.02 | prefix reuse 14.27%7215.82classic-pagedread amp 1.70 | locality 46.82 | prefix reuse 14.27%10368.30
Layout Metrics
LayoutRead AmpWasteCost
virtual-contiguous1.081.56%2649.08
hybrid-prefix-shared1.121.58%3173.11
hot-tail-contiguous1.281.49%5493.56
packed-block-table1.421.49%7215.82
classic-paged1.701.49%10368.30
## Allocator Model
- KV メモリは、各リクエストに対して連続した 1 つの割り当てを行うのではなく、固定サイズの物理ブロックに分割されます。
- トークンが同一のプレフィックスを持つリクエストは、同じプレフィックスブロックを指すことができ、参照カウントを増加させることができます。
- 共有シーケンスが分岐した場合、コピーオンライトにより、共有プレフィックスを破損させることなく、分岐したリクエストにプライベートなブロックが割り当てられます。
- ハイブリッドパスでは、これらのアロケーターセマンティクスを維持しつつ、コンパクトな論理スパンを生成することで、カーネルがより長い連続範囲を読み取れるようにします。
Top Profiles
ProfileTTFTITLGoodput
vllm-v1/class-aware-router254.5933.2922.32
sglang/class-aware-router234.2230.6320.98
vllm-v1/prefix-cache-rag359.9445.8020.80
vllm-v1/global-large-token-budget614.2748.4020.61
sglang/prefix-cache-rag331.1442.1319.56
sglang/global-large-token-budget565.1344.5319.38
vllm-v1/single-pool-balanced465.3552.0418.91
vllm-v1/slow-client-isolation353.6734.7818.53
Workload Mix
Bubble size = request share, x = p99 prompt tokens, y = shared prefix rateagent-tool-loopbatch-summaryinteractive-chatlong-prefillrag-shared-prefixslow-stream-client- ## How vLLM Works
非同期エンジン
スケジューラ
KV マネージャ
ブロックプール
PagedAttention 演算
モデルランナー
6 ステップの第 1 ステップ:1. API 受付
1. API 受付
OpenAI 互換サーバーはリクエストを受け取り、サンプリングパラメータを解析し、トークン化またはトークン ID を受け取って、非同期エンジンに処理を引き渡します。
TTFT(First Token Time)の負荷:キューイング、トークン化、長い事前生成(prefill)
ITL(Inter-Token Latency)の負荷:まだアクティブではない
チューニング対象:エンジンに入る前にワークロードクラスに基づいてルーティングを行う
レストラン厨房のアナロジー
リクエストは注文票のように流入します。まだ客には何も見えず、システムが次のステップにどの作業を入力するかを決定している段階です。
平易な英語による対応付け
注文票は、スケジューラを待っているユーザーのリクエストです。
- 下準備(prep work)は事前生成(prefill):回答が表示される前にプロンプトを読み込む作業です。
- 最初に盛り付けられる一品が TTFT:ユーザーが見ることができる最初のトークンです。
- 一品ずつ盛り付けるのがデコード(decode):繰り返しの次トークン生成の作業です。
- 下準備用のテーブルは KV キャッシュ:作業をやり直さないために保存されるコンテキストです。
- 皿を運ぶサーバーはストリーミングクライアントであり、遅いテーブルがサービスのバックアップを引き起こす可能性があります。
交通交差点の視点
各車線に信号機を設けると、長いプリフェッチ処理が交差点を占有し、短いチャットはその後ろで待機することになります。
メモリホテルの地図
リクエストは、プロンプトと生成されたトークンの状態を保持する固定サイズのブロック(ルーム)を取得することで、KV メモリにチェックインします。
トークンタイムラインスクラバー
0 ミリ秒:キュー待ち;TTFT 待機中;ITL は非アクティブ;デコード済みトークン数 0;KV 使用率 18%。
レイテンシの読み取り
- キュー時間は、スケジューラーがリクエストをエンジンステップに受け入れるまでの時間です。
- プリフェッチはプロンプトトークンを処理し、長いコンテキストでは通常 TTFT を支配します。
- 最初のトークンがユーザーが視認する TTFT の境界線となります。
- デコードはアクティブなリクエストをトークン単位で進め、ここで ITL が現れます。
- ストリーミングにより、エンジンが健全であっても視覚的な間隔が引き伸ばされることがあります。
スケジューラー予算エクスプローラー
デコードがステップの大部分を占有します。インタラクティブなトークン予算は小さく予測可能に保ちます。
ハイレベルモデル
- リクエストは OpenAI 互換 API を経由して入力され、エンジンステップを待機します。
- スケジューラーは各ステップのトークン予算を、プレフィルとデコード作業に配分します。
- プレフィルはプロンプト処理であり、デコードが飢餓状態にならないよう、長いプロンプトにはチャンキングが必要です。
- デコードは連続バッチ処理を通じて、多数のアクティブなリクエストを次のトークンへ進めます。
- KV キャッシュブロックがメモリの制約要因となります。プレフィックスの再利用が可能なのは、トークンが一致する場合に限られます。
- ストリーミングにより、エンジンの刻みはクライアント側で TTFT(First Token Time)やトークン間のギャップとして可視化されます。
知見
クラス認識型ルーティングは、混合トラフィックに対する単一のグローバル vLLM プールよりも優れています
最適なプロファイルである vllm-v1/class-aware-router は、ベースラインと比較して p99 TTFT を 45.29%、p99 トークン間ギャップを 36.03% 削減しました。
- カーネルチューニングを行う前に、小トークン予算のインタラクティブ用プールと、長文・バッチ処理用のプールの 2 つを使用してください。
- max_num_batched_tokens はクラス全体での定数ではなく、各レーンの制御パラメータとして扱ってください。
より大きなトークン予算は長文プレフィルの容量を向上させますが、インタラクティブな TTFT には悪影響を及ぼします
グローバルに大規模なトークン予算を設定する戦略は、長いプロンプトの処理を維持しますが、ユーザーが体感するレイテンシにおいてはルーティング済みプロファイルに劣ります。
- max_num_batched_tokens=8192 またはそれ以上の値は、長文コンテキスト用のレーンのみで使用してください。
低速なストリーミングクライアントは、エンジンの刻みから隔離する必要があります
最適なプロファイルは、ベースラインと比較して低速クライアントのペナルティを75.99%削減しました。
- 低速な SSE リーダーを、明示的な書き込み期限を持つプロキシまたはワーカーレーンの背後に配置してください。
- このテストを行わずに、ソケットのバックプレッシャーをスケジューラやカーネルの回帰と解釈してはいけません。
## 大規模プロファイルのストレスは狭い修正策を罰する
60万回のリクエスト全体において、クラス認識型ルーティングが唯一、全体のワークロードを TTFT/ITL のゲート内に維持したプロファイルでした。
- クラス認識型ルーティングを最初のアクセラレータ実験へ昇格させてください。
- プレフィックスキャッシュと低速クライアントの分離は、スタンドアロンのサービング戦略ではなく、ルーター内部のレーンとして維持してください。
## ハイブリッド KV は現在、テスト可能なカーネル書き換え候補である
実験室は、カーネル側のビューを変更しつつも PagedAttention のセマンティクスを保持しました。クラシックなページブロック、パッキングされたテーブル、ホットテール連続割り当て、仮想連続スパン、そしてハイブリッドプレフィックス共有をシミュレーションした後、各レイアウトを同じ参照アテンションパスに対して検証し、仮想連続型とハイブリッドプレフィックス共有型を最上位にランク付けしました。
- KV マネージャー内でブロック所有権、参照カウント、プレフィックス共有、_eviction_(退去)、コピーオンライトを維持する。
- コンパクトなスパンテーブルレコードをカーネルに出力する:論理開始位置、長さ、物理的実行範囲、共有状態、量子化スケールメタデータを含む。
- ハードウェアカーネルを実装する前に、読み込み増幅、メモリ浪費、局所性、推論読み取りコストの見積もりを測定する。
- 速度向上を主張する前に、実際の L2/DRAM カウンター、オキュパンシー(占有率)、CUDA グラフの動作、TTFT(First Token Latency:初回トークン遅延)、ITL(Inter-Token Latency:トークン間遅延)で検証を行う。
GPU デプロイテンプレート
これは FCK がラボで使用しているデプロイ形状です:vLLM は小型ルーターの背後で動作し、チャンク化されたプリフィルが有効になっており、スケジューラの制限は明示的であるため、各サービングレーンを独立して変更・測定できます。
services:
vllm:
image: vllm/vllm-openai:latest
command:
- --model=<your-model>
- --host=0.0.0.0
- --port=8000
- --enable-chunked-prefill
- --max-num-batched-tokens=4096
- --max-num-seqs=128
- --max-num-partial-prefills=2
- --max-long-partial-prefills=1
- --long-prefill-token-threshold=4096
ports: ["8000:8000"]
deploy:
resources:
reservations:
devices:
- capabilities: ["gpu"]
router:
image: nginx:stable
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
ports: ["8080:8080"]
depends_on: [vllm]
リファレンス
原文を表示
vLLM routing and KV
The vLLM real-world lab models mixed production traffic instead of a single throughput number. FCK generated the request mixes, ran the scheduler and routing profiles, captured build outcomes, and emitted the evidence used for the charts below. The lab covers six request classes: interactive chat, RAG with repeated prefixes, long-prefill requests, agent tool loops, batch summarization, and slow streaming clients.
The adapter matrix compares vLLM V1, SGLang, llama.cpp, and TGI through the same OpenAI-compatible contract. The serving sweep covers the profiles operators actually tune: one balanced pool, a large-token pool, a small interactive pool, prefix-cache routing, slow-client isolation, and class-aware routing.
Main result: one global vLLM pool is a poor default for mixed traffic. The best profile was vllm-v1/class-aware-router, which gave the strongest combined tradeoff across first-token latency, token cadence, slow-reader isolation, and useful throughput.
The practical advice is to split lanes before changing kernels. Keep interactive traffic on a smaller max_num_batched_tokens budget with moderate max_num_seqs, then move long-context and batch work to a separate pool with a larger token budget.
Chunked prefill needs the same treatment. Keep max_long_partial_prefills below max_num_partial_prefills so short prompts can still enter the scheduler while long prompts are being processed. Treat token budget, sequence count, partial-prefill limits, and stream interval as workload controls.
The scale run made the routing result clearer. The single shared pool failed the TTFT/ITL gate under the larger workload, while class-aware routing accepted nearly all requests. Prefix-cache-only and slow-client-only profiles helped their own lanes, but neither worked as a whole-system strategy.
The source-build run added useful deployment checks. Debian needed a larger rootfs and then hit a GCC version gate. Fedora 44 with GCC 16, Python 3.14, VLLM_TARGET_DEVICE=cpu, MAX_JOBS=4, and numactl-devel built the extension modules successfully. The build also surfaced a deployment note worth keeping: install tcmalloc for better runtime performance.
The hybrid KV lab covers the PagedAttention rewrite path. It keeps vLLM's block ownership, prefix sharing, refcounts, partial blocks, eviction, and copy-on-write semantics, then exposes compact logical spans to future kernels instead of a long per-token block table.
The correctness oracle compares each layout against a plain reference across odd lengths, MQA/GQA, ALiBI, sliding windows, prefix reuse, copy-on-write, partial final blocks, and FP8-style scaling. The first run passed 30/30 cases and ranked virtual-contiguous and hybrid-prefix-shared as the first layouts to profile on hardware.
Profile Stress Result
600k Request Stress Result
120k requests/profile, 30-minute simulated production windowclass-aware-router66.48 good rpsaccepted 99.72% | p99 TTFT 387.94 ms | p99 queue 310.07 msprefix-cache-rag26.89 good rpsaccepted 40.34% | p99 TTFT 1766.03 ms | p99 queue 1475.72 msslow-client-isolation7.04 good rpsaccepted 10.56% | p99 TTFT 2293.08 ms | p99 queue 2001.69 msglobal-large-token-budget4.31 good rpsaccepted 6.46% | p99 TTFT 1071.02 ms | p99 queue 888.14 mssingle-pool-balanced0.00 good rpsaccepted 0.00% | p99 TTFT 2455.03 ms | p99 queue 2203.19 ms
Profile Matrix
ProfileGoodputTTFTITL
class-aware-router66.48387.9447.33
prefix-cache-rag26.891766.03105.07
slow-client-isolation7.042293.0885.29
global-large-token-budget4.311071.0297.34
single-pool-balanced0.002455.03120.47
Tuning Read
- One global pool is not a safe production default for mixed traffic. Under the 600k-request stress, it produced zero gated goodput.
- A larger global token budget is not a router. It improved the single-pool control but still missed the gate with p99 TTFT over one second.
- Prefix-cache routing is valuable but incomplete. It protected RAG and agent-tool-loop requests while starving ordinary interactive, batch, long-prefill, and slow-client classes.
- Slow-client isolation fixed slow-reader penalty but did not solve long-prefill queueing. Use it as an edge/proxy lane inside a broader router.
- The winning shape is class-aware: short interactive, prefix-heavy, long-prefill, batch, and slow-stream lanes with different token budgets and sequence limits.
Source Build Lab
Build Outcomes
Bar length = build command wall time; failed setup cases are shown as diagnostic runsDebian 12 default rootfs1.5 GiB catalog rootfs exhausted at 150.5/190.2 MB of torch-2.11.0+cpu.setupDebian 12 + diskGiB 12CMake reached the x86 backend and rejected GCC/G++ 12.2.3.00sFedora 44, no NUMA headersCompiled 428 targets before csrc/cpu/utils.cpp needed numactl-devel.712.00sFedora 44 + numactl-develBuilt _C, _C_AVX512, and _C_AVX2 shared modules with MAX_JOBS=4.853.00sFedora CUDA negative controlVLLM_TARGET_DEVICE=cuda without CUDA runtime metadata failed with Unknown runtime environment.1.26s
Fedora source-build recipe
This was the passing path in the lab: Fedora 44, GCC 16, Python 3.14, 12 GiB rootfs, numactl-devel, and limited parallelism.
dnf install -y git gcc gcc-c++ cmake ninja-build python3.14 python3.14-devel numactl-devel
git clone https://github.com/vllm-project/vllm.git
cd vllm
git checkout 1c607d7b2cd4fb572b919c6053f19d0577203495
python3.14 -m venv .venv
. .venv/bin/activate
python -m pip install -U pip setuptools wheel
VLLM_TARGET_DEVICE=cpu MAX_JOBS=4 python -m pip install -v -e .Debian source-build preflight
Debian 12 exposed two checks before a successful build: disk space for the PyTorch wheel and GCC/G++ version. Use a larger rootfs and a compiler at or above the x86 backend gate.
apt-get update
apt-get install -y git cmake ninja-build python3 python3-venv python3-dev libnuma-dev
# Debian 12 GCC/G++ 12.2 failed the current x86 backend gate.
# Install a newer GCC/G++ toolchain first, then set CC/CXX.
git clone https://github.com/vllm-project/vllm.git
cd vllm
git checkout 1c607d7b2cd4fb572b919c6053f19d0577203495
python3 -m venv .venv
. .venv/bin/activate
python -m pip install -U pip setuptools wheel
CC=gcc-13 CXX=g++-13 VLLM_TARGET_DEVICE=cpu MAX_JOBS=4 python -m pip install -v -e .Hybrid KV Rewrite Lab
Paged Blocks To Virtual Spans
Lower estimated decode-read cost is better; all rows are simulator/reference evidencevirtual-contiguousread amp 1.08 | locality 90.43 | prefix reuse 15.63%2649.08hybrid-prefix-sharedread amp 1.12 | locality 86.41 | prefix reuse 16.99%3173.11hot-tail-contiguousread amp 1.28 | locality 71.89 | prefix reuse 14.27%5493.56packed-block-tableread amp 1.42 | locality 62.02 | prefix reuse 14.27%7215.82classic-pagedread amp 1.70 | locality 46.82 | prefix reuse 14.27%10368.30
Layout Metrics
LayoutRead AmpWasteCost
virtual-contiguous1.081.56%2649.08
hybrid-prefix-shared1.121.58%3173.11
hot-tail-contiguous1.281.49%5493.56
packed-block-table1.421.49%7215.82
classic-paged1.701.49%10368.30
Allocator Model
- KV memory is split into fixed-size physical blocks instead of one contiguous allocation per request.
- Requests with token-identical prefixes can point at the same prefix blocks and raise refcounts.
- When a shared sequence diverges, copy-on-write gives the diverging request private blocks without corrupting the shared prefix.
- The hybrid path keeps those allocator semantics but emits compact logical spans so kernels can read longer runs.
Top Profiles
ProfileTTFTITLGoodput
vllm-v1/class-aware-router254.5933.2922.32
sglang/class-aware-router234.2230.6320.98
vllm-v1/prefix-cache-rag359.9445.8020.80
vllm-v1/global-large-token-budget614.2748.4020.61
sglang/prefix-cache-rag331.1442.1319.56
sglang/global-large-token-budget565.1344.5319.38
vllm-v1/single-pool-balanced465.3552.0418.91
vllm-v1/slow-client-isolation353.6734.7818.53
Workload Mix
Bubble size = request share, x = p99 prompt tokens, y = shared prefix rateagent-tool-loopbatch-summaryinteractive-chatlong-prefillrag-shared-prefixslow-stream-client- ## How vLLM Works
async engine
scheduler
KV manager
block pool
PagedAttention op
model runner
Step 1 of 6: 1. API admission
1. API admission
The OpenAI-compatible server accepts a request, parses sampling parameters, tokenizes or receives token IDs, and hands work to the async engine.
TTFT pressurequeueing, tokenization, long prefill
ITL pressurenot active yet
Tuning surfaceroute by workload class before the engine
Restaurant Kitchen Analogy
Requests enter like order tickets. Nothing is visible to the diner yet; the system is deciding what work enters the next step.
Plain-English Mapping
Order tickets are user requests waiting for the scheduler.
- Prep work is prefill: reading the prompt before any answer appears.
- The first plated item is TTFT: the first token the user can see.
- Plating one item at a time is decode: repeated next-token work.
- The prep table is KV cache: stored context that avoids redoing work.
- Servers carrying plates are streaming clients; slow tables can back up service.
Traffic Intersection View
One traffic light for every lane lets a long prefill hold the intersection while short chats queue behind it.
Memory Hotel Map
A request checks into KV memory by taking rooms: fixed-size blocks that hold prompt and generated-token state.
Token Timeline Scrubber
At 0 ms: queue; TTFT waiting; ITL not active; decoded tokens 0; KV 18%.
Latency Read
- Queue is time before the scheduler admits the request into an engine step.
- Prefill processes prompt tokens and usually dominates TTFT for long contexts.
- First token is the user-visible TTFT boundary.
- Decode advances active requests token by token; this is where ITL shows up.
- Streaming can stretch visible cadence even when the engine is healthy.
Scheduler Budget Explorer
Decode owns most of the step. Keep the interactive token budget small and predictable.
High-Level Model
- Requests enter the OpenAI-compatible API and wait for an engine step.
- The scheduler spends each step's token budget on prefill and decode work.
- Prefill is prompt processing; long prompts need chunking so decode is not starved.
- Decode advances many active requests by the next token through continuous batching.
- KV cache blocks are the memory constraint; prefix reuse helps only when tokens match.
- Streaming turns engine cadence into client-visible TTFT and inter-token gaps.
Findings
Class-aware routing beats one global vLLM pool for mixed traffic
Best profile vllm-v1/class-aware-router cut p99 TTFT by 45.29% and p99 inter-token gap by 36.03% versus baseline.
- Use one small-token-budget interactive pool and one long/batch pool before tuning kernels.
- Treat max_num_batched_tokens as a per-lane control, not a cluster-wide constant.
A larger token budget improves long-prefill capacity but hurts interactive TTFT
The global-large-token-budget strategy keeps long prompts moving but is dominated by the routed profile for user-facing latency.
- Use max_num_batched_tokens=8192 or higher only in long-context lanes.
Slow streaming clients should be isolated from engine cadence
The best profile reduced slow-client penalty by 75.99% versus baseline.
- Put slow SSE readers behind a proxy or worker lane with explicit write deadlines.
- Do not interpret socket backpressure as a scheduler or kernel regression without this test.
Large profile stress punishes narrow fixes
Across 600,000 requests, class-aware routing was the only profile that kept the whole workload inside the TTFT/ITL gates.
- Promote class-aware routing to the first accelerator experiment.
- Keep prefix-cache and slow-client isolation as lanes inside the router, not standalone serving strategies.
Hybrid KV is now a testable kernel-rewrite candidate
The lab preserved PagedAttention semantics while changing the kernel-facing view. It simulated classic paged blocks, packed tables, hot-tail contiguous allocation, virtual-contiguous spans, and hybrid prefix sharing, then checked each layout against the same reference attention path before ranking virtual-contiguous and hybrid-prefix-shared first.
- Keep block ownership, refcounts, prefix sharing, eviction, and copy-on-write in the KV manager.
- Emit compact span-table records to kernels: logical start, length, physical run, sharing state, and quantization scale metadata.
- Measure read amplification, memory waste, locality, and estimated decode-read cost before writing a hardware kernel.
- Validate with real L2/DRAM counters, occupancy, CUDA graph behavior, TTFT, and ITL before claiming speed.
GPU Deploy Template
This is the deployment shape used by FCK for the lab: vLLM runs behind a small router, chunked prefill is enabled, and the scheduler limits are explicit so each serving lane can be changed and measured independently.
services:
vllm:
image: vllm/vllm-openai:latest
command:
- --model=<your-model>
- --host=0.0.0.0
- --port=8000
- --enable-chunked-prefill
- --max-num-batched-tokens=4096
- --max-num-seqs=128
- --max-num-partial-prefills=2
- --max-long-partial-prefills=1
- --long-prefill-token-threshold=4096
ports: ["8000:8000"]
deploy:
resources:
reservations:
devices:
- capabilities: ["gpu"]
router:
image: nginx:stable
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
ports: ["8080:8080"]
depends_on: [vllm]References
関連記事
今日のまとめ
AI日報で今日の重要ニュースをまとめ読み