
こんにちは。SB C&S の村上です。
この記事では、NVIDIA Dynamoが提供するKV Cache管理機能である「KVBM(KV Block Manager)」を利用し、KV CacheをGPUメモリだけでなくCPUメモリやストレージ側で保持する「KV Cache Offloading」を実際に動かしながら、その仕組みや動作について紹介します。
なお、本記事は連載となっているため、第5回までの記事もあわせてご覧ください。
【連載①】NVIDIA Dynamo 紹介:概要編
【連載②】NVIDIA Dynamo 紹介:3つの最適化機能
【連載③】NVIDIA Dynamo 紹介:Dynamoを用いた推論の実行
【連載④】NVIDIA Dynamo 紹介:KV Cache-Aware Routingを用いたルーティング最適化
【連載⑤】NVIDIA Dynamo 紹介:Disaggregated Servingを用いた分散推論
【連載⑥】NVIDIA Dynamo 紹介:KVBM / KV Cache Offloadingを用いたKVキャッシュの効率化←本記事
KVBM / KV Cache Offloadingとは
まず、KV Cacheについて簡単に振り返ります。
LLMの推論では、入力されたTokenをPrefillで処理し、その計算結果の一部をKV Cacheとして保持します。その後のDecodeでは、それまでに処理したTokenについて毎回最初から計算するのではなく、保持しているKV Cacheを参照しながら次のTokenを生成します。
KV Cacheを利用することで推論処理を効率化できますが、一方でKV CacheはGPUメモリを消費します。特に、長いコンテキストを扱う場合や、多数のリクエストを同時に処理する場合には、保持するKV Cacheの量も増加します。GPUメモリにはKV Cacheだけでなく、モデルのWeightや推論処理に必要なデータも配置されるため、KV Cacheとして利用できる容量には限りがあります。
多くの推論エンジンでは、起動時にKV Cache用のメモリ領域を確保し、その限られた領域内でKV Cacheを管理しています。そのため、長いコンテキストや多数のリクエストを処理すると、保持可能なKV Cacheの容量が不足し、利用頻度の低いKV Cacheを削除して新しいKV Cacheのための領域を確保する必要があります。
一度削除されたKV Cacheが再び必要になった場合には、該当する部分の計算をやり直す必要があります。このように、KV Cacheの入れ替えや再計算が頻発することで、推論効率が低下する場合があります。
そこで利用できるのが、NVIDIA DynamoのKVBM(KV Block Manager)です。
KVBMは、推論エンジンが利用するKV CacheをBlock単位で扱い、GPUメモリだけでなく、CPUメモリやストレージを含めた複数の階層で管理するための仕組みです。
そして、このKVBMを利用して、GPUメモリ上のKV CacheをCPUメモリやストレージへ退避させる動作をKV Cache Offloadingと呼びます。
KV Cache Offloadingを利用すると、GPU上ですぐに利用しないKV CacheをCPUメモリやストレージへ退避させることで、GPUメモリを空けながらKV Cacheを保持できます。
また、CPUメモリやストレージへ退避したKV Cacheが再び必要になった場合には、GPUメモリへ戻して再利用できます。
これにより、GPUメモリだけにKV Cacheを保持する場合と比べて、より多くのKV Cacheを保持しながら推論処理を行えるようになります。
KVBMの仕組み
まず通常のLLM推論では、推論時に生成されたKV CacheはGPUメモリ上に保持されます。
一方でKVBMでは、GPU上で保持しているKV CacheをGPUメモリだけでなく、CPUメモリやストレージを含めた以下4つの階層で管理します。

