AI・機械学習
上級

LLM推論バッチング(エルエルエムスイロンバッチング)

LLM推論バッチングは、複数の推論リクエストを束ねて同時に処理するスケジューリング技術。Static Batching(静的バッチング)・Dynamic Batching(動的バッチング)・Continuous Batching(連続バッチング)の3段階で進化し、GPUのTensor Core稼働率とメモリ帯域利用率を最大化する。連続バッチングの導入によりスループットは静的バッチング比で2〜5倍向上し、現代の推論エンジンの標準技術となっている。

0 回閲覧
0 いいね

LLM推論バッチングとは

LLM推論バッチングは、複数のユーザーリクエストを1つのGPU演算バッチにまとめて処理する技術である。LLMの推論はメモリ帯域律速(Memory Bandwidth Bound)であり、単一リクエストではGPUの演算能力の10〜20%しか活用できない。バッチングにより複数リクエストの行列演算をまとめることで、Tensor Coreの稼働率を80〜95%に引き上げ、リクエストあたりのコストを大幅に削減する。

バッチングの効果はLLMのAutoregressive(自己回帰)生成プロセスに起因する。1トークン生成のたびにモデル全体(数十GB)の重みをGPUメモリから読み出す必要があり、この「重み読み出しコスト」は1リクエストでも100リクエストでもほぼ同じである。したがって、バッチサイズを増やしても追加コストはKVキャッシュ分の僅かなメモリ増加のみで、リクエストあたりの演算コストは劇的に低下する(Arithmetic Intensity向上)。

バッチング手法の進化

Static Batching(静的バッチング)

最も原始的なバッチング手法。一定数のリクエストが溜まるか、タイムアウト期間が経過した時点でバッチを確定し、全リクエストが生成完了するまで新リクエストを受け付けない。

問題点:

  • Padding Wastage: バッチ内のリクエストは入力長・出力長が異なるため、最長シーケンスに合わせてパディングが必要。パディング部分のGPU演算は完全に無駄。
  • Tail Latency: バッチ内の1リクエストが長い出力を生成すると、短い出力で完了した他リクエストのレスポンスも遅延する。
  • GPU Idle: バッチ完了から次バッチ開始までのアイドル時間が不可避。
バッチ内リクエスト入力長出力長完了タイミングアイドル時間
リクエストA128320.8秒4.2秒待ち
リクエストB256641.5秒3.5秒待ち
リクエストC5122565.0秒基準(最遅)
リクエストD64160.4秒4.6秒待ち

上記例では、バッチ全体の完了は最遅のリクエストC(5.0秒)に律速され、A/B/Dは3.5〜4.6秒のアイドル時間が発生。バッチ平均GPU利用率は約35%にとどまる。

Dynamic Batching(動的バッチング)

Static Batchingのバッチ構成を動的に最適化する手法。入力長が近いリクエストを優先的にグループ化し、パディング量を最小化する。Triton Inference Serverのdynamic_batching設定が典型例で、preferred_batch_sizeとmax_queue_delay_microsecondsでバッチサイズと待機時間のトレードオフを制御する。

パディング削減により、Static Batching比で30〜50%のスループット向上が見込めるが、出力長のばらつきによるTail Latency問題は依然として残る。

Continuous Batching(連続バッチング / Iteration-Level Scheduling)

2023年にOrcaシステム(OSDI 2022論文)で提唱され、vLLMで広く普及した革新的スケジューリング手法。トークン生成の各イテレーション(1ステップ)単位でバッチ構成を動的に変更する。

動作原理:

  1. イテレーション開始時に、現在のバッチ内リクエストの状態を確認
  2. EOSトークンを生成したリクエストをバッチから除去
  3. 待機キューから新リクエストを空きスロットに投入
  4. 更新されたバッチでAttention + FFN演算を実行
  5. 1に戻る

これにより、GPUは常に最大バッチサイズに近い状態で稼働し、アイドル時間がほぼゼロになる。

