TT Lab
ブログ

한국어 English 日本語 中文

はじめる
ブログ

ブログ

LMCache: KV キャシを GPU の外へ、エンジン間へ

状態: ドラフト(2026-10-13)、一部実測。 実測値は、同じサイトの「トス ML/AI Infra 準備」第 8 回にて、8GB 級 GPU 1 枚、Qwen2.5-1.5B-Instruct、vLLM 0.29.0、LMCache 0.5.5(プロセス内モード)で測定した値を再引用しています。論文および公式ドキュメントは 2026-10-13 に直接開いて確認しました。LMCache の現在の推奨方式(MP モード)と大規模モデルの値は未実測です(イメージとモデルを新しくダウンロードする必要があるため承認待ち)。 2026-10-27 追加。 承認を得て、vLLM 0.32.0 公式イメージ(LMCache 0.5.6 がすでに含まれている)と Qwen3-0.6B で MP モードを測定しました。結果は第 9 節にあり、前節の 2026-10-13 の数値はそのまま残してあります。大規模モデルと複数サーバーについては依然として未実測です。 執筆ルール: 実測データを基本とし、測定できなかったものは「未実測」、確認できなかったものは「未確認」と表示します。

LMCache: KV キャシを GPU の外へ

目次

1. GPU 内キャッシュが持つ 2 つの限界

第 2 回のプレフィックスキャッシュは強力ですが、GPU メモリ内だけで動作します。そのため、2 つの物足りなさが残ります。

  1. 容量が足りなくなると削除されます。 削除されたプレフィックスが再び来ると、最初から計算し直します。
  2. インスタンスごとに個別です。 同じモデルを複数の GPU にデプロイした場合、隣のインスタンスが直前に計算した KV を使用できません。

1 つ目の制限がどれほど深刻か測定したことがあります。GPU の KV 容量(21,520 トークン)より大きな文書群(文書 10 個、1 文書あたり約 5,000 トークン、合計約 5万トークン)を順番に読み込ませ、 同じ順番で再度 読み込ませました。vLLM のみを使用した場合、2 回目の読み込みにおける最初のトークンまでの時間は 576ms で、初回(575ms)と同等であり、vLLM による強力なプレフィックスキャッシュのヒットは 0 でした。最も古いものから削除するキャッシュ(LRU)に循環しながらアクセスすると、容量が少しでも足りない場合、次の番に使う文書が常に直前に削除されているためです。

2. LMCache の構造

LMCache: KV 를 GPU 밖에 두고 다시 쓰기 vLLM 인스턴스 1 GPU 의 KV 블록 vLLM 인스턴스 2 GPU 의 KV 블록 KV 커넥터 KV 커넥터 LMCache 256토큰 청크 · 앞부분 해시가 열쇠 · 저장은 비동기 CPU 메모리 PCIe 실측 13.4GB/s 로컬 디스크(NVMe) 장치마다 다름 원격·공유 저장소 Redis·S3·Mooncake·NIXL 등 1GbE 실측 0.1GB/s 위로 갈수록 빠르고 작다 · 아래로 갈수록 크고 여러 엔진이 함께 쓴다 요청 하나가 들어오면 1 프롬프트를 256토큰씩 잘라 앞부분 해시를 만든다 2 빠른 계층부터 찾는다(대기 중에 미리 올려 두기도) 3 적중한 청크는 GPU 로 복사 → 그만큼 프리필 생략 4 나머지만 계산하고, 새 KV 는 뒤에서 내려 저장 인스턴스 2 도 같은 열쇠로 찾는다 → 엔진 사이 공유 앞부분이 아닌 조각은 일부 토큰만 다시 계산(CacheBlend)
図: LMCache のストレージ階層と再利用フロー

LMCache は、サービングエンジンの脇に配置される KV キャッシュ階層です。技術レポート(arXiv 2510.09665、2025-12 改訂版)および公式ドキュメント(2026-10-13 閲覧)で確認した構造は以下の通りです。

