vLLMは、LLM(大規模言語モデル)を効率良く実行し、APIとして提供するためのinference and serving engine(推論・配信エンジン)です。モデルそのものを賢くする技術ではありません。複数のrequestが同時に届く環境で、GPU memory、とくにKV Cacheを無駄なく使い、server全体のthroughput(単位時間当たりの処理量)を高めます。
中心技術のPagedAttentionは、requestごとに増減するKV Cacheを固定長のblockへ分け、物理memory上の空いている場所へ配置します。OSのvirtual memoryに着想を得ていますが、OSのpagingをそのままGPUへ移したものではありません。本記事では、block化が必要な理由をmemory容量の具体計算から追い、continuous batchingやprefix cachingとの違い、導入時の測定方法まで整理します。

- 3文要約
- vLLMは「モデル」ではなく「サーバー側の実行基盤」
- LLM serverで増え続けるKV Cache
- 連続領域で管理すると何が無駄になるのか
- PagedAttentionはKV Cacheをblockで管理する
- KV Cacheの容量をtokenから計算する
- blockはrequestの成長に合わせて割り当てる
- continuous batchingは別の問題を解く
- block共有とAutomatic Prefix Caching
- vLLM全体のthroughputは複数要因で決まる
- OpenAI互換serverとして試す最小例
- 調整parameterはtrade-offで選ぶ
- 導入前後で測る項目
- vLLMが向くケース・別の構成も検討するケース
- 第17〜21回の高速化技術との違い
- よくある質問
- まとめ
- 参考資料
3文要約
- vLLMはLLM modelではなく、複数requestを効率良く処理する推論・配信engineです。
- PagedAttentionはKV Cacheをlogical block(requestから見た論理block)とphysical block(GPU memory上の物理block)に分け、block tableで対応付けることで、大きな連続領域の事前予約とexternal fragmentationを抑えます。
- server全体の性能はPagedAttentionだけで決まらず、continuous batching、Attention kernel、promptとoutputの長さ、concurrencyを含めてTTFT、ITL、throughput、GPU memoryを実測する必要があります。
vLLMは「モデル」ではなく「サーバー側の実行基盤」
LLMを使う仕組みは、大きく3層に分けると理解しやすくなります。
| 層 | 主な役割 | 例 |
|---|---|---|
| model | 入力tokenから次tokenの確率を計算する | Transformer、model weights |
| inference engine | tensor計算、KV Cache、batch、schedulerを管理する | vLLM |
| API/application | 認証、rate limit、会話履歴、業務logicを扱う | OpenAI互換API、chat application |
vLLMは中央のinference engineを中心に、OpenAI互換HTTP serverも提供します。同じmodel weightsを使っても、requestをどの順に実行し、KV Cacheをどのように配置するかで、1台のGPUが単位時間に処理できるtoken数は変わります。
ここで「速い」には少なくとも2つの意味があります。
- latency: 1つのrequestが応答を受け取るまでの時間
- throughput: server全体が1秒間に処理できるrequest数やtoken数
PagedAttentionはmodelの演算量を直接減らすというより、KV Cacheのmemory効率を上げ、同時に載せられるrequestやtokenを増やす経路でthroughputへ効きます。1 requestだけを低concurrencyで実行したとき、必ず1 tokenの計算が大幅に短くなるという意味ではありません。

