たきびAIラボ TAKIBI · AI · LAB
💻AI開発 ハウツー 公開 2026.06.02

vLLM・SGLangのKVキャッシュ設定を本番チューニングする

prefix caching・量子化・ページドアテンション設定の実務ガイド

vLLM v0.8以降・SGLang最新版でKVキャッシュのGPUメモリ使用量を削減・最適化する実装ガイド。kv_cache_dtype(FP8/FP16)・--enable-prefix-caching・--gpu-memory-utilization・block_sizeの設定例と、SGLangのenable_radix_cache・mem_fraction_staticのチューニング手順を実装コード付きで解説する。

読了 約22分
vLLM・SGLangのKVキャッシュ設定を本番チューニングする:prefix caching・量子化・ページドアテンション設定の実務ガイド

vLLM や SGLang で LLM を本番運用していると、GPU メモリの大半を KV キャッシュが食い尽くすという場面に必ず遭遇する。モデルウェイトが 40GB を占めるなら、残りの 40GB を KV キャッシュにどう割り当てるか、どこまで圧縮できるか——この設定の差が、同時処理リクエスト数(スループット)とレイテンシに直結する。

本記事は、vLLM/SGLang の KV キャッシュ設定パラメータを実装特化で解説するガイドだ。理論的な最適化手法の全体像(4 戦略のサーベイ等)や FreeKV のような研究論文の深掘りではなく、今すぐ本番設定ファイルに書ける起動オプション・設定値・チューニング手順に絞る。speculative decoding によるレイテンシ削減(vllm_speculative_decoding_implementation_howto で扱うアプローチ)とは別軸で、GPU メモリ使用量そのものを削減・最適化することに特化する。

PagedAttention と KV キャッシュのメモリ問題

PagedAttention の仕組み

KV キャッシュを語る上で欠かせない基礎技術が PagedAttention だ。2023年9月に UC Berkeley の Woosuk Kwon らが arXiv に公開した論文「Efficient Memory Management for Large Language Model Serving with PagedAttention」(arXiv:2309.06180、2023-09-12)で提案された。

研究課題: LLM 推論では生成中のトークンに対してキー・バリューのテンソル(KV キャッシュ)を保持し続ける必要があり、従来の実装ではこれを連続したメモリ領域に確保していた。リクエストごとに最大シーケンス長のメモリを予め確保するため、実際の出力が短くても未使用領域が大量に生まれ(内部断片化)、また異なる長さのリクエストが混在するとメモリの連続領域確保が難しく外部断片化も発生していた。

提案手法: OS のページテーブル機構に着想を得た仮想メモリ管理。KV キャッシュを固定サイズの「ブロック」に分割し、物理ブロックを仮想ブロックにマッピングするブロックテーブルで管理する。メモリは実際に必要になったタイミングで確保され、複数シーケンスが同じプレフィックスを共有する場合はコピーオンライト(CoW)で物理メモリを共有する。

主な結果: 論文では A100 GPU 上での評価で、FasterTransformer や Aris Compute Library と比較して最大 24 倍のスループット改善を報告している(ただし比較対象やワークロードによって差は大きい)。本論文は vLLM の理論的基盤として位置付けられており、vLLM 公式ドキュメントでも PagedAttention への言及がある。

現在の KV キャッシュ問題

本番環境で GPU メモリが逼迫する典型的なシナリオは 3 つある。

  1. ロングコンテキスト: 128K トークンのリクエストでは KV キャッシュだけで数十 GB を消費する
  2. 高並列リクエスト: バッチサイズを増やすほど KV キャッシュが線形に増加する
  3. マルチモデル同時配信: 複数モデルを 1 台の GPU サーバで動かすと KV キャッシュ領域の取り合いになる

これらを解決するのが、vLLM と SGLang が提供する KV キャッシュ設定パラメータ群だ。

vLLM の主要 KV キャッシュ設定パラメータ

vLLM の KV キャッシュ関連設定は、大きく「メモリ割り当て比率」「データ型・量子化」「prefix caching」「ブロックサイズ」の 4 カテゴリに分かれる。

