AI・信頼性・評価
vLLMとSGLangのプレフィックスキャッシュの違い:一致の単位と限界
- プレフィックスキャッシュ
- LLM推論
- AIエージェント
- vLLM
- SGLang
- RadixAttention
- KVキャッシュ
- PagedAttention
- プレフィル
- デコード
- システムプロンプト
- ブロック
- ページ
- radix tree
- ハッシュ
- LRU
- スループット
- prefix_match_unit
- cache_salt
- タイムスタンプ
- FCFS
- マルチテナント
- ツール定義
- 会話履歴
目次
AIエージェントやチャットのアプリを、自前の LLM推論のサーバーで動かす開発者を考える。このアプリは、要求のたびに長いシステムプロンプト、ツール定義、それまでの会話履歴を LLM に送る。LLM は文章をトークンという単位に分けて処理し、各トークンについて計算した中間結果を GPU のメモリに持つ。この中間結果を KVキャッシュと呼ぶ。入力のプロンプト全体から KVキャッシュを作る段階をプレフィル、その KVキャッシュを使って回答のトークンを一つずつ生成する段階をデコードと呼ぶ。
要求の間で同じになるプロンプトの先頭部分を、プレフィックスと呼ぶ。前の要求で作ったプレフィックスの KVキャッシュを次の要求で使い回せば、プレフィルのうち共通の部分の計算を省ける。この再利用がプレフィックスキャッシュである。ところが、推論エンジンが何をもって一致とみなすかを知らないと、開発者はプロンプトの先頭にタイムスタンプや要求ごとの ID を書き、再利用が起きない形にしてしまう。開発者が回答の生成も速くなると期待しても、時間の大半が回答の生成にかかる要求では効果が出ない。
本稿は、推論エンジンの vLLM と SGLang を例に取る。二つを選んだのは、どちらの作り手もプレフィックスキャッシュの設計と、その設計を選んだ理由を一次資料に書いているからだ。
vLLM と SGLang は、プロンプトの先頭がどこまで一致すれば計算を省き、その一致をどの単位で数えるのか。
一致した部分の KVキャッシュがまだ保持されていれば、プレフィックスキャッシュで省けるトークン数は、プロンプトの先頭から一致したトークン数を、エンジンが一致を数える単位で切り捨てた数で決まる。 vLLM は V1 から、SGLang は公開時の設計から、この再利用を既定で有効にしている。利用者が決めるのはキャッシュを使うかどうかではなく、先頭のトークンから同じ並びになるプロンプトの組み方である。一致を数える単位は、vLLM では 16 トークンのブロック、SGLang では既定で 1 トークンのページである。
共通の 1,000 トークンは、タイムスタンプの位置で 992 にも 0 にもなる
※ この節の数値は説明のための仮定で、測定値ではありません。
社内ヘルプデスクのチャットボットを考える。各要求は、1,000 トークンの共通のシステムプロンプトの後に、利用者ごとに違う 40 トークンの質問を続ける。一致を数える単位は、vLLM が 16 トークン、SGLang が 1 トークンとする。簡単のため、チャットのテンプレートが先頭に加えるトークンは数えない。次の表は、二つ目以降の要求でプレフィルを省けるトークン数を、プロンプトの組み方と回答の長さを変えた四つの状況で示す。
| 状況 | vLLM が | SGLang が | 理由 |
|---|---|---|---|
| 毎回 | 992 | 1,000 | 先頭から |
| 要求ごとの | 0 | 0 | 最初の |
| 要求ごとの | 992 | 1,000 | 共通部分が |
| 先頭は | 992 | 1,000 | 省けるのは |
主張を担うのは、vLLM の 992 である。1,000 ÷ 16 = 62 余り 8 なので、vLLM が一致として数えるのは完全な 62 ブロック、62 × 16 = 992 トークンである。端数の 8 トークンは質問と同じブロックに入り、質問が要求ごとに違うので、そのブロックは一致しない。vLLM はこの 8 トークンを質問と一緒に計算し直す。SGLang は 1 トークン単位で数えるので、1,000 トークンをすべて省く。共通部分の長さが変わっても、vLLM が省く数は、先頭から一致したトークン数を 16 で割り、切り捨てて 16 を掛ければ求まる。
再利用は両エンジンで既定になり、利用者が設計するのはプロンプトの並べ方になった
vLLM と SGLang は、どちらも共通の先頭部分の KVキャッシュを、利用者が設定しなくても再利用する。vLLM はオープンソースのエンジンで、KVキャッシュをブロックに分けて管理する PagedAttention の研究から生まれた1。vLLM の GitHub のリポジトリは 2023年に作られ、スター数は 93,934 である2。
SGLang は、LMSYS の研究者が発表したオープンソースのエンジンで、GitHub のリポジトリは 2024年に作られ、スター数は 37,217 である34。
vLLM の作り手は、V0 ではプレフィックスキャッシュを有効にすると CPU の負荷が大きくなることがあり、ヒット率が低いと性能がかえって下がったので、既定で無効にしていたと書く5。V1 の作り手は、追い出す KVキャッシュを定数時間で選べるデータ構造に変え、Python のオブジェクトを作る負荷を減らした5。作り手の測定では、ヒット率が 0% でもスループットの低下は 1% 未満で、ヒット率が高いと性能は数倍になった5。ブログの図によれば、測定は Llama 3.1 8B を H100 1 枚で動かし、入力 2,048・出力 128 トークンの要求で行った5。作り手は、この負荷の小ささを理由に V1 では既定で有効にした5。V1 の目標の一つに、作り手は機能を既定で有効にして設定を要らなくすることを挙げている5。現在のコードでも、プレフィックスキャッシュの設定の既定値は有効である6。設計の文書は、プレフィックスキャッシュがほとんど負担なしに使えてモデルの出力を変えないので、広く使われていると書く7。
SGLang の作り手は、最初から自動で再利用する設計にした。理由として、KVキャッシュを再利用できる既存のシステムも手動の設定や場当たりの調整を要し、手動で設定してもすべての場面に自動で対応できるシステムが無かったことを挙げる3。そこで SGLang は、生成を終えた要求の KVキャッシュを捨てず、プロンプトと生成結果の両方の KVキャッシュを radix tree に保持する3。この仕組みが RadixAttention で、作り手は論文でも定式化した8。現在のサーバーの引数でも、RadixAttention を止める設定の既定値は「止めない」である9。作り手は、キャッシュがヒットしない場合でも目立つ負荷は無かったので、RadixAttention を常に有効にしたと書く3。作り手は最大 5 倍のスループットも示すが、比べた相手は vLLM v0.2.5・guidance・TGI で、作り手は Llama-7B を A10G で、Mixtral-8x7B を 8 枚の A10G で動かして測った3。作り手はこの差を RadixAttention だけでなく、ほかの設計との組み合わせによるものとしている3。1% 未満と 5 倍は、どちらも作り手が公表した値であり、本稿は何も実行していない。
どちらのエンジンでも再利用を有効にする操作が要らなくなったので、利用者がヒット率を上げるために触れるのはプロンプトの並べ方になる。要求の間で変わらないシステムプロンプトとツール定義を先頭に置き、要求ごとに変わるタイムスタンプ・要求の ID・質問をその後ろに置く。SGLang の論文も、問いの文と指示の文の順序を入れ替えて共有できる先頭を長くする例を挙げ、この入れ替えは元の計算を厳密には保たない最適化だと断っている8。
ヒットには先頭のトークンからの一致が要り、vLLM はブロック単位、SGLang はトークン単位で数える
ヒットするのは先頭のトークンから一致した部分だけで、その長さを vLLM は 16 トークンのブロック単位で、SGLang は 1 トークン単位で数える。vLLM は、各ブロックの KVキャッシュを、ブロック内のトークンとそれより前のすべてのトークンからハッシュで求めた鍵で引く7。鍵の材料は、親のブロックのハッシュ値、ブロック内のトークン、LoRA の ID や画像などの入力のハッシュである7。親のハッシュ値を材料に含むので、最初のブロックの中で 1 トークンが変わると、それ以降のすべてのブロックの鍵が変わる。vLLM が保存するのは、トークンで埋まった完全なブロックだけである7。設定上のブロックの大きさの既定値は 16 トークンで、ハッシュの計算方式の既定は SHA-256 である6。ハッシュを使う方式を選んだ理由を、原典は書いていない7。
SGLang は、KVキャッシュを GPU 上でページに分けて置き、1 ページの大きさを 1 トークンにした3。現在のサーバーの引数でも、ページの大きさの既定値は 1 トークンである9。radix tree は、共通の先頭部分を持つトークン列を一つの枝にまとめる木で、作り手は先頭部分の検索・追加・追い出しを効率よく行えることを利点に挙げる3。木は根から先頭のトークンの順にたどるので、最初のトークンが違えば根の直後で枝が分かれ、その要求は何もヒットしない。
保持する範囲は、プロンプトだけでなく生成したトークンにも及ぶ。vLLM は、生成中の要求のトークンでブロックが埋まると、そのブロックもキャッシュに加える7。SGLang も、生成結果の KVキャッシュを radix tree に保持する設計である3。vLLM の文書は、効果のある使い方として、長い文書に繰り返し質問する場合と、複数回のやりとりを続ける会話を挙げる10。ここからは本稿の推論である。前の回答のトークン列が次の要求で同じ並びのまま会話履歴に入れば、その部分もヒットの対象になる。チャットのテンプレートや再トークン化で並びが変わると、その部分は一致しない。
メモリが足りなくなったときは、両エンジンとも LRU(最も長く使われていないものから追い出す方式)で KVキャッシュを捨てる。vLLM は、要求を終えたブロックを空きの列に逆順で加え、要求の最後のブロックから先に追い出す7。作り手の理由は、最後のブロックはハッシュの材料になるトークンが多く、ほかの要求に再利用されにくいことである7。SGLang の LRU は、木の葉から再帰的に追い出す3。現在の SGLang は、追い出し方式として既定の LRU のほかに LFU・SLRU・priority を選べる9。
一致の単位の違いが vLLM に課す損失は、要求 1 件あたり最大でブロックの大きさから 1 を引いた 15 トークンである。既定の設定では、この損失はなくならない。ただし、共通部分が 1,000 トークンなら 15 トークンは 1.5% にあたり、長いプレフィックスほど比率は下がる。
vLLM は、一致の単位を物理的なブロックの大きさから切り離す設定 prefix_match_unit を加えた10。コードの説明によれば、この設定はヒットが止まる最も細かいトークンの境界を決め、各ブロックの大きさを割り切れる値であれば、ブロックより細かくできる6。説明は、1,024 トークンのブロックを持つハイブリッドのモデルで 32 トークンにする例を挙げ、この設定が変えるのは一致の細かさだけで、状態を保存する頻度は変えないと書く6。文書がこの設定を説明する文脈は、ヒットからの再開がブロックの境界に限られる Mamba を含むハイブリッドのモデルである10。現在のコードは、KVキャッシュのグループが一つのモデルでは一致の単位がブロックの大きさと等しいことを前提にしているので、そうしたモデルでは一致をブロックより細かくできない11。
先頭のトークンから一致していなければヒットしないという条件は、両エンジンとも、鍵や木の経路がそれより前のすべてのトークンで決まる設計である限り恒久である。
再利用で縮むのはプレフィルだけで、回答を生成するデコードの時間は縮まない
プレフィックスキャッシュが縮めるのはプレフィルの時間だけで、デコードの時間は縮まない。vLLM の文書は、この機能が回答のトークンを生成する時間を減らさないので、長い回答の生成に大半の時間を使う要求や、既存の要求と共通の先頭部分を持たない要求では性能が上がらないと書く10。SGLang の論文も、複数ターンの会話では出力が短いほど効果が目立ち、出力が長いとデコードの時間が支配して速度はほとんど上がらないと報告している8。vLLM のブログが示した数倍も、出力 128 トークンに対して入力 2,048 トークンの負荷での値である5。ただし SGLang の論文は、KVキャッシュの共有でメモリの使用量が減り、一度に処理できる要求が増えることも最大のスループットを上げると書いている8。
SGLang の作り手は、LRU にキャッシュを考える順序付けを組み合わせてヒット率を上げると書いた3。ただし、現在のサーバーの引数では、要求を処理する順序の既定は到着順の FCFS である9。キャッシュと長く一致する要求を先に処理する lpm(最長プレフィックス一致)は、利用者が選ぶ設定である9。lpm を選んでも、待ち行列の要求が 128 件を超えると、SGLang は一致の計算と並べ替えを止めて FCFS に戻す12。コードの注釈は、待ち行列が長いときに一致の計算と並べ替えの負荷が大きいことを理由に挙げる12。
再利用が既定で起きるという向きには、利用者が自分で決める例外が二つある。一つ目は vLLM の V0 で、既定で無効だったので、V0 を使う利用者は自分で有効にする5。二つ目はマルチテナントの運用で、vLLM の cache_salt は最初のブロックのハッシュに加わり、同じ値を持つ要求だけが KVキャッシュを共有する7。作り手の目的は、応答時間の差から他の利用者のキャッシュの中身を推測する攻撃を防ぐことである7。運用者が利用者ごとに違う値を与えると、プロンプトが同じでも利用者の間ではヒットしない。
出典12件
-
Kwon ほか「Efficient Memory Management for Large Language Model Serving with PagedAttention」SOSP, 2023. https://arxiv.org/abs/2309.06180 — vLLM の出自となった研究。 ↩
-
vllm-project「vllm」GitHub(2026-10-17 取得). https://github.com/vllm-project/vllm — スター数 93,934 とリポジトリの作成日 2023-02-09。 ↩
-
Zheng ほか「Fast and Expressive LLM Inference with RadixAttention and SGLang」LMSYS Blog, 2024. https://lmsys.org/blog/2024-01-17-sglang/ — 手動の設定を要する既存システムへの対策として、1 トークンのページの KVキャッシュを radix tree で保持する設計。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10 ↩11
-
sgl-project「sglang」GitHub(2026-10-17 取得). https://github.com/sgl-project/sglang — スター数 37,217 とリポジトリの作成日 2024-01-08。 ↩
-
vLLM Team「vLLM V1: A Major Upgrade to vLLM’s Core Architecture」vLLM Blog, 2025. https://blog.vllm.ai/2025/01/27/v1-alpha-release.html — V0 で既定を無効にした理由、V1 で負荷を下げて既定で有効にした経緯、図の測定条件。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8
-
vLLM project「vllm/config/cache.py」GitHub(2026-10-17 取得). https://github.com/vllm-project/vllm/blob/main/vllm/config/cache.py — 既定のブロックが 16 トークン、既定で有効、ハッシュが SHA-256、prefix_match_unit が一致の境界を決めること。 ↩ ↩2 ↩3 ↩4
-
vLLM project「Prefix Caching」vLLM design docs(2026-10-17 取得). https://github.com/vllm-project/vllm/blob/main/docs/design/prefix_caching.md — 親のハッシュとブロック内のトークンで鍵を作る設計、完全なブロックだけの保存、LRU の追い出し順、cache_salt。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Zheng ほか「SGLang: Efficient Execution of Structured Language Model Programs」NeurIPS, 2024. https://arxiv.org/abs/2312.07104 — RadixAttention の定式化、長い出力ではほぼ速くならない実測、メモリ共有の効果、順序の入れ替えの例。 ↩ ↩2 ↩3 ↩4
-
SGLang「Server Arguments」SGLang docs(2026-10-17 取得). https://docs.sglang.io/advanced_features/server_arguments.html — ページの既定が 1 トークン、RadixAttention が既定で有効、順序付けの既定が FCFS であること。 ↩ ↩2 ↩3 ↩4 ↩5
-
vLLM project「Automatic Prefix Caching」vLLM docs(2026-10-17 取得). https://github.com/vllm-project/vllm/blob/main/docs/features/automatic_prefix_caching.md — 縮むのはプレフィルだけで、長い回答や共通部分の無い要求では速くならないという限界と、prefix_match_unit の説明。 ↩ ↩2 ↩3 ↩4
-
vLLM project「vllm/v1/core/kv_cache_coordinator.py」GitHub(2026-10-17 取得). https://github.com/vllm-project/vllm/blob/main/vllm/v1/core/kv_cache_coordinator.py — グループが一つのモデルでは一致の単位とブロックの大きさが等しいことを前提にする処理。 ↩
-
SGLang「schedule_policy.py」GitHub(2026-10-17 取得). https://github.com/sgl-project/sglang/blob/main/python/sglang/srt/managers/schedule_policy.py — 待ち行列が 128 件を超えると lpm の一致の計算を止めて FCFS に戻す処理。 ↩ ↩2
この記事はAIを使用して執筆されました。内容には誤りが含まれる可能性があります。事実や数字の出典は脚注から確かめられます。