LLM serverで増え続けるKV Cache
LLMの生成は、promptをまとめて処理するprefillと、tokenを1つずつ追加するdecodeに分かれます。decode時に過去tokenのKeyとValueを毎回計算し直さないため、各layerの結果をKV Cacheへ保存します。詳しい役割はKV Cacheとは?LLMの生成を高速化する仕組みで解説しました。
model weightsはrequest間で共有できます。一方、KV Cacheは原則としてrequestごとに異なり、sequence lengthとともに増えます。
たとえば次のrequestが同時に走る状況を考えます。
| request | prompt長 | 現在のoutput長 | 今後のoutput長 |
|---|---|---|---|
| A | 1,000 | 20 | 不明 |
| B | 80 | 150 | 不明 |
| C | 4,000 | 2 | 不明 |
requestごとに現在長も終了時刻も違います。生成はEOS tokenで早く終わることもあれば、設定した最大token数まで続くこともあります。つまりKV Cacheは「大きい」だけでなく、「requestごとに予測しにくい速度で伸び、別々の時刻に解放される」memoryです。
連続領域で管理すると何が無駄になるのか
単純な方法は、requestごとにKV Cache用の連続した領域を確保することです。しかし、可変長requestでは3種類の無駄が生じます。
将来token用の予約
最大sequence lengthまで先に確保すれば、途中で領域が足りなくなる心配は減ります。しかし、実際に100 tokenで終了するrequestへ2,048 token分を予約すると、大半は使われません。その予約中は他のrequestが利用できないため、batchへ載せられるrequest数が減ります。
internal fragmentation
allocatorの単位がrequestの実使用量より大きいと、確保した領域の内側に未使用部分が残ります。これをinternal fragmentation(確保領域内部の断片化)と呼びます。
external fragmentation
requestの終了と伸長を繰り返すと、空き領域が複数箇所へ分かれます。合計容量が足りていても、新しいrequestが必要とする大きな連続領域を置けない状態がexternal fragmentation(空き領域間の断片化)です。

PagedAttentionはKV Cacheをblockで管理する
PagedAttentionの原論文は、OSのvirtual memoryとpagingから着想を得て、KV Cacheを固定数tokenごとのblockへ分けました。重要なのは、requestから見える順序と、GPU memory上の配置を分離することです。
- logical block: requestのtoken順に並ぶblock
- physical block: GPU memory上に確保された固定長block
- block table: logical blockがどのphysical blockにあるかを示す対応表
たとえばrequest Aのlogical block A0, A1, A2を、physical block 7, 1, 9へ配置できます。GPU memory上では非連続ですが、block tableはA0→7、A1→1、A2→9という順序を保持します。Attention kernelはこの表を参照して、必要なK/Vを読みます。
request BのB0, B1はphysical block 3, 8へ置けます。physical block 2と4が離れて空いていても、空きを1つの連続領域へまとめる必要はありません。固定長blockとして個別に利用できます。

これはOS pagingと同じ発想を利用した比喩ですが、同一の実装ではありません。対象はLLM servingのKV Cacheであり、vLLM自身のblock managerとAttention実装が対応関係を扱います。「SSDへ追い出すから速い」「OSがGPU memoryを自動管理する」という説明ではありません。
KV Cacheの容量をtokenから計算する
block化の効果を理解するには、まず1 tokenのKV Cacheがどれくらい大きいかを計算します。layer数を\(L\)、KV head数を\(H_{kv}\)、1 headの次元を\(d_h\)、1要素のbyte数を\(b\)とします。KとVの2つを保存するため、raw KV payload(K/V tensor本体)のtoken当たり容量は次です。
block sizeを\(B\) tokenとすると、1 blockのraw payloadは次になります。
現在のsequence lengthが\(N\) tokenなら、必要block数\(n_{blocks}\)、割当payload \(M_{alloc}\)、末尾blockの未使用payload \(M_{waste}\)は次です。
100 tokenを保存する具体例
説明用に、\(L=32\)、\(H_{kv}=8\)、\(d_h=128\)、BF16の\(b=2\) byteとします。
\(B=16\) tokenなら、1 blockは\(16\times128=2{,}048\) KiB、つまり2 MiBです。\(N=100\) tokenには\(lceil100/16\rceil=7\) blockが必要です。
| 項目 | 計算 | 結果 |
|---|---|---|
| 実token分 | \(100\times128\) KiB | 200 MiB |
| 割当capacity | \(7\times16=112\) token | 224 MiB |
| 末尾の未使用 | \((112-100)\times128\) KiB | 24 MiB |
block化しても、最後のblockには最大で\(B-1\) token分のinternal fragmentationが残ります。それでもrequest最大長まで予約する方法に比べ、未使用量をblock単位へ制限できます。

