
2026/08/07 6:30
vLLM の内側:ハイスループットLLM推論システムの解剖図(2025)
RSS: https://news.ycombinator.com/rss
要約▶
日本語翻訳:
以下の改善された要約は、欠落したコンテキスト、具体的な技術メカニズム(ハッシュベースのキャッシュリングや構造化出力マスキングなど)、およびアーキテクチャ上のニュアンスを明確化することを含みながら、単なる項目リストにならないよう構成されています。
改善された要約:
vLLM のコアシステムに関するシリーズ記事の第 1 部として、この記事では V1 エンジン——廃止対象となっていた V0 の重要な代替物——を紹介します(コードは 2025 年 8 月のコミット 42172ad に基づく)。V1 エンジンは、大規模言語モデルの推論における高スループットを実現することを目的として設計されており、そのアーキテクチャの中心には、メモリ帯域幅に制限されるデコードリクエストを計算資源に制限されたプリフィルタリングタスクよりも優先する高度なスケジューラーがあります。主要な効率化要因としては、カーネル起動オーバーヘッドを削減するための CUDA グラフキャプチャーや、カスタムページ済みアテンションカーネルを用いた継続的なバッチ処理(continuous batching)が含まれます。システムは複数の適応的技術を採用し、パフォーマンスを最適化しています:プロンプトの独占を防ぐためのチャンク付きプリフィルタリング、SHA-256 を含むハッシュベースのブロック再利用を利用するプレフィックスキャッシング、さらに disaggregated プリフィル/デコードインスタンスを活用した疑似推論(speculative decoding)の統合による追加の加速などが挙げられます。
このエンジンは、TPU や MoE 構造など多様なハードウェアおよびモデルアーキテクチャ、さらには Mamba などの新興モデルも支持し、システムの過渡なしにモジュラー性を維持する直交プラグインを通じて対応しています。具体的な実装としては、サービスレベル目標(SLO)の下でスループットを最大化するための自動チューニング機構、
xgrammar バックエンドによる構造化出力デコードのサポート、単一プロセス実行からメッセージキューとヘッドレス API サーバー協調を利用したマルチ GPU スケーリングに至るまで、柔軟なデプロイメントモードが用意されています。これらの進歩は、ブロックサイズ設定や SLO などの特定の制約にも従いながら、より広範な AI アプリケーションに対して生产レベルのパフォーマンスを可能にしています。本文
現代の高スループット LLM 推論システムのコアコンポーネント:vLLM 深度解説(第 1 部)
本記事では、現代の高スループット LLM(大規模言語モデル)推論システムを構成するコアコンポーネントと高度な機能について段階的に紹介します。 逆ピラミッド手法を採用し、概要から詳細へ積み上げることで、最小限の情報でシステム全体に対する正確なメンタルモデルの構築を目指します。
📋 アウトラインと前提条件
記事構成
本シリーズは以下の 5 つのポイント に焦点を当てます。
- LLM エンジン & エンジンコア: vLLM の基本原理(スケジューリング、ページ化された Attention[3]、連続バッチ処理など)
- 高度な機能: チャンク化されたプリフィル、プレフィックスキャッシング、ガイド付きデコーディング、推定デコーディング、非統合化 P/D
- スケーリング: 単一 GPU から複数 GPU/ノードへの実行拡大
- サービングレイヤー: 分散・同時並行的 Web スキャフォールディング
- ベンチマークと自動チューニング: レイテンシとスループットの測定
🔧 注釈と前提条件
- 分析の基準: Commit
(2025 年 8 月 9 日)を基にしています。42172ad - ターゲット読者: SOTA LLM エンジンの動作原理に関心がある人、vLLM や SGLang への貢献を検討している人向けです。
- バージョン: V1 エンジンに焦点を当てます(V0 は非推奨ですが、多くの概念が継承されています)。
- 学習曲線: LLM エンジン&エンジンコアのセクションは少し複雑ですが、後半には豊富なサンプルと視覚資料があります。
1. LLM Engine & Engine Core
LLM エンジンは vLLM の基本的な構成要素です。単体でオフライン環境における高スループット推論が可能ですが、Web 提供には分散システムのスキャフォールディングが必要です。
オフライン推論の基本コード例
以下のコードは
basic.py を元にしたものです。
from vllm import LLM, SamplingParams prompts = [ "Hello, my name is", "The president of the United States is", ] sampling_params = SamplingParams(temperature=0.8, top_p=0.95) def main(): llm = LLM(model="TinyLlama/TinyLlama-1.1B-Chat-v1.0") outputs = llm.generate(prompts, sampling_params) if __name__ == "__main__": main()
⚙️ 環境変数設定
この構成は以下の条件を満たしています:
- オフライン: Web/分散システムなし。
- 同期処理: すべての実行が単一のブロックプロセス内。
- 単一 GPU: データ/モデル/パイプライン/エキスパート並列化なし(DP/TP/PP/EP = 1)。
VLLM_USE_V1="1" # エンジン V1 を使用 VLLM_ENABLE_V1_MULTIPROCESSING="0" # 単一プロセスで実行
コンストラクタの主要コンポーネント
LLM クラスの作成時に以下が発生します:
- vLLM Config: モデル、キャッシュ、並列化などの設定ノブを保持。
- Processor: 生入力 →
に変換(バリデーション、トークナイゼーション、処理)。EngineCoreRequest - Engine Core Client: 実装例では
を使用(InprocClient
がスケール時に使用される)。DPLBAsyncMPClient - Output Processor: 生出力 → ユーザー向け出力に整形。
エンジンコア (Engine Core) の内部構造
エンジンコアは以下のサブコンポーネントから成り立ちます:
- モデルエグゼキューター: モデルの Forward Pass を駆動(
→ 将来的にUniProcExecutor
)。MultiProcExecutor - 構造化出力マネージャー: ガイドされたデコーディングで使用。
- スケジューラー: 次のステップへのリクエスト決定。
- ポリシー: FCFS または優先度ベース。
- キュー: 待機キュー、実行キュー。
- KV キャッシュマネージャー: ページ化された Attention の心臓部。
: 利用可能な KV キャッシュブロックのプール(双方向リスト)。free_block_queue
🧮 ブロックサイズと計算
非 MLA の場合、ブロックサイズは以下で計算されます: $$2 (\text{Key/Value}) \times \text{block_size} (\text{デフォルト}=16) \times \text{num_kv_heads} \times \text{head_size} \times \text{dtype_num_bytes} (\text{bf16 で 2})$$
初期化フロー (Model Executor 構築時)
Worker オブジェクトが作成される際に行う主要な手順:
- Init Device: GPU 割り当て、dtype チェック、VRAM 検証、分散設定セットアップ。
- Instantiate Model Runner: サンプラー、KV キャッシュ、Forward Pass バッファの準備。
- InputBatch のインスタンス化: CPU 側バッファ、ブロックテーブル、サンプリングメタデータの準備。
🧠 モデル読み込みと KV キャッシュ初期化
- モデルロード: アーキテクチャ作成 →ウェイト読み込み→
→ オプションのmodel.eval()
。torch.compile - KV キャッシュ設定:
- 仕様決定: ハイブリッドモデル(例:Jamba)の場合は
を使用。Jenga - ダミーパス: VRAM に収まるブロック数を計算するためのグリッドサイズ確認。
- 割り当てとバインド: テンソルをアタッチ、FlashAttention バックエンド設定。
- CUDA Graphs: 早期実行(ウォーミングアップ)でグラフをキャプチャし、ケネル起動オーバーヘッドを削減。
- 仕様決定: ハイブリッドモデル(例:Jamba)の場合は
2. Generate Function (生成関数)
エンジンが初期化されると、
generate 関数が各プロンプトに対して以下の手順を実行します。
リクエスト処理フロー
- 入力準備: プロンプトトークナイゼーション →
にパック(優先度、サンプリングパラメータなど)。EngineCoreRequest - キューイング: スケジューラーの待機キューに追加(FCFS は
、優先度はヒーププッシュ)。append
同期 vs 非同期処理
- 同期エンジン: 初期プロンプトのみ処理し、実行中に新しいリクエストを注入しません。
- 非同期エンジン (Continuous Batching): 各ステップ後に、新旧両方のリクエストを考慮します。標準的な Transformer レイヤーでも本質的に連続バッチ処理をサポートしています。
ステップ実行サイクル
エンジンが
step() を反復呼び出します。各ステップは以下の 3 ステージから構成されます:
- Schedule: 実行するリクエストを選択(デコードおよび/またはチャンク化されたプリフィル)。
- Forward Pass: モデル実行とトークンサンプリング。
- Postprocess: 結果の整理、停止条件確認、KV キャッシュブロックの解放 (
へ返却)。free_block_queue
🛑 停止条件 (Stop Conditions)
- 長制限:
またはリクエスト固有のmax_model_length
を超えた場合。max_tokens - EOS ID: サンプルされたトークンがエンド・オブ・シーケンス ID(ただし
の場合は別)。ignore_eos - 停止トークン:
と一致するトークンが発生した場合。stop_token_ids - 停止文字列: 出力に指定された停止文字列が含まれた場合(その時点で要求を中止)。
3. スケジューラー & 実行フローの詳細
ワークロードの分類
推論エンジンが処理する主要なワークロード:
- プリフィル (Prefill): プロンプト全長の Forward Pass。計算量制限。
- デコード (Decode): 最新トークンへのみの Forward Pass。メモリ帯域幅制限(KV キャッシュ全体の読み込みが必要)。
vLLM V1 の革新: V0 はプリフィルとデコードを別々に処理しましたが、V1 スケジューラーは両方のタイプを混合して処理できます。
- 優先順位: 実行キューにあるデコード要求を優先。
- プリフィル処理: 待機キューからポップし、実行キューへ移動(ステータス
→WAITING
)。RUNNING
allocate_slots の動作
新しい KV キャッシュブロックの数を計算します。
- 例:新規トークン数 17 トークンの場合、$ \lceil 17/16 \rceil = 2 $ ブロックが必要です。
- 割り当て:
からブロックを取得し、free_block_queue
マップに格納。req_to_blocks
4. 前方パス & Continuous Batching / Paged Attention
model_executor.execute_model を呼び出し、Forward Pass を実行します。
処理手順
- 状態更新: 完了した要求のバッファ剪定、メタデータ更新。
- 入力準備: CPU→GPU コピー、位置計算、スロットマッピング。
- 前方パス実行: カスタムページ化 Attention ケネル使用。全シーケンスを単一の「スーパーシーケンス」に連結(位置インデックスにより相互干渉を防ぐ)。
- ラストトークン抽出: 最終位置の隠れ状態から logits を計算。
- サンプリング: 設定(greedy, temperature など)に従ってトークンを決定。
実行モード
- Eager Mode:
の場合、標準 PyTorch フロー。enforce-eager=True - Captured Mode: 事前にキャプチャされた CUDA グラフを再実行(オーバーヘッド削減)。
5. 高度な機能 (Advanced Features)
🧩 チャンク化されたプリフィル (Chunked Prefill)
長時間の プロンプトを小チャンクに分割し、単一ステップでの計算負荷を分散します。
- 目的: 長いプロンプトによるエンジン占有時間の短縮。
- 実装:
を設定することで有効化(例:8 トークン以上を チャンク)。long_prefill_token_threshold
🗄️ プレフィックスキャッシング (Prefix Caching)
複数のプロンプトで共有される先頭部分の再計算を回避します。
- 仕組み: ハッシュベースのキャッシュ(SHA-256 など)を使用。
と紐付け、既存の KV ブロックを直接再利用。BlockHash - メリット: プリフィル要求が大幅に加速(デコードには役立たないため)。
🧠 ガイドされたデコーディング (Guided Decoding / FSM)
文法規則に基づいて出力を制限します(例:JSON 形式、特定の選択肢のみ)。
- 仕組み:
がStructuredOutputManager
を維持し、禁止された logit を -∞ に設定。_grammar_bitmask
🚀 推定デコーディング (Speculative Decoding)
小さなドラフト LLM が複数のトークンを提案し、大きな LLM で一度に検証することで高速化します。
- vLLM V1 の対応: ドラフトモデル直接使用ではなく、n-gram, EAGLE, Medusa などの軽量スキームを実装。
📡 非統合化された P/D (Disaggregated P/D)
計算量制限(プリフィル)とメモリ帯域幅制限(デコード)を分離し、レイテンシ制御を最適化します。
- 仕組み: 専用 KV キャッシュサービスへの書き込み・読み出しによる動的なリソース配分(
を使用)。SharedStorageConnector
6. スケーリング:UniprocExecutor から MultiProcExecutor へ
モデルが単一 GPU に収まらない場合の拡張方法です。
UniprocExecutor → MultiProcExecutor
- 目的: Tensor Parallelism (TP) や Pipeline Parallelism (PP) のサポート。
- 仕組み:
- メッセージキュー(共有メモリ)の初期化。
- 親プロセスが複数のダモンワーカープロセスを起動 (
)。WorkerProc.make_worker_process - RPC を通じてモデル実行を分散し、結果を集約。
7. 分散システムサービング (Distributed System Serving)
複数のノード(例:H100 クラスター)で vLLM エンジンを展開します。
アーキテクチャ概要
- Headless Node:
で DP グループを初期化。CoreEngineProcManager - API Server Node:
とAsyncLLM
が通信し、リクエストを分散。DPLBAsyncMPClient
リクエストライフサイクル
- クライアントから POST
。/completion - API サーバーが DP コーディネーターに基づき最適なエンジンを選択(負荷分散)。
- エンジン内部でスレッド管理を行い処理を完了。
- JSON レスポンスとして返却。
8. ベンチマークと自動チューニング
主要メトリクス
- TTFT (Time To First Token): 最初から最初のトークンまでの時間(インタラクティブ性)。
- ITL (Inter-Token Latency): トークン間の遅延。
- E2E Latency: 全体的な応答時間。
- Throughput: スループット能力。
Roofline Model & バッチサイズの影響
- バッチサイズ減少: ITL が低下(競争がない)。
- バッチサイズ増加: ITL は増えるが、スループットは向上(ウェイト I/O の相殺)。
- 飽和ポイント ($B_{sat}$): HBM バンド幅に達するとステップレイテンシが平坦化。それ以上は計算量制限へ移行。
vLLM ベンチマーク実行
vllm bench {serve,latency,throughput} コマンドを提供。
vllm bench latency \ --model <model-name> \ --input-tokens 32 \ --output-tokens 128 \ --batch-size 8
エピローグ
本記事では、基本的なエンジンコアから推定デコーディングや分散システムへの拡張までを解説しました。vLLM は以下の特殊処理も備えています(プラグイン方式):
- ハードウェア: TPUs, AWS Neuron 等。
- アーキテクチャ: MLA, MoE, Whisper, LoRA, ALiBi, State-Space Models (Mamba) など。
- 並列化: TP/PP/SP。
今後のステップ: 次の投稿では、特定のスブシステム(例:ページ化 Attention の詳細実装)に深く掘り下げていきます。