同じプレフィックスをキーとして使用するため、 インスタンス 1 が保存した KV をインスタンス 2 が見つけて使用できます。 同一ノードであれば MP サーバーの CPU メモリを、異なるノードであれば共有ストレージや RDMA を介して近隣のメモリを読み取る P2P 方式を使用します。

3. なぜ TTFT とスループットが向上するのか

核心は一言です。 再計算するプリフィルを、ストレージからの読み込みに置き換えます。

レポートの数値(8×H100 サーバー、Llama-3.1-8B・70B、Qwen2.5-72B など 6 つのモデル、CPU オフロード上限 500GB):

実験 条件 結果(最強のベースラインと比較)
マルチターン文書 Q&A、CPU オフロード(8.2 節) 入力約 1 万トークン(文書約 12 ページ + 質問)、出力最大 100 トークン、ベースラインは vLLM 0.10.2(GPU プレフィックスキャッシュ)・vLLM CPU オフローディング・商用製品 2 つ 1 リクエスト/秒における TTFT が 1.9〜8.1 倍小さく、ITL が 7〜92% 小さい、 同じ TTFT におけるスループットが 2.3〜14 倍
実際のサービス記録の再生(8.3 節) 2 社の入出力分布、CPU 500GB、ベースライン vLLM TTFTが3.7〜6.8倍小さく、ITLが19〜58%小さい
中央ストレージサーバー(8.4節) リモートサーバーと15Gbpsのリンク 同じTTFTでのスループットが1.3〜3倍

アブストラクトにはスループットが「最大15倍」と記載されていますが、結果の節で確認した最大値は8.2節の14倍でした。リモートストレージ側のメリットが1.3〜3倍とCPUメモリより小さいのはリンクが遅いためであり、次の節の損益分岐点と同じ話になります。

4. 実測値:576msから48.6msへ

1節の実験でLMCacheを有効にしました(CPUメモリの上限1.5GB、チャンク256)。環境は8GBクラスのノートPC用GPU、Qwen2.5-1.5B-Instruct(fp16)、vLLM 0.29.0、LMCache 0.5.5で、リクエストは1つずつ送信しました。

設定 · パス 最初のトークン p50 p95 パス全体
vLLMのみ · 初回 575.5ms 637.3ms 6.2秒
vLLMのみ · 再度 576.2ms 577.9ms 6.2秒
LMCache · 初回 597.8ms 676.2ms 6.5秒
LMCache · 再度 48.6ms 59.6ms 0.9秒

「再度」は同じ順序で再び読み込んだ2回目のパスです。

長さを変えてみると、コンテキスト長に応じてメリットが大きくなります(CPU 2GB、GPUから追い出されたドキュメントを再リクエスト)。

入力トークン 再計算 ロード 倍率
554 66ms 21ms 3.1倍
1,077 119ms 24ms 5.0倍
2,144 231ms 29ms 8.0倍
4,273 481ms 45ms 10.7倍
6,414 768ms 41ms 約19倍

再計算にかかる時間は長さにほぼ比例しますが、ロードにかかる時間はGPUで直接ヒットする場合(20〜32ms)よりも数ms余分にかかる程度で緩やかです。

容量は崖のようになっています。 同じ再読み込みをCPUの上限のみを変えて測定しました(ワーキングセットのKVは計算上で約1.49GB)。

CPU上限 再読み込み最初のトークン 外部ヒット
なし(vLLMのみ) 600ms —
0.5GB 620ms 0%
1.0GB 620ms 0%
1.5GB 39ms 98%
2.0GB 39ms 98%

ワーキングセットよりも少しでも小さければメリットは0になり、保存コストだけが残りました。1節と同様のLRU循環アクセスの罠です。現実のように一部のドキュメントが頻繁に読み込まれる分布(ジップ則、ドキュメント60個)では、小さなキャッシュも役に立ち、平均の最初のトークンがvLLMのみの278msから、CPU 1GBで174ms、2GBで63msに短縮されました。