この224 MiBはK/V tensorのraw payloadだけです。model weights、allocator metadata、alignment、workspace、CUDA graph、temporary tensorなどは含みません。また、KV head数はmodelにより異なります。GQA/MQAとは?KV Cacheを減らすAttention改良で扱ったように、\(H_{kv}\)を減らしたmodelならtoken当たり容量も減ります。
blockはrequestの成長に合わせて割り当てる
block pool(再利用可能なphysical blockの集合)を使うrequest lifecycleは、概念的に次の流れです。
- prefillするtoken数に応じて必要blockをpoolから割り当てる。
- decodeで現在blockが埋まったら、次のfree blockを追加する。
- requestが終了したら、そのrequestだけが使うblockをpoolへ戻す。
- 後続requestが解放済みblockを再利用する。
大きな連続領域を探すのではなく、必要な数の固定長blockを確保すればよいため、空きmemoryを使いやすくなります。

ただし、physical blockが足りなくなれば何も起きないわけではありません。schedulerは実行するsequenceを減らしたり、後で再開するためのpreemption(実行の一時退避・再計算)を選んだりします。preemptionが頻発すれば追加costが増えるため、memory効率が高いこととmemory無制限は別です。
continuous batchingは別の問題を解く
PagedAttentionと一緒に語られるcontinuous batching(継続的batching)は、memory配置ではなくschedulingの技術です。
static batchでは、同じbatchに入ったrequestの一部が早く終了しても、長いrequestが終わるまで次のrequestを入れにくい構成があります。生成長がばらつくほど、終了済みの枠が遊びます。
continuous batchingでは、decode iterationごとに実行対象を見直します。終了したrequestを外し、budgetとmemoryに収まる新しいrequestを入れます。PagedAttentionがblockを素早く割り当て・解放できるため、schedulerは可変長requestを詰め替えやすくなります。
| 技術 | 管理対象 | 主な役割 |
|---|---|---|
| PagedAttention | KV Cache memory | 非連続なphysical blockへ効率良く配置する |
| continuous batching | 実行sequence | iterationごとに終了requestと新requestを入れ替える |
| Attention kernel | tensor計算とmemory access | 実際のAttention計算を効率化する |

FlashAttentionとは?Attentionを高速化する技術はkernel内部のHBM accessを減らす技術です。PagedAttentionはrequest間のKV Cache配置、continuous batchingは実行枠の管理を扱います。どれか1つが他を置き換えるのではなく、異なるbottleneckへ作用します。
block共有とAutomatic Prefix Caching
physical blockにはreference count(何個のsequenceが参照しているか)を持たせ、同じ内容を複数sequenceで共有できます。共有blockの内容を書き換える必要が生じたときだけcopy-on-writeで複製すれば、parallel samplingや共通prefixでK/Vを重複保持せずに済みます。
vLLMのAutomatic Prefix Caching(APC)は、同じprefixに対応するKV Cache blockを再利用します。長いsystem prompt、同じdocumentへの複数質問、共通の会話履歴などでは、同じprefixのprefillを繰り返さずに済みます。
ただしAPCは、新しいoutput tokenをdecodeする計算を省きません。
| workload | APCの期待効果 |
|---|---|
| 長い共通promptを何度も使う | prefill再計算を減らしやすい |
| prefixがrequestごとに異なる | cache hitが少なく効果は限定的 |
| promptは短くoutputが長い | 全体時間に占めるdecodeが大きく、効果は限定的 |
PagedAttentionはKV Cacheをblockとして管理する土台、APCは同一内容のblockを再利用する追加機能です。この2つを同義にしないことが重要です。
vLLM全体のthroughputは複数要因で決まる
vLLM公式サイトは、PagedAttention、continuous batching、最適化されたkernel、OpenAI互換APIなどを主要機能として挙げています。server全体の性能は、それらの組合せで決まります。

