Transformers連続バッチングのKV予算設計
CHATGPT / AIChatGPTなどの生成AIを活用して運営・記事制作・更新を行っています。制作方針
Transformers v5.14.1のcontinuous batchingを対象に、入力buffer、Paged KV cache、予約枠の競合を整理し、TTFT・生成latency・throughput・VRAMを再現可能に比較する設計を解説します。

先に結論
Transformers 5のcontinuous batchingでは、1 stepへ入れる入力tokenの予算と、進行中requestが保持するPaged KV cacheが同じGPU memoryの余白を使います。max_batch_tokensを増やせばprefillをまとめやすくなりますが、入力bufferと一時tensorが増え、KV cacheやdecodeの余白を圧迫します。num_blocksだけを増やしても、admissionを無制限にするとactive requestがcacheを埋め、offloadや再計算でtail latencyが悪化し得ます。
最初の比較はauto設定をbaselineにし、次にmax_batch_tokens、KV cache容量、safety_marginを1つずつ変えます。CUDA graphs、async batching、prefix caching、CPU offloadは同時に有効化せず、TTFT、生成中のtoken間隔、完了latency、token throughput、peak VRAMを分けて観測します。本記事は公式仕様から組み立てた検証計画で、特定GPUの最適値やbenchmark結果は示しません。
確認日時: 2026年8月4日 05:30(Asia/Tokyo)
対象version: Transformers v5.14.1、commita08ace4(2026年7月16日公開)。現行continuous batching architecture/API referenceは2026年8月4日に再監査
検証区分: 公式documentationとrepository releaseに基づく机上調査・測定設計です。GPUでのmodel load、生成、memory計測、latency計測、障害注入は実施していません。数値はAPIの既定値・設定例だけで、性能結果ではありません。
想定範囲: 単一CUDA GPU、単一model、Transformers標準generate_batch()/ContinuousBatchingManager。tensor parallel、MPS、CPU-only、HTTP serverのnetwork overheadは適用外
確認済みの仕組みと設計判断を分ける
2026年8月4日の公式文書から確認できる事実は次のとおりです。
- continuous batchingは生成stepごとに完了requestを外し、待機requestを入れる
- requestはpending、prefilling、decoding、finishedのlife cycleを進む
- Paged AttentionはKV cacheを固定長blockへ分け、requestごとにblock IDを持たせる
max_batch_tokensは1回のforward passで処理するquery tokenの上限であるnum_blocksとmax_batch_tokensは未指定時にGPU memoryから推定されるmax_memory_percentはmodel load後の空きGPU memoryからKV cacheへ使う割合を制限するsafety_marginを下回るまでfree blockが減ると、新規prefillを止めてactive decodeを優先する- prefix cachingは既定で有効だが、全layerがfull attentionのmodelだけで作動し、sliding-window modelでは自動的に無効になる
- async batchingはCPU準備とGPU計算を重ねる代わりにinput buffer用VRAMを概ね倍にし、CUDA graphsを必要とする
「入力予算、KV cache、予約枠を別々に測り、1変数ずつ変更する」のは本記事の設計判断です。公式文書は機能と相互作用を説明しますが、個々のworkloadの最適値は保証しません。
GPU memoryを3つの予算へ分ける
model weightをloadした後の空きGPU memoryを、少なくとも次の3領域として観測します。
| 予算 | 主な設定 | 増やしたときの狙い | 先に確認する副作用 |
|---|---|---|---|
| 入力・一時tensor | max_batch_tokens、max_requests_per_batch | prefillをまとめる、schedulerの空きを減らす | logits、一時buffer、mask、peak VRAM、decode待ち |
| Paged KV cache | num_blocks、block_size、max_memory_percent | active requestと長いcontextを保持する | modelごとのKV bytes、block内部の余り、offload |
| decode予約枠 | safety_margin | 進行中requestの次blockを守る | 新規prefillの待ち、queue、TTFT |
この3つは独立した物理memoryではありません。たとえば大きなmax_batch_tokensはprefill throughputを上げる可能性がある一方、入力bufferや語彙sizeに比例するlogits tensorを増やします。その結果、KV cacheに使える余白が減り、同時に保持できるrequestが減る場合があります。
公式architecture文書は、KV cacheのblock sizeの既定を256 tokenと説明しています。blockはallocation単位なので、小さいrequestが多いworkloadではblock内部の未使用部分、長いrequestではblock数とblock tableの上限を観測します。単純に「blockを小さくすれば節約」とせず、allocation回数、backendの対応、実測latencyまで含めます。
固定条件と変更変数
比較runごとに次を固定して保存します。
| 区分 | 固定する値 |
|---|---|
| software | Transformers version/commit、Python、PyTorch、CUDA runtime、driver |
| hardware | GPU型番と枚数、総VRAM、power/clock policy、他processの有無 |
| model | repository、revisionまたはcommit、weight hash、dtype、quantization、attention implementation |
| workload | prompt token長の分布、生成上限、request数、到着間隔、共通prefix率 |
| generation | do_sample、seed、EOS、logits processor、chat template |
| measurement | warm-up回数、測定run数、clock、同期点、失敗の数え方 |
最初のbaselineはContinuousBatchingConfig()のauto推定とし、解決後の設定、起動直後のfree VRAM、最初のrequestを入れる前のmemoryを記録します。次に変更するのは1列だけです。
from transformers.generation import ContinuousBatchingConfig
baseline = ContinuousBatchingConfig()
prefill_budget_run = ContinuousBatchingConfig(
max_batch_tokens=512,
)
decode_protection_run = ContinuousBatchingConfig(
max_batch_tokens=512,
safety_margin=0.15,
)
ここにある512と0.15は比較軸を示すための値です。0.15はFIFO schedulerの公式既定で、すべてのworkloadへの推奨値ではありません。prefill_firstでは既定marginが0なので、schedulerを変えたrunを同じ設定名で比較しません。
観測する5つの指標
平均latencyだけでは、prefillを優先してdecodeが詰まったrunを見落とします。最低限、次をrequest単位とrun全体で分けます。
- TTFT: request投入から最初の生成tokenまで
- inter-token latency: 生成中のtoken間隔。平均だけでなくp50、p95、p99
- 完了latency: request投入からEOSまたは生成上限まで
- token throughput: 測定区間のprompt tokenとgenerated tokenを区別した毎秒token数
- memoryとqueue: peak allocated/reserved VRAM、free KV block、pending/active request、offload/soft reset回数
generate_batch(record_timestamps=True)は生成tokenのtimestampを記録する入口を持ちます。ただし、host側の受付時刻、GPU同期、最初のtokenを利用可能にした時刻をどう定義するかは測定harness側で統一します。nvidia-smiの低頻度sampleだけでは一瞬のpeakを逃すため、PyTorchのmemory統計と外側のGPU監視を併用し、測っている範囲を記録します。
比較を4段階に分ける
1. 正しさとauto baseline
同じprompt集合で、全requestが完了し、出力tokenが固定batchの基準と同じ終了条件を満たすことを確認します。samplingは無効にし、OOM、timeout、cancel、空出力を失敗として数えます。最初のrunはcompileやkernel warm-upを含むので、測定runから分けます。
2. 入力予算だけを変える
max_batch_tokensを段階的に変えます。短いpromptだけでなく、p50、p95、最大長を含む分布を使い、chunked prefillが発生する条件も残します。大きくしたrunでprompt throughputが上がっても、TTFT p95、inter-token p99、peak VRAMが悪化していないかを確認します。
max_requests_per_batchも別runで変更します。公式API文書は、request数が多いと語彙sizeを含むlogits tensorが大きくなるため、この上限がOOM回避に関係すると説明しています。max_batch_tokensと同時に変えると原因を分離できません。
3. KV cacheと予約枠を変える
autoで解決されたnum_blocksを記録した後、必要ならmax_memory_percentまたはnum_blocksのどちらか一方を固定します。長いdecode requestと新しいprefillが重なるworkloadで、free block、admission停止、offload、再計算を観測します。
safety_marginはfree blockの割合を予約し、新規prefillよりactive decodeの継続を優先します。marginを増やせば常に速くなるわけではなく、decodeのtailを守る代わりに待機requestのTTFTを延ばす可能性があります。SLOがTTFT中心か、会話中のtoken間隔中心かを先に決めます。
4. 高度な最適化を1つずつ足す
baseのmemory境界を確定してから、次を個別runにします。
- CUDA graphs: kernel dispatch overheadを減らす候補。capture warm-up、graph memory、対応shapeを記録
- async batching: CPU準備とGPU実行を重ねる候補。CUDA graphs必須で、input buffer用VRAMが概ね倍になる
- prefix caching: 共通prefixのKV blockを共有する候補。共通prefix率を記録し、sliding-window modelでは無効になることを確認
- CPU offload: GPU cache不足時の再prefillを避ける候補。pinned host memory、PCIe transfer、復帰latencyを観測
- compile level: warm-upを長くしてsteady-stateを改善する候補。短命processと常駐serviceを分ける
複数機能を一度に足して最大値を探す前に、それぞれがどの指標へ効いたかを確認します。async batchingのrunはCUDA graphsとの差分を切り分けるため、「graphsのみ」と「graphs+async」を比較します。
固定batchとの比較方法
固定batch側はmodel.generate()でpaddingした入力を処理し、continuous側は同じtoken列、生成上限、sampling、dtype、attention backendを使います。固定batchのpadding tokenをprompt token throughputへ含めるか、実tokenだけ数えるかを事前に決めます。
比較workloadは少なくとも3種類に分けます。
| workload | 目的 |
|---|---|
| 同じ長さ・一括到着 | scheduler overheadと固定batchが有利な範囲を見る |
| 長さが不均一・一括到着 | 完了requestの入替効果を見る |
| 長さが不均一・段階到着 | servingに近いqueue、admission、TTFTを見る |
全件を一度にgenerate_batch()へ渡すだけでは「途中で外部から到着するrequest」を再現しません。3つ目のworkloadではContinuousBatchingManagerへarrival scheduleどおりにadd_request()し、request IDと投入時刻を保存します。
適用外と失敗時の戻し方
sliding-window/hybrid model
continuous batching自体が使えても、prefix cachingはfull-attention layerだけのmodelで有効です。sliding-window modelでは自動無効になるため、prefix hitを前提にcapacityを計算しません。layer groupごとにKVの形が違うhybrid modelは、単一の「1 tokenあたりKV bytes」だけで一般化しません。
OOMまたはoffload急増
最後に変更した変数をbaselineへ戻します。model load時OOM、prefill時OOM、decode成長中のcache不足を分け、同じ対策で扱いません。max_batch_tokensを下げる、max_requests_per_batchを絞る、KV blockを減らす、marginを増やす、model/dtypeを変える操作はそれぞれ別runです。
tail latency悪化
throughputが上がってもinter-token p99がSLOを超えるなら、safety_margin、scheduler、long prefillのchunkingを確認します。結果を平均値だけで採用せず、どのrequest長とarrival条件で悪化したかを残します。
production servingへの一般化
generate_batch()のprocess内測定にはHTTP parsing、network、authentication、queue limit、client cancelがありません。公式文書はproduction deploymentにtransformers serveを案内していますが、本記事の数値をそのままserver容量へ置き換えることはできません。serverでは同じmodel条件を固定し、network込みの負荷試験を別に行います。
採用判断
continuous batchingを採用する条件は、単発requestが速いことではありません。実workloadの到着と長さの分布で、正しさを維持しながらGPU idleを減らし、throughputの改善がTTFTとinter-tokenのSLO、memory上限を壊さないことです。
既存のvLLM記事はengine全体のprefix cache、prefill、speculative decodingを扱っています。vLLM V1入門とは役割を分け、本記事はTransformers標準APIの設定と測定境界に限定しました。llama.cppのserver slotと共有KVはllama-server連続バッチング設定を参照してください。最初の実行から始める場合は、Transformers連続バッチング入門へ戻れます。
参考資料
- Hugging Face公式:Continuous batching(2026年8月4日確認)
- Hugging Face公式:Continuous batching architecture(2026年8月4日確認)
- Hugging Face公式:Continuous batching API reference(2026年8月4日確認)
- Hugging Face公式:Paged attention(2026年8月4日確認)
- Hugging Face公式GitHub:Transformers v5.14.1(2026年7月16日公開、2026年8月4日確認)