この実験の限界:同時実行 1、1.5B モデル 1 つ、乱数単語で作った合成文書、1 回ずつ測定した値であり、今回の文書が勧める MP モードではなくインプロセスモードでした。

5. 損益分岐:読み込みが計算より安い場合のみ

다시 계산할까, 불러올까: 첫 토큰까지 걸린 시간(ms) Qwen2.5-1.5B · 8GB급 GPU · 토큰당 KV 28,672바이트 0 250 500 750 1000 0 2,000 4,000 6,000 입력 토큰 수 다시 계산(실측) 768ms CPU 에서 불러옴(실측) 41ms 1GbE 로 불러오기(계산) ↑ 재계산 실측 불러오기 실측 1GB/s 로 불러오기(계산) 1GbE 로 불러오기(계산) 손익 분기 대역폭 ≈ 28,672B ÷ 0.12ms ≈ 0.24GB/s (계산) 이보다 느린 길로 KV 를 옮기면 다시 계산하는 편이 빠르다
図:再計算と読み込みの時間の比較

読み込みが有利であるためには、 KV を転送する時間がその KV を再計算する時間より短い必要が あります。トークン n 個について書くと次のようになります。

n が両辺にあるため、固定遅延を除くと、 損益分岐帯域幅 = トークンあたりの KV バイト ÷ トークンあたりのプリフィル時間となります。上記の測定値(6,414 トークン 768ms、1 トークンあたり約 0.12ms)を当てはめると、28,672 バイト ÷ 0.12ms ≈ 0.24GB/s となります(計算)。

モデルと GPU が分岐を動かします。 プリフィルの計算量はトークンあたりおおよそ「2 × パラメータ数」FLOP であり、KV バイトは第 1 部の式のとおりです。両者の比(KV 1 バイトを節約したときに浮く計算量)を公開構成で計算すると次のようになります(アテンション計算は除外して概算)。

モデル トークンあたりのプリフィル計算 トークンあたりの KV KV 1 バイトあたりの計算
Qwen2.5-1.5B 約 3.1 GFLOP 28KiB 約 11 万 FLOP
Llama 3.1 8B 約 16 GFLOP 128KiB 約 12 万 FLOP
Qwen2.5-7B 約 15 GFLOP 56KiB 約 27 万 FLOP
Llama 3.1 70B 約 140 GFLOP 320KiB 約 43 万 FLOP

KV 1 バイトあたりの計算が大きいほど、読み込みが有利になります。GQA で KV を削減した大きなモデルほど「読み込む価値」が高くなります。逆に、 GPU が速いほど 再計算が速くなり、分岐帯域幅が上がります。同じストレージであっても、遅い GPU ではメリットになり、速い GPU ではデメリットになる可能性があります。

ヒット率にも分岐があります。 ヒットすると 527.6ms を節約でき(576.2 − 48.6)、ミスすると保存コスト 22.3ms が発生します(597.8 − 575.5)。ヒット確率 p において期待利益が 0 になる p は 22.3 ÷ (22.3 + 527.6) ≈ 4% です。これは本実験の値であり、一般的な定数ではありません。他の公開データの数値を当てはめると、20% 前後まで上がります。

6. 先頭以外の断片も再利用:CacheBlend、CacheGen

プレフィックスキャッシュは、 先頭から完全に一致する場合のみ 使用します。RAG のように複数の文書断片を毎回異なる順序で結合する場合、最初の断片以外はヒットしません。断片ごとに個別に作成した KV を単に繋ぎ合わせると、断片間のアテンション(後の断片が前の断片を見る処理)が抜け落ちて答えが間違ったものになります。