まず、推論で直接利用するKV CacheはG1のGPUメモリ上に配置されます。
G1はGPU上にあるため、推論処理から最も高速に参照できますが、利用できる容量には限りがあります。そこでKVBMでは、GPU上ですぐに利用しないKV CacheをG2のCPUメモリへ退避できます。
さらにKVBMでは、KV CacheをG3のローカルディスクやG4のリモートストレージへオフロードすることもできます。
G3ではWorkerに搭載されたローカルのNVMeストレージを利用します。
一方、G4ではNFSなどのネットワークファイルシステムやオブジェクトストレージなどのリモートストレージを利用することで、Worker単体のローカル容量だけに依存せず、より大きな保存領域を利用できます。
最もGPUに近いG1が高速で、G2、G3、G4と保存先を広げることで、より大きなKV Cache領域を利用できるようになります。
また、各階層間のデータ転送には、保存先に応じて異なる転送方式が利用されます。
利用する環境やストレージ構成が対応している場合には、GDS(GPUDirect Storage)を利用して、ストレージからGPUへ直接データを転送することも可能です。[
GPUDirect Storageについては以下記事にて解説しています
https://licensecounter.jp/engineer-voice/blog/articles/20260114_gpu_direct_storage.html
このようにKVBMでは、G1のGPUメモリ、G2のCPUメモリ、G3のローカルディスク、G4のリモートストレージをまたいでKV Cacheを保持・移動・再利用することで、GPUメモリだけに依存しないKV Cache管理を実現しています。
より詳しい仕様については以下公式ドキュメントをご参照ください。
https://docs.nvidia.com/dynamo/v1.4.0/knowledge-base/modular-components/kvbm/kvbm-design
環境準備
今回利用する基本的な環境は、これまでの連載で利用してきた環境と同様です。
ただし今回はKVBMによるKV Cache Offloadingそのものを確認するため、構成をシンプルにし、Workerは1台のみ利用します。
各サーバーのOSはUbuntu 24.04で、コンテナを実行できる状態まで準備済みとしています。
Workerとして動作させるWorker_1にはNVIDIA H200を搭載し、NVIDIA GPU DriverとNVIDIA Container Toolkitをインストール済みとしています。
コンテナイメージとしては、以下の2種類を利用します。
基本となるDynamoのコンテナイメージに加えて、FrontendとWorker間の、サービスディスカバリのためにetcdも使用します。なお、今回は推論エンジンに vLLM を用いるため、vLLM 用のコンテナイメージを使用します。
- Dynamo vLLM Runtime
- vLLMの推論エンジンを搭載したDynamoのコンテナイメージ
- nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0
- etcd
- 複数Worker構成におけるサービスディスカバリに使用
- quay.io/coreos/etcd:v3.5.14
利用するLLMに関しては事前にHugging FaceからQwen3-32Bをダウンロードして、マシン上の/opt/models配下に格納済みとなります。
https://huggingface.co/Qwen/Qwen3-32B
なお、以降の手順では各サーバーのIPアドレスを${ETCD_IP}、${FRONTEND_IP}、${WORKER1_IP}のように表記します。
実行時には、それぞれ利用する環境のIPアドレスに読み替えてください。
KVBMを有効化した環境構築
それではKVBMを利用した推論環境を構築していきます。
基本的には第3回で構築した通常のDynamo環境と同様ですが、Worker側で以下の設定を追加します。
- KVBMのDynamoConnectorを有効化
- CPU Cache領域を確保
- Disk Cache領域を確保
- KVBMメトリクスを有効化
今回の記事では、KVBMによるKV Cache Offloadingそのものの動作を確認するため、第4回のKV Cache-Aware Routingおよび第5回のDisaggregated Servingは利用しません。
また、検証環境の制約上GPU Direct Storageは使わない構成で進めていきます。
etcdの起動
etcdについてはこれまでと同じ構成を利用します。
etcd用のサーバー上で以下コマンドを実行します。
docker run --rm -d \
--name dynamo-etcd \
--network host \
quay.io/coreos/etcd:v3.5.14 \
/usr/local/bin/etcd \
--listen-client-urls=http://0.0.0.0:2379 \
--advertise-client-urls=http://${ETCD_IP}:2379
起動後、以下コマンドでetcdの状態を確認し、trueが返答されれば準備完了です。
curl -s http://${ETCD_IP}:2379/health
## 出力
{"health":"true","reason":""}
Frontendの起動
続いてFrontendを起動します。
Frontendに関しては、KVBM固有の設定はなく、純粋に起動を行えば問題ないです。
docker run --rm -d \
--name dynamo-frontend \
--network host \
-v /opt/models:/models:ro \
-e ETCD_ENDPOINTS=http://${ETCD_IP}:2379 \
-e DYN_REQUEST_PLANE=tcp \
nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0 \
python3 -m dynamo.frontend \
--discovery-backend etcd \
--model-name qwen3-32b \
--model-path /models/Qwen3-32B \
--http-port 8000
Frontendが正常に起動したことを確認します。
curl -s http://${FRONTEND_IP}:8000/health | jq
KVBMを有効化したWorkerの起動
続いてWorker_1上でKVBMを有効化したWorkerを起動します。
第3回で構築した通常のWorkerとの主な違いは、KVBMを利用するために、CPUメモリやローカルディスクをKV Cacheの保存先として利用する設定と、vLLMからKVBMを利用するためのConnector設定を追加している点です。
今回は、KVBMの動作を確認するため、G2のCPUメモリ(システムメモリ)として100GB、G3のローカルディスクとして200GBのCache領域を設定します。

docker run --rm -d \
--name dynamo-worker \
--gpus all \
--ipc=host \
--network host \
-v /opt/models:/models:ro \
-v /opt/kvbm-cache:/kvbm-cache \
-v /run/udev:/run/udev:ro \
-e ETCD_ENDPOINTS=http://${ETCD_IP}:2379 \
-e DYN_SYSTEM_PORT=8081 \
-e DYN_REQUEST_PLANE=tcp \
-e DYN_KVBM_CPU_CACHE_GB=100 \
-e DYN_KVBM_DISK_CACHE_GB=200 \
-e DYN_KVBM_DISK_CACHE_DIR=/kvbm-cache \
-e DYN_KVBM_METRICS=true \
-e DYN_KVBM_NIXL_BACKEND_GDS=false \
nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.4.0 \
python3 -m dynamo.vllm \
--model /models/Qwen3-32B \
--served-model-name qwen3-32b \
--discovery-backend etcd \
--max-model-len 40960 \
--kv-cache-memory-bytes 32G \
--dyn-reasoning-parser qwen3 \
--kv-transfer-config \
'{"kv_connector":"DynamoConnector","kv_role":"kv_both","kv_connector_module_path":"kvbm.vllm_integration.connector"}'
KVBMを利用するために追加した主な設定は以下となります。
- --kv-cache-memory-bytes 32G
- G1のGPUメモリ上で利用するKV Cache容量の指定
- 今回はOffloadを確認しやすくするため32GBに制限
- 未指定時はvLLMがGPUメモリ使用率をもとに自動決定
- DYN_KVBM_CPU_CACHE_GB=100
- G2のCPUメモリ(Host Memory)として利用するCache容量の指定
- GPU KV Cache以上の容量を確保することを推奨
- 今回はGPU 32GBに対して100GBを設定
- DYN_KVBM_DISK_CACHE_GB=200
- G3のローカルディスクとして利用するCache容量の指定
- CPU Cache以上の容量を確保することを推奨
- 今回はCPU 100GBに対して200GBを設定
- DYN_KVBM_DISK_CACHE_DIR=/kvbm-cache
- G3のローカルディスクとして利用するディレクトリの指定
- DYN_KVBM_METRICS=true
- KVBMのメトリクス出力の有効化
- OffloadやCache Hitなどの確認に利用
- DYN_KVBM_NIXL_BACKEND_GDS=false
- GDS Backendを明示的に無効化
- 本記事ではGDSを使わない構成のため無効化を設定
- --kv-transfer-config
- vLLMとKVBMを連携するKV Connectorの設定
- DynamoConnectorを利用
本記事上では、KV Cache Offloadingの動作を確認しやすくするため、G1のGPUメモリ上のKV Cache容量を32GBに制限しています。
そのうえで、G2のCPUメモリを100GB、G3のローカルディスクを200GBに設定し、下位の階層ほど大きなCache容量を持たせています。
本番環境では、利用するGPUのメモリ容量やモデルサイズ、同時実行数、CPUメモリ容量、ストレージ容量などに応じて各Cache容量を調整する必要があります。
KVBMでは、G2のCPU CacheはG1のGPU KV Cache以上、G3のDisk CacheはG2のCPU Cache以上とする構成が基本となります。今回は動作確認を目的として、比較的小さい値を設定しています。
それではFrontendへのWorkerの登録状況を確認します。
Worker_1の情報が表示されていれば、KVBMを利用した推論環境の準備は完了です。
curl -s http://${FRONTEND_IP}:8000/health | jq
KVBM / KV Cache Offloadingの動作確認
ここからは、実際に推論リクエストを送信し、KVBMによってKV CacheがGPUメモリ、CPUメモリ、Disk Cacheの間で移動することを確認します。
通常の短いプロンプトを数回送信するだけでは、GPU上のKV Cache領域に余裕があるため、十分なOffloadが発生しない場合があります。
そこで今回は、長い入力と複数の同時リクエストを組み合わせ、KV Cacheに比較的大きな負荷を与えることが可能なOSSとして公開されているLMCache/LMBenchmarkを利用して負荷をかけます。
https://github.com/LMCache/LMBenchmark
LMBenchmarkをGitHubから取得します。
git clone https://github.com/LMCache/LMBenchmark.git cd LMBenchmark
必要なPythonのパッケージなどについては、LMBenchmarkのREADMEに従って事前に準備しておきます。
それではベンチマークのスクリプトを実行し、負荷をかけていきます。
./run_benchmarks.sh \
qwen3-32b \
http://${FRONTEND_IP}:8000 \
./kvbm_test \
long-input \
8
スクリプトの各引数は以下となります。
- qwen3-32b
- ベンチマークで利用するモデル名
- ./kvbm_test
- ベンチマーク結果の保存先に利用する名前
- long-input
- Long Input / Short Outputのワークロードを利用
- 8
- QPS(Queries Per Second)を8に設定
それではKVBMの挙動を確認していきます。
今回Worker起動時にKVBM固有のPrometheus形式のメトリクス出力を有効にしているため、それを取得してグラフ化したもので確認します。
※メトリクスは6880番ポートの/metricsから取得できます
利用可能なメトリクスの一覧は以下をご参照ください
https://docs.nvidia.com/dynamo/v1.4.0/knowledge-base/modular-components/kvbm/kvbm-guide#available-metrics
結果の全体像としては以下図のようになりました。
まず、GPU上のKV Cache使用率を見ると、ベンチマーク開始後に大きく上昇し、80〜90%前後の高い使用率で推移しています。それとあわせて、GPUからCPUメモリへオフロードされたToken数も増加しています。
また、CPUメモリからローカルディスクへのOffloadについては、GPUからCPUメモリへのOffloadが開始した後、少し遅れて増加しています。
この結果からCPUメモリとローカルディスクへのオフロードは問題なく動作していることが伺えます。
続いて、オフロードしたものをGPUへ戻す処理も問題なく動き、GPUから溢れるKV Cacheの再利用が出来ていることが確認できます。
まとめ
本記事では、NVIDIA DynamoのKVBMを利用し、KV CacheをGPUメモリだけでなく、CPUメモリやローカルストレージへOffloadする構成を実際に作成しました。
KVBMでは、GPUメモリ上に保持しているKV CacheをCPUメモリやローカルストレージへ退避し、必要になった際には再びGPUへ戻して利用できます。これにより、GPUメモリだけにKV Cacheを保持する場合と比べて、より多くのKV Cacheを保持できるようになります。
特に、長いコンテキストを扱う場合や、多数のリクエストによってKV Cacheの使用量が大きくなる環境では、限られたGPUメモリを効率的に利用するための選択肢となります。
また、今回はKVBMによるKV Cache Offloadingそのものの動作確認に焦点を当てるためGDSは利用しませんでしたが、対応した環境ではGDSを利用する構成も可能です。
なお、KV Cache管理に関連する新しいプロジェクトとして、KVCR(KV Cache Runner)の開発も進められています。
本記事で紹介したKVBMとは異なるアプローチを含む新しいプロジェクトであり、今後のKV Cache管理技術の発展という観点でも注目されています。
KVCR GitHub:https://github.com/ai-dynamo/kvcr
これまでの連載では、KV Cache-Aware Routingによるリクエストルーティングの最適化、Disaggregated ServingによるPrefillとDecodeの分離、そして今回のKVBMによるKV Cacheの階層化について紹介しました。
NVIDIA Dynamoでは、このように複数の機能を組み合わせながら、LLM推論環境をさまざまな観点から最適化できます。
NVIDIAに関する情報発信はこちら
著者紹介
SB C&S株式会社
ICT事業本部 技術本部 第1技術統括部
第2技術部 1課
村上 正弥 - Seiya.Murakami -
VMware vExpert