1. GPU メモリ割り当て比率:--gpu-memory-utilization

# デフォルト値:0.9(GPU 全メモリの 90% を vLLM が管理)
vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --gpu-memory-utilization 0.85

このパラメータは vLLM が GPU メモリ全体のうち何割を確保するかを制御する。モデルウェイトのロード後、残りのメモリが KV キャッシュブロックとして割り当てられる。

0.9(デフォルト)は積極的に使い切る設定だが、他のプロセスが同居する場合や OOM エラーが頻発する場合は 0.800.85 に下げる。逆に完全に vLLM 専用の GPU なら 0.95 まで上げることも可能だが、CUDA カーネルやライブラリが確保する分のマージンは必要なので 1.0 にはしない。

Python API から設定する場合:

from vllm import LLM, SamplingParams

llm = LLM(
    model="meta-llama/Llama-3.1-8B-Instruct",
    gpu_memory_utilization=0.85,
)

2. KV キャッシュのデータ型:kv_cache_dtype

FP16 や BF16 で推論しているモデルの KV キャッシュをより小さいデータ型に圧縮するのが kv_cache_dtype 設定だ。vLLM 公式の量子化ドキュメントでも推奨される設定の一つ。

# FP8 KV キャッシュ(デフォルトは auto = モデルと同じ dtype)
vllm serve meta-llama/Llama-3.1-70B-Instruct \
  --kv-cache-dtype fp8 \
  --gpu-memory-utilization 0.90
llm = LLM(
    model="meta-llama/Llama-3.1-70B-Instruct",
    kv_cache_dtype="fp8",
    gpu_memory_utilization=0.90,
)

auto vs fp8 の違い:

設定値説明メモリ削減効果品質影響
autoモデルと同じ dtype(BF16 等)なしなし
fp8FP8 に圧縮(H100/H200/A100 等で対応)約 50% 削減(BF16 比)微小な精度低下の可能性

FP8 KV キャッシュが有効に機能するには対応 GPU(H100、H200、A100 等)が必要。A10G や 3090 など対応していない GPU では fp8 を指定してもフォールバックする。まずは本番の開発環境で MMLU や MT-Bench などのベンチマークを回して品質への影響を確認してから本番適用する。

3. prefix caching で重複プレフィックスを再利用:--enable-prefix-caching

システムプロンプトが全リクエストで共通の RAG アプリや、同じドキュメントを何度も参照するエージェントで特に効果が大きい設定が --enable-prefix-caching だ。vLLM 公式の prefix caching ドキュメントで詳細が確認できる。

vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --enable-prefix-caching \
  --gpu-memory-utilization 0.90
llm = LLM(
    model="meta-llama/Llama-3.1-8B-Instruct",
    enable_prefix_caching=True,
)

仕組み: PagedAttention のブロック共有機能を利用し、同一プレフィックスを持つリクエストの KV キャッシュをブロック単位でハッシュ化して保持する。2 回目以降の同一プレフィックスは KV 計算をスキップし、キャッシュから直接取得する。

効果が出やすいワークロード:

  • 全リクエストで同じ長いシステムプロンプト(1,000〜4,000 トークン)を先頭に付けるケース
  • RAG で同じドキュメントチャンクを繰り返し参照するケース
  • チャット履歴が蓄積するマルチターン会話

効果が薄いワークロード:

  • リクエストごとにプレフィックスが完全に異なるケース(都度ランダムなコンテキスト)
  • バッチサイズが 1 で常時単発リクエストのケース

4. ブロックサイズの最適化:block_size

vLLM の KV キャッシュ管理単位は「ブロック」で、1 ブロックが何トークン分のキャッシュを格納するかを block_size で指定する。

vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --block-size 32 \
  --enable-prefix-caching

デフォルトは 16 トークン/ブロックだが、GPU アーキテクチャや使用パターンによって最適値が変わる。

block_size特徴
8ブロック粒度が細かい。短いリクエストが多い環境で断片化が少ない
16デフォルト。汎用的なバランス
32メモリアクセスの効率が上がる場合がある。H100 等の大容量 GPU に向く