CacheBlend(Yao ら、EuroSys 2025)は、繋ぎ合わせた後に、 KV のズレが最も大きいトークンのみを選んで再計算します。レイヤーごとにそのようなトークンは 10〜15% ほどであり、10〜20% だけを再計算すれば十分でした。再計算を次のレイヤーの読み込みとオーバーラップさせて隠蔽します。結果として、全体再計算と比較して TTFT が 2.2〜3.3 倍削減され、スループットが 2.8〜5 倍向上し、品質(F1 および Rouge-L)の低下は 0.01〜0.03 以下でした(Mistral-7B・Yi-34B・Llama-70B、A40 2 基、NVMe 実測 4.8GB/s)。LMCache の MP サーバーは、 --engine-type blend でこの機能を有効にします。

CacheGen(Liu ら、SIGCOMM 2024)は、KV を転送するコスト自体を削減します。隣接するトークン間で KV が似ているという点を利用して差分のみを符号化し、前方のレイヤーは密に、後方のレイヤーは粗く量子化します。通常の量子化よりも KV が 3.5〜4.3 倍小さくなり、3Gbps リンクでの TTFT が量子化ベースラインより 3.2〜3.7 倍低くなり、精度低下は 2% 以内でした(Mistral-7B・Llama-34B・70B、A40 4 基)。帯域幅が低下した場合、チャンクごとに圧縮レベルを下げたり、 テキストを送信して再計算する方法に切り替えたり します。これは、第 5 節の損益分岐をチャンクごとにリアルタイムで計算しているようなものです。

7. プリフィル・デコード分離における KV 転送

KV を転送する能力は、別の用途にも使われます。第 4 部で取り上げる 分離サービングでは、プリフィル GPU で作成した KV をデコード GPU に送信する必要があります。LMCache の文書における分離構成では、プリフィル→デコード間の転送を NIXL(vLLM の NixlConnector)に任せ、vLLM の MultiConnector によって LMCache コネクタを統合し、リクエスト間の再利用を高めています。報告書によると、8K 入力・200 出力、NVLink の条件下で、vLLM 単体の分離構成よりも平均 TTFT が 1.53〜1.84 倍、平均 ITL が 1.12〜1.66 倍低かったと報告されています(8.5 節、モデル名は節内で確認できず)。

8. メリットがないケース

条件 理由 根拠
プロンプトが短い 再計算がすでに安いため節約効果が小さい。固定遅延のほうが大きい 実測:554 トークンで 3.1 倍、6,414 トークンで 19 倍
同じプレフィックスが再来しない ヒットしない場合、保存コストしか発生しない 実測:初回読み込み +4%
キャッシュ容量がワーキングセットより小さい(循環アクセス) LRU により次に使うものが常に削除される 実測:0.5・1.0GB でヒット率 0%
ストレージやネットワークが遅い 転送時間が再計算より長い 計算:1GbE はこのモデルの分岐 0.24GB/s を下回る。報告書 8.7 節
小規模なモデル、高速な GPU 再計算が速くなり、分岐帯域幅が上がる 計算(第 5 節の表)
GPU プレフィックスキャッシュで十分である ワーキングセットが GPU にすべて収まる場合、vLLM 単体でヒットする 実測:チャンクサイズのテストで vLLM 単体のみを使用した場合の値と同等だった
頻繁に読み込まれる文書が GPU に残る(ジップ則) 検索・保存コストのみが加算される 実測(2026-10-27、9-4 節):平均初トークン 17.9 → 約 20ms
文書がチャンク(256 トークン)より短い チャンクに満たない端数は読み込むことができない 実測(2026-10-27、9-3 節):510 トークンで 1.1 倍

9. 2026-10-27 MP モード再測定

第 4 節は、現在の文書で「レガシー」と表示されているインプロセスモードでした。同じ 8GB 級 GPU において推奨方式である MP モード(個別の lmcache server と LMCacheMPConnector)を再度測定しました。モデルとバージョンが異なるため第 4 節の数値と直接比較はしないでいただき、同日かつ同条件で測定した「vLLM 単独 / インプロセス / MP」の 3 行の間で比較してください。