原論文は、評価した条件でFasterTransformerやOrcaに比べ、同程度のlatencyで2〜4倍のthroughputを報告しました。この数値はPagedAttentionとvLLMの設計が有効だった実例ですが、2023年当時の対象model、hardware、workload、baselineによる結果です。現在の任意のmodelやGPUで2〜4倍になる保証ではありません。
とくに次の条件で結果は変わります。
- prompt長とoutput長の分布
- 同時request数と到着pattern
- model size、KV head数、dtype
- GPUの種類、枚数、memory容量
- sampling設定と最大sequence length
- prefix共有率
- baseline engineとsoftware version
OpenAI互換serverとして試す最小例
現行のvLLM Quickstartでは、vllm serveでmodelを読み込み、OpenAI互換serverを起動できます。次はQwen2.5 1.5B Instructを使う例です。
vllm serve Qwen/Qwen2.5-1.5B-Instruct \
--generation-config vllm \
--api-key local-dev-key
別terminalからChat Completions endpointへrequestします。
curl http://localhost:8000/v1/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer local-dev-key" \
-d '{
"model": "Qwen/Qwen2.5-1.5B-Instruct",
"messages": [{"role": "user", "content": "KV Cacheを一文で説明して"}],
"temperature": 0
}'
--generation-config vllmは、model repositoryのgeneration_config.jsonがserverのsampling defaultを上書きする影響を避け、比較条件を明示しやすくするために付けています。用途によってはmodel側の推奨設定を使う判断もあります。
commandや対応hardwareはversionで変わります。導入時は公式Quickstartを確認してください。また、--api-keyだけでinternet公開に必要なsecurityが完結するわけではありません。外部公開ではTLS、network access control、認証・認可、rate limit、logの機密情報対策を別途設計します。
調整parameterはtrade-offで選ぶ
vLLMにはKV Cacheやschedulerを調整するoptionがあります。名称や対応値はversion依存ですが、判断軸は次のように整理できます。
| 調整軸 | 増やす・変える利点 | 主なcost・注意点 |
|---|---|---|
| block size | 管理するblock数を減らせる場合がある | 末尾の未使用slotが増え得る |
| KV Cache memory budget | 多くのtokenを保持しやすい | weightsやworkspaceの余裕が減る |
| max batched tokens | 1 iterationの処理量を増やせる | TTFTやmemory pressureへ影響し得る |
| max sequences | concurrencyを増やせる | request当たりlatencyやpreemptionへ影響し得る |
| cache dtype | 低bit化で容量を減らせる場合がある | accuracy、hardware対応、変換costを検証する |
| prefix caching | 共通prefixのprefillを再利用できる | hash管理のoverheadがあり、cache hitが必要 |
「GPU memory utilizationを上限近くへ設定すれば最速」とは限りません。runtimeが必要とする一時領域やtrafficの揺れを含め、OOMを起こさないmarginが必要です。公式engine argumentsと使用versionの仕様を確認し、小さく変更して測ります。
導入前後で測る項目
benchmarkでは、baselineとvLLMで同じmodel、dtype、prompt集合、sampling、最大output長を使います。平均値だけでなく、p50、p95、p99のtail latencyも確認します。
| 分類 | 指標 | 分かること |
|---|---|---|
| 初動 | TTFT(Time To First Token) | requestから最初のtokenまでの待ち時間 |
| 生成 | ITL/TPOT | token間の待ち時間、decodeの体感速度 |
| 全体 | end-to-end latency | request完了までの時間 |
| 処理量 | output tokens/s、requests/s | server全体のcapacity |
| memory | GPU使用量、KV cache utilization | block budgetと余裕 |
| scheduler | queue time、preemption | 混雑とmemory不足の影響 |
| 品質・安定性 | error率、出力条件の一致 | OOMや設定差による比較崩れ |
| APC | prefix cache hit、prefill削減 | 共通prefix再利用の寄与 |