prefix caching と組み合わせる場合、block_size が大きいほどキャッシュヒットの粒度も大きくなる(ブロック単位で一致が必要なため)。実際のユースケースに合わせてベンチマークしながら決める。

実装例:本番向け vLLM 起動コマンド

# Llama 3.1 70B をシングル A100 80GB で動かす例
vllm serve meta-llama/Llama-3.1-70B-Instruct \
  --gpu-memory-utilization 0.90 \
  --kv-cache-dtype fp8 \
  --enable-prefix-caching \
  --block-size 16 \
  --max-model-len 32768 \
  --tensor-parallel-size 1 \
  --port 8000
# Python API での設定
from vllm import LLM, SamplingParams

llm = LLM(
    model="meta-llama/Llama-3.1-70B-Instruct",
    gpu_memory_utilization=0.90,
    kv_cache_dtype="fp8",
    enable_prefix_caching=True,
    block_size=16,
    max_model_len=32768,
)

sampling_params = SamplingParams(temperature=0.7, max_tokens=512)
outputs = llm.generate(["こんにちは、今日の天気は?"], sampling_params)

SGLang の KV キャッシュ設定

SGLang は vLLM と同様に PagedAttention ベースの KV キャッシュ管理を採用しているが、設定パラメータ名が異なる。特に Radix Tree 構造を使った prefix caching(RadixAttention) が SGLang の特徴だ。

enable_radix_cache:prefix caching の有効化

SGLang の prefix caching は enable_radix_cache オプションで制御する。Radix Tree で共通プレフィックスを木構造として管理するため、複数のリクエストが共通の接頭辞を持つ場合の再利用効率が高い。

# SGLang サーバの起動例
python -m sglang.launch_server \
  --model-path meta-llama/Llama-3.1-8B-Instruct \
  --enable-radix-cache \
  --mem-fraction-static 0.85 \
  --port 30000
# Python から設定する場合(SGLang の Runtime API)
import sglang as sgl

runtime = sgl.Runtime(
    model_path="meta-llama/Llama-3.1-8B-Instruct",
    enable_radix_cache=True,
    mem_fraction_static=0.85,
)

mem_fraction_static:静的メモリの割り当て比率

vLLM の gpu_memory_utilization に相当するのが mem_fraction_static だ。モデルウェイト・KV キャッシュプール等に事前に確保する GPU メモリの割合を指定する。

# メモリ割り当てを調整した起動例
python -m sglang.launch_server \
  --model-path meta-llama/Llama-3.1-70B-Instruct \
  --mem-fraction-static 0.88 \
  --enable-radix-cache \
  --tp 4 \
  --port 30000
mem_fraction_static使用シナリオ
0.80〜0.85複数プロセスが GPU を共有する場合
0.85〜0.90標準的な本番設定(デフォルト相当)
0.90〜0.95vLLM 専用 GPU、最大スループット優先

prefix caching のキャッシュヒット率モニタリング

設定を変えても、実際にキャッシュが活用されているかどうかを確認しなければチューニングにならない。vLLM は Prometheus メトリクスを標準でエクスポートしており、キャッシュヒット率を追跡できる。

vLLM のメトリクスエンドポイント

vLLM は起動時に自動的に /metrics エンドポイントを公開する。

# vLLM を --enable-metrics オプション付きで起動(v0.8 以降はデフォルト公開)
vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --enable-prefix-caching \
  --port 8000

# Prometheus メトリクスの確認
curl http://localhost:8000/metrics | grep -i cache

出力に含まれる主要な KV キャッシュ関連メトリクス:

# 現在の KV キャッシュ使用率(0.0〜1.0)
vllm:gpu_cache_usage_perc
# prefix caching のヒット数(累積)
vllm:cache_hit_count_total
# prefix caching のミス数(累積)
vllm:cache_miss_count_total

Python でのヒット率計算

import requests