Prefill/Decode分離アーキテクチャ

LLM推論には2つのフェーズがある。

Prefillフェーズ

入力プロンプト全体を一括処理してKVキャッシュを構築するフェーズ。入力トークン数に対して演算量がO(n²)で増加し、Compute Bound(演算律速)となる。バッチ内の全リクエストのPrefillを並列実行することで、GPU演算能力を最大活用できる。

Decodeフェーズ

1トークンずつ自己回帰生成するフェーズ。KVキャッシュの読み出しが支配的で、Memory Bandwidth Bound(メモリ帯域律速)となる。バッチサイズを大きくしてもメモリ帯域がボトルネックとなり、スループット向上に限界がある。

Chunked Prefill

PrefillとDecodeを同一バッチ内で混在させる技術。長い入力のPrefillをチャンク(例: 512トークン単位)に分割し、Decodeリクエストと交互に実行する。これにより、Prefillの長い入力がDecodeリクエストのレイテンシを阻害する「Prefill Starvation」問題を解消する。vLLM 0.6以降、SGLang 0.4以降で標準実装。

フェーズ演算特性ボトルネックバッチング効果
PrefillO(n²)演算Compute Bound高(演算並列化)
DecodeKVキャッシュ読み出しMemory BW Bound中(帯域上限)
Chunked PrefillPrefill+Decode混在両方のバランス高(レイテンシ平準化)

スループットvsレイテンシのトレードオフ

バッチサイズの増大はスループット向上とレイテンシ増加のトレードオフを伴う。

スループット最大化(バッチ推論・オフライン処理)

バッチサイズを可能な限り大きくし、GPUメモリの限界まで同時リクエストを詰め込む。オフラインのバッチ評価(ベンチマーク・データセット処理)で使用。レイテンシは犠牲にするが、1トークンあたりのコストを最小化。

レイテンシ最小化(リアルタイムチャット)

バッチサイズを制限し、個々のリクエストのTTFT(Time to First Token)とITL(Inter-Token Latency)を最小化する。チャットアプリケーションでは、TTFT < 200ms、ITL < 50msが快適性の目安。vLLMの--max-num-seqsやSGLangの--max-running-requestsでバッチサイズ上限を制御する。

SLO(Service Level Objective)ベースの制御

本番環境では、P99レイテンシ目標(例: TTFT P99 < 500ms)を満たす範囲でスループットを最大化する。vLLMの--scheduling-policyやカスタムスケジューラで、SLO違反リスクが高まった時点でバッチサイズを動的に縮小する。

よくある質問(FAQ)

Q1: バッチサイズはいくつに設定すべきですか?

用途とハードウェアに依存する。リアルタイムチャットなら8〜32、APIサービングなら32〜128、オフラインバッチ処理なら256〜1024が目安。GPUメモリに余裕があればバッチサイズを大きくするほどスループットが向上するが、KVキャッシュのメモリ消費が増加するため、--gpu-memory-utilization 0.9(vLLM)のようにメモリ使用率の上限を設定して安全マージンを確保する。

Q2: Continuous BatchingとDynamic Batchingの違いは何ですか?

Dynamic Batchingはバッチ全体の開始・終了を単位とするのに対し、Continuous Batchingはトークン生成の1イテレーション単位でバッチ構成を変更する。Dynamic Batchingでは生成完了したリクエストがバッチ終了まで待機するが、Continuous Batchingでは即座に退出・新リクエスト投入が行われる。スループットはContinuous Batchingが2〜5倍優れる。

Q3: Prefill/Decode分離はどんな場合に有効ですか?

入力プロンプトが長い(4K〜128Kトークン)場合に特に有効。長いPrefillがDecodeリクエストのレイテンシを数秒単位で阻害する「Prefill Starvation」が発生するため、Chunked PrefillまたはPrefill/Decode専用GPUの分離構成で解消する。RAG(検索拡張生成)やドキュメント要約など、入力長が出力長を大幅に上回るワークロードで必須の技術。