9-1. 環境と方法

lmcache server --l1-size-gb 0.75 --eviction-policy LRU --port 5555 --http-port 8080
vllm serve Qwen/Qwen3-0.6B --disable-hybrid-kv-cache-manager \
  --kv-transfer-config '{"kv_connector":"LMCacheMPConnector","kv_role":"kv_both","kv_connector_extra_config":{"lmcache.mp.port":5555}}'

9-2. 基本的な効果:GPU にすべて収まらない文書群の再読み込み

GPU KV を 2,336 トークンに制限し、約 1,020 トークンの文書 4 つ(合計約 4,080 トークン)を同じ順序で 4 回読み込みました。

設定 初回パス(保存を含む) 再読み込み(2〜4 回目のパスの中央値) 文書あたりの読み込み済みトークン数
vLLM 単独 55.7ms 46.5ms 0
インプロセス(LMCacheConnectorV1、CPU 0.75GB) 71.3ms(+28%) 24.2ms(1.9 倍) 768
MP(LMCacheMPConnector、L1 0.75GB) 60.7ms(+9%) 32.9ms(1.4 倍) 768

9-3. 長さ別:再計算 vs 読み込み

GPU KV を 0.75GiB(7,020 トークン)に設定し、文書を 1 回読み込んで保存した後、vLLM の /reset_prefix_cache(開発モード)で GPU キャッシュのみをクリアして再度読み込みました。LMCache 側は文書ごとに管理 API を用いて L1 をクリアしました。文書の長さごとに 4 回のうち最初の 1 回を除外して中央値を使用しました。

入力トークン vLLM 単独 再計算 MP 読み込み 読み込み済みトークン数 倍率 GPU ヒット(参考)
約 510 32.2ms 29.8ms 256 1.1 倍 11.2ms
約 1,005 45.9ms 37.0ms 768 1.2 倍 12.0ms
約 2,010 98.3ms 46.3ms 1,792 2.1 倍 14.5ms
約 4,020 226.2ms 68.2ms 3,840 3.3倍 19.2ms
約6,030 390.9ms 91.3ms 5,888 4.3倍 23.8ms

第4節の1.5B実測(6,414トークンで約19倍)より倍率が小さくなります。このモデルは再計算が速くKVは大きいため、同じ長さで節約できる時間が少なく、転送すべきバイト数が多いためです。それでも、長さが長くなるほど倍率は大きくなる傾向は同じでした。

最初の試行から学んだこと。 L1を空けずに長さ別のテストを実行したところ、2,000トークン以上でほとんどヒットしませんでした。LMCacheのログに「Failed to batched allocate … no enough memory」という警告が蓄積され、指標 lmcache_mp_l1_allocation_failure_chunks_total{during="l1_store"} が増加しました。退避(Eviction)は、使用率がしきたり(デフォルト0.8)を超えると非同期で20%ずつ解放する方式であるため、大きなドキュメントが一度に入ってきたときに空きを待たずに 保存を破棄しました。 エラーは発生せずにヒットのみが消えるため、L1は一度に入ってくる最大ドキュメントよりも余裕を持たせて確保し、この指標を監視する必要があります。

9-4. 容量:崖の代わりに部分ヒット、そしてジップ分布の逆効果

循環アクセスは9-2と同じドキュメント4つ(ワーキングセット約0.46GB)、ジップ分布は約480トークンのドキュメント20個を120回(指数1.1)読み込みました。GPU KVは2,336トークンです。

設定 循環:再読み込み初トークン 循環:外部ヒットトークン ジップ:平均初トークン
vLLM単体 46.5ms 0 17.9ms
MP、L1 0.25GB 41.2ms 1,791 / 4,034(44%) 20.3ms
MP、L1 0.5GB 32.6ms 3,328 20.0ms
MP、L1 0.75GB 33.0ms 3,328 19.9ms