def get_cache_hit_rate(base_url: str = "http://localhost:8000") -> float:
    """vLLM Prometheus エンドポイントからキャッシュヒット率を計算する"""
    resp = requests.get(f"{base_url}/metrics")
    resp.raise_for_status()
    
    hit_count = 0
    miss_count = 0
    
    for line in resp.text.splitlines():
        if line.startswith("vllm:cache_hit_count_total"):
            hit_count = float(line.split()[-1])
        elif line.startswith("vllm:cache_miss_count_total"):
            miss_count = float(line.split()[-1])
    
    total = hit_count + miss_count
    if total == 0:
        return 0.0
    return hit_count / total

# 使用例
hit_rate = get_cache_hit_rate()
print(f"KV キャッシュヒット率: {hit_rate:.1%}")

ヒット率の目安と判断基準

ヒット率判断
70% 以上良好。prefix caching が効いている
40〜70%標準。ワークロード次第では十分
40% 未満キャッシュ効果が薄い。プレフィックスのパターンを見直す

ヒット率が想定より低い場合、まず確認すべき点:

  1. システムプロンプトの位置: プレフィックス一致はトークン列の先頭からの連続一致で判定する。システムプロンプトが毎回わずかに変わっていないか確認する
  2. リクエストの順序: バッチ処理で同じプレフィックスのリクエストをまとめて送ると効果が上がる
  3. block_size の影響: ブロック単位のハッシュ一致が必要なため、プレフィックス長がブロックサイズの倍数に近いほど効率が良い

複数モデル同時配信時のメモリ分割戦略

本番環境では複数のモデルを 1 台の GPU サーバで動かすケースがある。この場合、KV キャッシュのメモリ割り当てを明示的に調整する必要がある。

vLLM のマルチモデル設定

# GPU 1 枚に 2 つの vLLM サーバを立てる例(GPU メモリ 80GB を 2 分割)
# モデル 1:70% を使用
CUDA_VISIBLE_DEVICES=0 vllm serve meta-llama/Llama-3.1-8B-Instruct \
  --gpu-memory-utilization 0.45 \
  --enable-prefix-caching \
  --port 8000 &

# モデル 2:残りの約 45% を使用(少し余白を持たせる)
CUDA_VISIBLE_DEVICES=0 vllm serve google/gemma-2-9b-it \
  --gpu-memory-utilization 0.45 \
  --enable-prefix-caching \
  --port 8001 &

speculative decoding との組み合わせ時の KV キャッシュ影響

speculative decoding(ドラフトモデルによる投機的デコーディング)を有効にすると、ドラフトモデルの分だけ追加で KV キャッシュが必要になる。

# speculative decoding + prefix caching + FP8 KV キャッシュの組み合わせ例
vllm serve meta-llama/Llama-3.1-70B-Instruct \
  --speculative-model meta-llama/Llama-3.2-1B-Instruct \
  --num-speculative-tokens 5 \
  --kv-cache-dtype fp8 \
  --enable-prefix-caching \
  --gpu-memory-utilization 0.88

speculative decoding ではドラフトモデルと本体モデルがそれぞれ KV キャッシュを持つため、メモリ使用量が増加する。FP8 KV キャッシュや gpu_memory_utilization の調整で吸収するのが現実的なアプローチだ。

実務でのチューニング手順まとめ

実際の本番チューニングは次の順序で進めると効率がよい。

Step 1: ベースラインを計測する

# プロファイリング用に起動し、スループット・レイテンシ・メモリ使用率を記録
vllm serve <model> \
  --gpu-memory-utilization 0.90 \
  --port 8000

# 別ターミナルで負荷テスト(vLLM 付属のベンチマークツール)
python benchmarks/benchmark_throughput.py \
  --backend vllm \
  --dataset <dataset> \
  --model <model>

Step 2: prefix caching を有効化してヒット率を確認

vllm serve <model> \
  --enable-prefix-caching \
  --gpu-memory-utilization 0.90

# ヒット率が 50% 以上なら効果あり
curl http://localhost:8000/metrics | grep cache

Step 3: kv_cache_dtype を FP8 に変更して品質を確認

FP8 が使える GPU 環境であれば有効化し、ベンチマーク数値が許容範囲内かを確認する。