trafficは一定間隔だけでなく、実運用に近いburstも用意します。短いchatだけでなく、長いprompt、長いoutput、長短混在も分けます。throughputを上げた結果、TTFTやp99 latencyが許容範囲を超えていないかを同時に見ます。
vLLMが向くケース・別の構成も検討するケース
vLLMは、同一modelへ複数requestが届き、長さがばらつくonline servingで特に検討価値があります。OpenAI互換APIを使って既存clientから接続したい場合にも便利です。
一方、次のケースでは効果と運用costを比較します。
- 1 requestだけを低頻度で実行し、throughputより起動時間を重視する
- edge deviceや対応外hardwareで動かす
- 独自kernelや特殊なmodel architectureへの対応が必要
- server管理よりmanaged APIの運用簡素化を優先する
- strictなsingle-request latencyだけが目標で、batchingの待ちを許容しない
選択肢は「vLLMなら常に正解」ではありません。対象modelが現行versionで対応しているか、必要precisionとhardwareで安定するか、実trafficでSLO(service level objective、応答性能の目標)を満たすかを確認します。
第17〜21回の高速化技術との違い
フェーズ3で扱った技術は、すべてLLM推論を速く・軽くするものですが、削るcostが異なります。
| 技術 | 主に変える場所 | 主な効果 | vLLMとの関係 |
|---|---|---|---|
| KV Cache | decodeのstate | 過去K/Vの再計算を省く | PagedAttentionが効率良く管理する対象 |
| 量子化 | weight・activation・cacheのbit幅 | memoryと計算costを減らす | 対応backend・精度を確認して併用する |
| FlashAttention | Attention kernel | HBM accessを減らす | kernel側の最適化として補完する |
| GQA/MQA | model architecture | KV headとcache容量を減らす | 1 token当たりblock容量を小さくする |
| Speculative Decoding | decode algorithm | target call当たり複数tokenを確定する | 対応方式ではserving engine上で併用できる |
| PagedAttention | serving runtime | KV Cacheの割当無駄を減らす | vLLMのmemory管理の中核 |
速度最適化は足し算ではありません。たとえばGQAでKV Cacheを小さくし、PagedAttentionで効率良く配置しても、compute-bound(演算律速)ならmemory削減がそのままlatency短縮にはなりません。bottleneckを測ってから組み合わせます。
よくある質問
PagedAttentionはmodel weightsもpageに分けますか?
本記事で扱うPagedAttentionの中心は、requestごとに変化するKV Cacheのblock管理です。model weightsのoffloadやstorage pagingとは分けて考えてください。
block化すればmemory wasteは完全にゼロですか?
いいえ。末尾blockの未使用slot、metadata、alignment、runtimeの一時memoryなどは残ります。大きな連続領域の事前予約とexternal fragmentationを抑え、無駄の範囲を小さなblock単位へ限定しやすくする設計です。
PagedAttentionとFlashAttentionは同じですか?
違います。PagedAttentionは複数requestのKV Cacheをどう配置して参照するかを扱います。FlashAttentionはAttention計算中のmemory accessを工夫し、中間行列をHBMへ何度も読み書きするcostを減らします。
vLLMを使えば1 requestも必ず速くなりますか?
保証できません。vLLMの大きな利点は、memory効率とschedulingによりserver throughputを高めやすいことです。single-request latencyはmodel、prompt/output長、kernel、hardware、設定で変わるため実測します。
prefix cachingを有効にすればdecodeも速くなりますか?
共有prefixのprefill再計算を省きますが、新しく生成するoutput tokenのdecode計算は省きません。長い共通prefixが繰り返されるworkloadほど効果を期待できます。
まとめ
vLLMは、LLMをAPIとして高効率に提供するinference and serving engineです。PagedAttentionは、動的に伸びるKV Cacheをlogical blockとphysical blockへ分け、block tableで結びます。これにより、requestごとの最大長を大きな連続領域として予約せず、離れたfree blockも利用できます。
ただし、block化だけがperformanceを決めるわけではありません。continuous batchingは実行枠をiterationごとに入れ替え、prefix cachingは共有promptのprefillを再利用し、kernelはAttention計算を効率化します。それぞれが別のbottleneckを担当します。
導入判断ではpaperの倍率をそのまま期待せず、自分のprompt/output分布とconcurrencyを再現してください。TTFT、ITL、throughput、GPU memory、preemption、error率を同時に測り、必要なlatencyとcapacityを満たす設定を選ぶことが重要です。



コメント