9-5. MPモードならではの利点

9-6. L2ローカルディスク階層と損益分岐点

L1を0.25GBと小さく設定し、ファイルシステムL2を追加しました(--l2-adapter '{"type":"fs","base_path":"…","use_odirect":true}'、ページキャッシュを経由しないO_DIRECT、コンテナの一時ディスク)。

項目 値
循環の再読み込み初トークン(L1 0.25GB + L2) 41.6ms(L2がない場合は41.9ms)
L1から押し出された1,000トークンのドキュメントをL2から再読み込み 54.6ms、55.4ms(768トークンを読み込み)
同じドキュメントをvLLM単体で再計算 45.9ms
L2読み込み速度(LMCache指標の平均) / dd O_DIRECT読み込み 秒速3.8GB / 秒速4.8GB
L1→GPU読み込み速度(LMCache指標、20回平均) 秒速11.8GB
GPU→L1保存速度(同指標、32回平均) 秒速12.6GB

9-7. 指標

MPサーバーのPrometheus指標は、今回の構成における管理HTTPポートの /metrics に出力されました(--prometheus-port で指定した9090は応答しない)。 lmcache_mp_ で始まる指標が48種類あり、運用時にまず確認すべきものは次のとおりです。

知りたいこと 指標 今回確認した値
ヒット率(チャンク単位) lookup_hit_tokens_total ÷ lookup_requested_tokens_total 長さ別テスト 83,456 ÷ 150,528
どの階層から来たか lookup_hit_l1_tokens_total, lookup_hit_l2_tokens_total L2テスト 5,120 / 1,024
L1の埋まり具合 l1_usage_ratio, l1_memory_usage_bytes 0.77(L1 0.25GB)
保存内容が破棄されているか l1_allocation_failure_chunks_total{during="l1_store"} L2テスト 6チャンク
転送速度 l0_l1_load_throughput_GB_per_second, l0_l1_store_…, l2_load_…(ヒストグラム) 9-6表
逐出 l1_evicted_chunks_total, l1_eviction_loop_triggered_total
再利用間隔 real_reuse_gap_seconds, l1_chunk_reuse_gap_seconds

vLLM側では vllm:prompt_tokens_by_source_total{source="external_kv_transfer"} がLMCacheから受け取ったトークン数です(この節の「外部ヒットトークン」)。管理HTTP APIには認証がないとヘルプに記載されており、デフォルトでは127.0.0.1のみでリスンします。パスのリストには /cache/clear, /reconfigure/…, /run_script があるため、このポートをポッド外へ公開すると、誰でもキャッシュをクリアしたり設定を変更したりできるようになります(/run_script のデフォルトの有効状態は未確認)。

9-8. まとめ

質問 2026-10-27 MPモード実測の結果
押し出された長い文書を素早く再読み込みするか はい。6,000トークン 391 → 91ms(4.3倍)
短い文書は メリットが小さい。チャンク(256トークン)に満たない端数は再計算され、1,000トークンで1.2倍
プロセス内モードとの比較 再読み込みは約9ms遅く、保存コストは小さい
エンジンを再起動してもキャッシュは残るか はい(MPのみ可能)
ホット文書がGPUに残る負荷 むしろ遅くなる(17.9 → 20ms)
ディスク階層 この小さなモデルでは再計算よりも遅い

一言で言うと。 試合の映像を見直すために、毎回新しく試合をやり直す必要はありません。録画したものを再生すればよいのです。ただし、録画データが遠くの倉庫にあり、道が混雑している場合は、いっそもう一試合やり直したほうが早いこともあります。どれくらい遠いのか、何回見直すのかを事前に測定しなければなりません。

参考

ログインするといいねできます

コメント

まだコメントはありません。

ログインするとコメントできます