Step 4: gpu_memory_utilization を微調整

OOM が発生しない範囲で最大値を探す。0.900.920.95 と段階的に上げる。

Step 5: block_size を本番ワークロードに合わせる

短いリクエストが多いなら 16(デフォルト)、長いコンテキストが多いなら 32 を試す。

よくある質問(FAQ)

FAQ

よくある質問

クリックで展開。

vLLM の prefix caching はデフォルトで有効ですか?

vLLM v0.8 時点では --enable-prefix-caching は明示的に指定しないと有効になりません。将来のバージョンでデフォルト化される予定はありますが、2026年6月現在の安定版では手動で指定する必要があります。vLLM の公式ドキュメントで最新の設定を確認してください。

FP8 KV キャッシュは A100 で使えますか?

A100(SM80)では FP8 演算のネイティブサポートは限定的ですが、vLLM は軟化した FP8 対応を提供しています。H100(SM90)以降では最も効果的に動作します。お使いの GPU で vllm serve --kv-cache-dtype fp8 を試し、エラーが出る場合や精度低下が大きい場合は auto に戻してください。

SGLang の enable_radix_cache と vLLM の enable_prefix_caching の違いは?

両者とも「同じプレフィックスの KV キャッシュを再利用する」機能ですが、SGLang の RadixAttention は Radix Tree(トライ木)で管理するため、複数リクエストが長い共通プレフィックスを持つ場合の効率が理論上高くなります。実際の効果はワークロードに依存するので、同一環境でベンチマークを取って比較するのが確実です。

キャッシュヒット率が低いときにまず確認することは?

最初に確認するのは「システムプロンプトが本当に毎回同じトークン列になっているか」です。空白や改行の差異など、見た目は同じでもトークン化結果が異なるケースがあります。次に「block_size の倍数にプレフィックス長が近いか」を確認します。prefix caching はブロック単位(デフォルト 16 トークン)でハッシュ照合するため、プレフィックスが 16、32、48 トークンのように block_size の倍数であると効率が最大化されます。

複数の最適化を同時に有効にしても問題ありませんか?

--enable-prefix-caching + --kv-cache-dtype fp8 + --gpu-memory-utilization の調整は基本的に組み合わせ可能です。ただし speculative decoding も同時に使う場合、ドラフトモデル分のメモリが追加で必要になるため、gpu_memory_utilization を少し下げて OOM を防ぐ必要があります。設定変更は 1 つずつ適用してベンチマークを取り、複合効果を把握してから全部まとめて有効化するのが安全な進め方です。

まとめ

vLLM/SGLang の KV キャッシュ設定は、GPU メモリ使用量とスループットに直結する重要なチューニングポイントだ。

  • --gpu-memory-utilization / mem_fraction_static: まず最初に調整する基本設定。0.850.90 の範囲で OOM が出ない最大値を探す
  • --enable-prefix-caching / --enable-radix-cache: システムプロンプトが共通のワークロードで即効性がある。ヒット率を Prometheus で確認する習慣をつける
  • --kv-cache-dtype fp8: H100/A100 以上の環境で約 50% のメモリ削減が見込める。必ず品質検証を先に行う
  • --block-size: 多くの場合はデフォルト(16)で問題ない。ロングコンテキストが多い環境では 32 を試す

理論的な KV キャッシュ最適化手法(量子化・エビクション・マージ・プレフィル再利用の 4 戦略)の全体像や、FreeKV のような最新研究については別記事で扱うが、まず本記事の設定パラメータを一通り試すことで、追加のハードウェアコストなしに GPU メモリ効率を大幅に改善できる。

関連記事

参考リンク

次に読むおすすめ

本記事で紹介した KV キャッシュ設定は実務の出発点だ。vLLM/SGLang の設定チューニングを含む LLM 本番運用の全体像を、実際に複数の AI ツールを使い込んだ視点でまとめた独自コンテンツも合わせてチェックしてほしい。

noteで続きを読む — AI ツール活用の実務知見を note で継続的に発信している。