Continuous Batching(連続バッチング)とは、LLMの推論においてイテレーション(デコードステップ)単位でリクエストの追加と完了を動的に管理するバッチング手法である。従来のStatic Batchingがバッチ内の全リクエスト完了まで待機するのに対し、Continuous Batchingは完了したリクエストを即座に解放して新たなリクエストを挿入することで、GPUの利用率を常に最大化する。vLLM、TGI、TensorRT-LLMなど主要なLLMサービングエンジンが採用する標準的なバッチング方式である。
Continuous Batching(連続バッチング)は、LLMのトークン生成プロセスにおいて、各デコードイテレーションの境界でバッチの構成を動的に変更するスケジューリング手法である。2023年にOrcaの論文で提案されたIteration-Level Schedulingの概念を基盤としており、現在のLLMサービングにおけるデファクトスタンダードとなっている。
従来のStatic Batching(ナイーブバッチング)では、バッチを構成する全リクエストの生成が完了するまで、バッチ全体がGPUを占有する。例えば、バッチ内に10トークンで完了するリクエストと500トークンを生成するリクエストが混在する場合、10トークンのリクエストが完了した後も500トークンの生成が終わるまでそのスロットはアイドル状態となる。これは「バブル問題」と呼ばれ、GPU利用率を大幅に低下させる原因であった。
Continuous Batchingはこの問題を根本的に解決する。各デコードステップの完了後にスケジューラがバッチの状態を確認し、生成が完了したリクエストをバッチから除去すると同時に、待機キューから新しいリクエストを挿入する。これにより、GPUは常にフルバッチに近い状態で稼働し続ける。
以下のタイムラインで両者の動作の違いを具体的に示す。4つのリクエスト(R1〜R4)がバッチサイズ2で処理される場合を考える。
| ステップ | スロット1 | スロット2 | 状態 |
|---|---|---|---|
| t=0〜5 | R1(5トークン生成) | R2(3トークン生成) | R2はt=3で完了、t=5までアイドル |
| t=5〜12 | R3(7トークン生成) | R4(4トークン生成) | R4はt=9で完了、t=12までアイドル |
| 合計 | 12ステップ | GPU利用率: 58% | アイドルスロット: 5ステップ分 |
| ステップ | スロット1 | スロット2 | 状態 |
|---|
| t=0〜3 | R1 | R2(t=3で完了) | R2完了、R3を挿入 |
| t=3〜5 | R1(t=5で完了) | R3 | R1完了、R4を挿入 |
| t=5〜9 | R4(t=9で完了) | R3 | R4完了 |
| t=9〜10 | - | R3(t=10で完了) | 全完了 |
| 合計 | 10ステップ | GPU利用率: 95% | アイドルスロット: 1ステップ分 |
この例では、Continuous Batchingにより処理完了が17%早まり、GPU利用率は58%から95%に向上している。実際のワークロードではリクエストの生成長のばらつきがさらに大きいため、改善幅は数倍に達することが多い。
Continuous Batchingの実装は、主に3つのコンポーネントで構成される。
スケジューラはContinuous Batchingの中核であり、以下の責務を持つ。
| 責務 | 説明 | 実装例 |
|---|---|---|
| リクエスト受付 | 新規リクエストをwaiting_queueに追加 | FIFO、優先度付きキュー |
| バッチ構成 | 各イテレーションでバッチメンバーを決定 | max_num_seqs制約下で最大充填 |
| プリエンプション | メモリ不足時にリクエストを退避 | Swap(CPU退避)、Recompute(再計算) |
| 完了判定 | EOSトークンまたはmax_tokens到達を検出 | トークン単位のチェック |
vLLMのスケジューラは各イテレーションで以下の順序で処理を行う。
Continuous Batchingでは、各リクエストのKVキャッシュが異なるタイミングで確保・解放されるため、メモリの断片化が深刻な問題となる。PagedAttentionはこの問題を解決する技術で、KVキャッシュを固定サイズのブロック(ページ)に分割し、論理アドレスから物理アドレスへのマッピングテーブルで管理する。これにより連続したメモリ領域を確保する必要がなくなり、断片化によるメモリ浪費が解消される。
LLMの推論はプリフィルフェーズ(入力トークンの一括処理)とデコードフェーズ(出力トークンの逐次生成)で計算特性が大きく異なる。プリフィルは計算バウンド(大量の行列演算)、デコードはメモリバウンド(KVキャッシュの読み出しが支配的)である。Continuous Batchingでは、プリフィルリクエストとデコードリクエストが同一バッチに混在する「チャンクドプリフィル」という手法も採用されている。これにより、プリフィルの長い計算がデコード中のリクエストのレイテンシを増加させる「prefill interference」問題を軽減できる。
各LLMサービングエンジンはContinuous Batchingを異なる最適化で実装している。
| 特性 | vLLM | TGI | TensorRT-LLM |
|---|---|---|---|
| バッチング名称 | Continuous Batching | Continuous Batching | Inflight Batching |
| KVキャッシュ管理 | PagedAttention v2 | Paged KV Cache | Paged KV Cache |
| プリフィル処理 | Chunked Prefill | Chunked Prefill | Separate Prefill |
| プリエンプション | Swap/Recompute選択可 | Swap | Swap |
| スケジューリング | FCFS + Priority | FCFS | Capacity Scheduler |
| マルチモデル | 非対応(v0.6時点) | 非対応 | 対応 |
| 分散推論 | Tensor/Pipeline並列 | Tensor並列 | Tensor/Pipeline並列 |
| 量子化対応 | AWQ/GPTQ/FP8/INT8 | AWQ/GPTQ/BnB | FP8/INT4/SmoothQuant |
vLLMはPagedAttention v2により、ブロック単位のメモリ管理に加えてGPUカーネルレベルの最適化を行い、KVキャッシュアクセスのレイテンシを削減している。TGIはRust実装のルーターとPython実装の推論エンジンを組み合わせたアーキテクチャで、ルーティングのオーバーヘッドを最小化している。TensorRT-LLMはNVIDIAのTensorRTカーネル最適化を活用し、特にNVIDIA GPU上で最高のスループットを実現する。
Continuous Batchingの性能を最適化するための重要なパラメータを以下に示す。
| パラメータ | 説明 | 典型的な範囲 | トレードオフ |
|---|---|---|---|
| max_num_seqs | 同時処理可能な最大シーケンス数 | 64〜512 | スループット ↔ レイテンシ |
| max_num_batched_tokens | 1イテレーションの最大トークン数 | 2048〜32768 | GPU利用率 ↔ メモリ |
| block_size | PagedAttentionのブロックサイズ | 16〜32 | メモリ効率 ↔ 管理オーバーヘッド |
| swap_space | CPU退避用メモリ(GB) | 4〜32 | プリエンプション耐性 ↔ CPU RAM |
| enable_chunked_prefill | チャンクドプリフィルの有効化 | true/false | デコードレイテンシ安定化 |
| max_model_len | モデルが処理する最大コンテキスト長 | 2048〜131072 | 対応入力長 ↔ メモリ消費 |
max_num_seqsはスループットとレイテンシのトレードオフを制御する最も重要なパラメータである。値を大きくするとバッチサイズが増加しスループットが向上するが、各リクエストの処理に使えるGPUリソースが分散されるためレイテンシが増加する。本番環境ではSLA要件に基づいてこのパラメータを調整する。
Continuous Batchingはデコーダーベースの自己回帰言語モデル(GPT系、Llama系、Mistral系など)に適用できる。エンコーダー・デコーダーモデル(T5、BART等)にも原理的には適用可能だが、エンコーダー側の処理が一括で行われるため、恩恵はデコーダー側に限定される。ViTなどのエンコーダーのみのモデルではContinuous Batchingの適用は一般的ではなく、通常のDynamic Batchingが使われる。vLLMは主要なデコーダーモデルをサポートしており、Hugging Faceのモデル形式に対応していれば基本的に利用可能である。
Continuous Batchingのスケジューリングオーバーヘッドは1イテレーションあたり数十マイクロ秒程度であり、推論計算(数ミリ秒〜数十ミリ秒)と比較して無視できるレベルである。ただし、PagedAttentionのメモリマッピング管理にはわずかなオーバーヘッドがあり、非常に短いシーケンス(数トークン)を大量に処理する場合はStatic Batchingと比較して1〜3%程度のレイテンシ増加が観測されることがある。実用上、このオーバーヘッドはスループット改善の恩恵に比べて極めて小さい。
レイテンシのばらつき(テールレイテンシ)を制御するには、max_num_seqsを適切に制限し、プリエンプション戦略を設定する。max_num_seqsを低く設定すると各リクエストに割り当てられるGPUリソースが増加し、レイテンシが安定する。また、enable_chunked_prefillをtrueに設定することで、長い入力のプリフィル処理が細分化され、デコード中のリクエストへの干渉が軽減される。SLAが厳しい場合は、リクエストの優先度に基づくスケジューリングポリシーを導入し、高優先度リクエストのレイテンシを保証することも有効である。vLLMでは将来的にpriority-based schedulingの本格サポートが計画されている。
Prefill Interferenceとは、新規リクエストのプリフィル処理(入力トークンの一括処理)が、既にデコード中のリクエストのレイテンシを増大させる現象である。プリフィルは計算集約的であるため、同一バッチでデコード中のリクエストがGPUリソースの競合によりトークン生成が遅延する。対処法としてチャンクドプリフィル(Chunked Prefill)がある。これはプリフィル処理を小さなチャンクに分割し、各チャンクをデコードイテレーションと交互に実行する方式で、デコードレイテンシの急激な増加を防ぐ。Sarathi-Serveなどの研究ではPiggyback Schedulingが提案されており、プリフィルチャンクをデコードバッチの空きスロットに効率的に詰め込むことで、スループットとレイテンシの両方を改善している。