Offload KV-cache prefix blocks from TPU HBM to a host-RAM pool on a single TPU
VM, using tpu-raiden as the device↔host DMA engine. One vLLM engine runs locally
and acts as both producer and consumer (kv_role=kv_both):
- On a prefix-cache miss, the engine computes the prefix and saves its full
KV blocks to the host pool (device→host,
d2h_auto_allocate). - On a later hit, the engine loads those blocks back from host RAM
(host→device,
h2d) instead of recomputing them.
This extends vLLM's on-device prefix cache with a much larger second tier in host
RAM. The connector lives in tpu-inference
(tpu_inference.offload.raiden_offload_connector); the physical transfers and the
host block pool are provided by tpu-raiden.
┌──────────────────────── single TPU VM ───────────────────────┐
client ───► │ vLLM engine :8000 (RaidenOffloadConnector, kv_both) │
(benchmark) │ │ ▲ │
│ d2h │ │ h2d ┌───────────────────────────────┐ │
│ (save)│ │ (load) ◄────► │ host-RAM KV pool (tpu-raiden)│ │
│ ▼ │ │ TPU_OFFLOAD_NUM_CPU_CHUNKS │ │
└───────────┴────────────────┴───────────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
- A single TPU VM with enough free chips for
TENSOR_PARALLEL_SIZE(default 8) and a python3.12 venv. - The venv must have
tpu_raiden,vllm, andtpu_inferenceimportable (see Step 1). These scripts do not activate any environment — activate your venv yourself before running them. - The benchmark (
benchmark.sh) uses tpu-inference'sbenchmark_serving.py, which importsevaluateandnltk(installed bysetup.sh). - Hugging Face access for the model (e.g.
export HF_TOKEN=...) if it is gated.
In an activated python3.12 venv:
-
Install tpu_raiden via one of:
# Build from source (generally available) -- from the repo root: ./build.sh jax # Or, where the wheel index is reachable (internal-only until PyPI publication): pip install tpu-raiden-jax --extra-index-url https://us-python.pkg.dev/cloud-tpu-inference-test/tpu-raiden/simple/
The run scripts pick up whichever install you did (see
raiden_env.sh). -
Install vllm + tpu_inference (the offload connector ships in tpu_inference), plus the benchmark client's
evaluate/nltkdeps, via this example's own installer:bash setup.sh
setup.shclones vLLM + tpu-inference at pinned commits (which include the RaidenOffloadConnector) into a hidden in-tree.src/and installs them editable. OverrideVLLM_COMMIT/TPU_INFERENCE_COMMITto use different versions.
bash run_offload.shThis starts the offload server, waits for it to be healthy, runs the benchmark
against it (:8000), prints the results and an offload-activity summary (the
External prefix cache hit rate is the fraction served from the host pool), then
tears the server down.
Logs land in tmp/<timestamp>/ (server.log, benchmark.log).
Assumes the local TPU is free — there is no pre-run TPU cleanup. The script stops only the server it launched.
All knobs are environment variables (with defaults); set them before run_offload.sh.
Model / serving (server.sh):
| Var | Default | Meaning |
|---|---|---|
MODEL |
Qwen/Qwen3-32B |
Served model (must match benchmark.sh). |
TENSOR_PARALLEL_SIZE |
8 |
TPU chips to shard across. |
GPU_MEMORY_UTILIZATION |
0.8 |
Device HBM fraction for the KV cache. |
PORT |
8000 |
Server port. |
Offloading (server.sh):
| Var | Default | Meaning |
|---|---|---|
TPU_OFFLOAD_NUM_CPU_CHUNKS |
8192 |
Host KV pool size, in blocks. |
TPU_OFFLOAD_DECODE_SAVE |
0 |
Also save KV produced during decode. |
Benchmark (benchmark.sh): RANDOM_PREFIX_LEN (32000), RANDOM_INPUT_LEN (128),
RANDOM_OUTPUT_LEN (128), NUM_PROMPTS (75), REQUEST_RATE (4). The shared prefix
is what gets offloaded and re-loaded across requests. The benchmark runs
tpu-inference's benchmark_serving.py (found via the installed tpu_inference;
override the path with BENCH_SCRIPT).
Example — smaller model on a single chip:
MODEL=Qwen/Qwen3-8B TENSOR_PARALLEL_SIZE=1 GPU_MEMORY_UTILIZATION=0.5 bash run_offload.sh- Pool sizing. The logical store capacity and the physical host pool are both
sized from
TPU_OFFLOAD_NUM_CPU_CHUNKS. Keep it comfortably above the benchmark's distinct-block working set so the pool serves hits without needing to evict. A larger pool also means a larger host allocation at startup (which can add a minute), so size it to your host RAM. - Files.
server.sh(engine + offload connector),benchmark.sh(shared-prefix benchmark),run_offload.sh(one-shot orchestrator),raiden_env.sh(makestpu_raidenimportable for both wheel and build-from-source installs).