TT Lab
Blog

한국어 English 日本語 中文

Get started
Blog

Blog

LMCache: Taking KV Cache Out of the GPU and Across Engines

Status: Draft (2026-10-13), partial measurements. Measurements re-cite values measured with an 8GB-class GPU, Qwen2.5-1.5B-Instruct, vLLM 0.29.0, and LMCache 0.5.5 (in-process mode) from Part 8 of "Toss ML/AI Infra Preparation" on the same site. The paper and official documentation were opened and verified directly on 2026-10-13. LMCache's currently recommended approach (MP mode) and the values for larger models are unmeasured (awaiting approval because new images and models need to be downloaded). Added on 2026-10-27. Upon approval, we measured MP mode using the official vLLM 0.32.0 image (which already includes LMCache 0.5.6) and Qwen3-0.6B. The results are in Section 9, while the 2026-10-13 figures from the preceding sections have been left as-is. Large models and multi-server setups remain unmeasured. Writing rules: Measured data is the baseline; unmeasured items are marked as "Unmeasured", and unverified items are marked as "Unverified".

LMCache: Moving KV Cache Out of the GPU

Table of Contents

1. Two Limitations of Cache Inside the GPU

The prefix cache from Part 2 is powerful, but it only works inside GPU memory. Because of this, it has two regrettable limitations.

  1. It gets evicted when space is tight. When an evicted prefix reappears, it is computed from scratch.
  2. It is isolated per instance. When the same model is deployed across multiple GPUs, neighboring instances cannot use the KV just computed by another instance.

We once measured how painful the first limitation is. A document bundle (10 documents, about 5,000 tokens each, totaling about 50,000 tokens) larger than the GPU's KV capacity (21,520 tokens) was fed sequentially and then read again in the same order . When using vLLM alone, the first token latency of the second read was 576ms, identical to the first (575ms), and vLLM's strong prefix cache hit rate was 0. This is because under a least-recently-used (LRU) eviction policy with cyclic access, even a slight shortage in space causes the document needed in the next turn to always have been just evicted.

2. Architecture of 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)
Figure: LMCache's Storage Hierarchy and Reuse Flow

LMCache is a KV cache tierattached alongside the serving engine. The architecture verified in the technical report (arXiv 2510.09665, Dec 2025 revision) and official documentation (viewed 2026-10-13) is as follows.

Because the same prefix is used as a key, KV saved by instance 1 can be found and used by instance 2 . If they are on the same node, it uses the CPU memory of the MP server; if on different nodes, it uses a P2P approach reading neighbor memory via shared storage or RDMA.

3. Why TTFT and Throughput Improve

The core can be summarized in one line: Replacing recomputed prefills with storage retrieval.

Figures from the report (8×H100 server, six models including Llama-3.1-8B/70B and Qwen2.5-72B, CPU offload limit 500GB):

Experiment Condition Result (vs. strongest baseline)
Multi-turn document QA, CPU offload (Section 8.2) Query ~10k tokens (~12 pages of document + question), output up to 100 tokens, baselines: vLLM 0.10.2 (GPU prefix cache), vLLM CPU offloading, two commercial products 1.9–8.1x smaller TTFT at 1 req/s, 7–92% smaller ITL, 2.3–14x throughput at the same TTFT
Real service trace replay (Section 8.3) I/O distribution of two companies, CPU 500GB, baseline vLLM 3.7–6.8x smaller TTFT, 19–58% smaller ITL
Central storage server (Section 8.4) Remote server and 15Gbps link 1.3–3x throughput at the same TTFT

The abstract mentions throughput "up to 15x", but the maximum confirmed in the results section was 14x from Section 8.2. The remote storage benefit being 1.3–3x, smaller than CPU memory, is because the link is slower, which is the same story as the break-even point in the next section.

4. Directly Measured Values: From 576ms to 48.6ms

We turned on LMCache for the experiment in Section 1 (CPU memory limit 1.5GB, chunk 256). The environment was an 8GB laptop GPU, Qwen2.5-1.5B-Instruct (fp16), vLLM 0.29.0, LMCache 0.5.5, with requests sent one by one.

Setting / Pass First Token p50 p95 Total Pass
vLLM only · First 575.5ms 637.3ms 6.2s
vLLM only · Again 576.2ms 577.9ms 6.2s
LMCache · First 597.8ms 676.2ms 6.5s
LMCache · Again 48.6ms 59.6ms 0.9s

"Again" refers to the second pass read again in the same order.

Varying the length shows that the benefit increases with context length (CPU 2GB, re-requesting documents evicted from the GPU).

Input Tokens Recompute Loaded Ratio
554 66ms 21ms 3.1x
1,077 119ms 24ms 5.0x
2,144 231ms 29ms 8.0x
4,273 481ms 45ms 10.7x
6,414 768ms 41ms About 19x

While recomputation time is almost proportional to length, loading time grows gently, taking only a few ms longer than a direct GPU hit (20–32ms).

Capacity is a cliff. We measured the same re-read while varying only the CPU limit (the working set's KV was about 1.49GB by computation).

CPU Limit Re-read First Token External Hit
None (vLLM only) 600ms —
0.5GB 620ms 0%
1.0GB 620ms 0%
1.5GB 39ms 98%
2.0GB 39ms 98%

Even if it was slightly smaller than the working set, the benefit was 0, leaving only the storage cost. This is the trap of LRU cyclic access, same as in Section 1. Under a realistic distribution where some documents are read frequently (Zipfian distribution, 60 documents), even a small cache helped, reducing average first token latency from 278ms with vLLM alone to 174ms for CPU 1GB and 63ms for CPU 2GB.

Limitations of this experiment: 1 concurrency, a single 1.5B model, synthetic documents made of random words, single-measurement values, and using the in-process mode rather than the MP mode recommended by the current documentation.

5. Break-even: When loading is cheaper than recomputing

다시 계산할까, 불러올까: 첫 토큰까지 걸린 시간(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 를 옮기면 다시 계산하는 편이 빠르다
Figure: Time comparison between recomputation and loading again

For loading to be beneficial, the time to transfer the KV cache must be shorter than the time to recompute that KV cache. For n tokens, it can be written as follows:

Since n is on both sides, subtracting the fixed latency gives: Break-even bandwidth = KV bytes per token ÷ prefill time per tokenPlugging in the actual measurements above (6,414 tokens in 768ms, approximately 0.12ms per token), we get 28,672 bytes ÷ 0.12ms ≈ 0.24 GB/s (calculated).

Models and GPUs shift the break-even point. The prefill computation amount is roughly \u201c2 \u00d7 number of parameters\u201d FLOPs, and the KV bytes follow the formula from Part 1. Calculating their ratio (the computation saved by saving 1 byte of KV) for public configurations yields the following (excluding attention calculations for estimation):

Model Prefill Computation per Token KV per Token Computation per 1 Byte of KV
Qwen2.5-1.5B ~3.1 GFLOPs 28 KiB ~110k FLOPs
Llama 3.1 8B ~16 GFLOPs 128 KiB ~120k FLOPs
Qwen2.5-7B ~15 GFLOPs 56 KiB ~270k FLOPs
Llama 3.1 70B ~140 GFLOPs 320 KiB ~430k FLOPs

The larger the computation per 1 byte of KV, the more advantageous loading becomes. Larger models that reduce KV via GQA have a higher \u201cvalue of loading.\u201d Conversely, the faster the GPU, the faster recomputation becomes, which raises the break-even bandwidth. Even with the same storage, it might be a win on a slow GPU and a loss on a fast GPU.

Hit rate also has a break-even point. A hit saves 527.6ms (576.2 \u2212 48.6), while a miss incurs a storage cost of 22.3ms (597.8 \u2212 575.5). The probability p at which the expected gain becomes zero is 22.3 \u00f7 (22.3 + 527.6) \u2248 4%. This is specific to this experiment and not a universal constant. Plugging in numbers from other public sources raises it to around 20%.

6. Rewriting non-prefix chunks: CacheBlend, CacheGen

Prefix caches are used only when they match from the very beginning . If you concatenate multiple document chunks in a different order every time like in RAG, only the first chunk will hit. If you simply stitch together KV caches created separately for each chunk, the attention between chunks (later chunks looking at earlier chunks) is lost, yielding incorrect answers.

CacheBlend(Yao et al., EuroSys 2025) stitches them together and then recomputes only the tokens where the KV cache deviates the most. Such tokens accounted for about 10\u201315% per layer, and recomputing only 10\u201320% was sufficient. The recomputation is overlapped and hidden behind the loading of the next layer. Results compared to full recomputation: TTFT reduced by 2.2\u20133.3x, throughput increased by 2.8\u20135x, and quality drop (F1/Rouge-L) was below 0.01\u20130.03 (Mistral-7B \u00b7 Yi-34B \u00b7 Llama-70B, dual A40s, measured NVMe 4.8 GB/s). LMCache's MP server enables this method via --engine-type blend .

CacheGen(Liu et al., SIGCOMM 2024) reduces the KV transfer cost itself. It exploits the similarity of KV caches between neighboring tokens by encoding only the differences, quantizing earlier layers densely and later layers sparsely. The KV cache became 3.5\u20134.3x smaller than standard quantization, TTFT was 3.2\u20133.7x lower than the quantization baseline on a 3Gbps link, and accuracy loss was within 2% (Mistral-7B \u00b7 Llama-34B \u00b7 70B, 4x A40s). When bandwidth drops, it lowers the compression level per chunk or switches to sending text for recomputation. This is essentially calculating the break-even point of Section 5 in real time for each chunk.

7. KV transfer in disaggregated prefill-decode architectures

The ability to move KV caches is also used elsewhere. Disaggregated serving, which we will see in Part 4, requires sending the KV cache generated by the prefill GPU to the decode GPU. The disaggregated setup in the LMCache documentation delegates prefill-to-decode transfer to NIXL (part of vLLM's NixlConnector) and couples it with LMCache connectors via vLLM's MultiConnector to further enhance reuse between requests. The report states that under 8K input, 200 output, and NVLink conditions, average TTFT was 1.53\u20131.84x lower and average ITL was 1.12\u20131.66x lower than vLLM's native disaggregated setup (Section 8.5, model name not verified within the section).

8. When is there no benefit?

Condition Why Basis
Prompt is short Recomputation is already cheap, so there is little to save. Fixed latency dominates. Measured: 3.1x at 554 tokens, 19x at 6,414 tokens
The same prefix does not recur Without hits, you only pay storage costs. Measured: First read +4%
Cache capacity is smaller than the working set (cyclic access) LRU constantly evicts what will be used next. Measured: 0% hit rate at 0.5 and 1.0 GB
Storage/network is slow Transfer time is longer than recomputation. Calculated: 1GbE is below this model's break-even point of 0.24 GB/s. Report Section 8.7
Small model, fast GPU Recomputation is fast, raising the break-even bandwidth. Calculated (Section 5 table)
GPU prefix cache is sufficient If the working set fits entirely in the GPU, vLLM alone achieves hits. Measured: Chunk size tests matched values using vLLM alone
Frequently read documents remain on the GPU (Zipfian distribution) Only lookup and storage overheads are added. Measured (2026-10-27, Section 9-4): Average time to first token 17.9 \u2192 ~20ms
Document is shorter than a chunk (256 tokens) Tails shorter than a chunk cannot be loaded. Measured (2026-10-27, Section 9-3): 1.1x at 510 tokens

9. Re-measurement of MP mode on 2026-10-27

Section 4 used the in-process mode now labeled as \u201cLegacy\u201d in the documentation. We re-measured the recommended MP mode (separate lmcache server and LMCacheMPConnector) on the same 8GB-class GPU. Because the model and version differ, do not directly compare these numbers with Section 4; instead, compare the three lines measured on the same day under the same conditions: \u201cvLLM standalone / in-process / MP.\u201d

9-1. Environment and Methodology

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. Basic effect: Re-reading document bundles that do not fit entirely in the GPU

GPU KV was constrained to 2,336 tokens, and four documents of about 1,020 tokens each (totaling about 4,080 tokens) were read four times in the same order.

Configuration First pass (including storage) Re-read (median of 2nd\u20134th passes) Loaded tokens per document
vLLM standalone 55.7 ms 46.5 ms 0
In-process (LMCacheConnectorV1, CPU 0.75 GB) 71.3 ms (+28%) 24.2 ms(1.9x) 768
MP (LMCacheMPConnector, L1 0.75 GB) 60.7 ms (+9%) 32.9 ms(1.4x) 768

9-3. By length: Recomputation vs. loading

GPU KV was set to 0.75 GiB (7,020 tokens), a document was read once and stored, and then vLLM's /reset_prefix_cache(development mode) was used to clear only the GPU cache and re-read it. For LMCache, L1 was cleared per document using the management API. Excluding the first of four runs for each document length, the median was used.

Input Tokens vLLM Standalone Recomputation MP Loaded Loaded Tokens Ratio GPU Hit (Reference)
~510 32.2 ms 29.8 ms 256 1.1x 11.2 ms
~1,005 45.9 ms 37.0 ms 768 1.2x 12.0 ms
~2,010 98.3 ms 46.3 ms 1,792 2.1x 14.5 ms
~4,020 226.2 ms 68.2 ms 3,840 3.3x 19.2ms
about 6,030 390.9ms 91.3ms 5,888 4.3x 23.8ms

The magnification is smaller than the 1.5B actual measurement in Section 4 (about 19x at 6,414 tokens). This is because this model has fast recalculation and a large KV, so the time saved at the same length is small while the bytes to move are large. However, the pattern of the magnification increasing with length remained the same.

Lessons learned from the first attempt. When running tests by length without clearing L1, most requests did not hit at 2,000 tokens or more. The LMCache logs accumulated "Failed to batched allocate … no enough memory" warnings, and the metric lmcache_mp_l1_allocation_failure_chunks_total{during="l1_store"} increased. Since eviction works by asynchronously clearing 20% when the usage exceeds the threshold (default 0.8), when a large document comes in all at once, it drops the storage instead of waiting for space to become available. Because no errors occur and only hits disappear, L1 should be set generously larger than the largest document expected to arrive at once, and this metric must be monitored.

9-4. Capacity: Partial Hits Instead of a Cliff, and the Backfire of Zipfian Distribution

For cyclic access, 4 documents identical to 9-2 (working set approx. 0.46GB) were used, and for Zipfian distribution, 20 documents of approx. 480 tokens each were read 120 times (exponent 1.1). The GPU KV is 2,336 tokens.

Configuration Cycle: Re-read First Token Cycle: External Hit Token Jeep: Average First Token
vLLM standalone 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. Benefits Exclusive to MP Mode

9-6. L2 Local Disk Tier and Break-Even Point

L1 was kept small at 0.25GB, and an L2 file system was attached(--l2-adapter '{"type":"fs","base_path":"…","use_odirect":true}', O_DIRECT bypassing the page cache, container's temporary disk).

Item Value
Circular re-read time to first token (L1 0.25GB + L2) 41.6ms (41.9ms without L2)
Re-reading a 1,000-token document evicted from L1 into L2 54.6ms, 55.4ms (768 tokens loaded)
Recomputing the same document with vLLM alone 45.9ms
L2 read speed (LMCache metric average) / dd O_DIRECT read 3.8GB per second / 4.8GB per second
L1→GPU load speed (LMCache metric, average of 20 runs) 11.8GB per second
GPU→L1 save speed (same metric, average of 32 runs) 12.6GB per second

9-7. Metrics

The Prometheus metrics for the MP server in this setup appeared on the management HTTP port at /metrics (--prometheus-port given via did not respond). lmcache_mp_ There were 48 types of metrics starting with , and the ones to look at first in operations are as follows:

Question Metric Value Observed This Time
Hit rate (chunk unit) lookup_hit_tokens_total ÷ lookup_requested_tokens_total Test by length: 83,456 ÷ 150,528
Which tier did it come from? lookup_hit_l1_tokens_total, lookup_hit_l2_tokens_total L2 test: 5,120 / 1,024
How full is L1? l1_usage_ratio, l1_memory_usage_bytes 0.77 (L1 0.25 GB)
Is storage being evicted? l1_allocation_failure_chunks_total{during="l1_store"} L2 test: 6 chunks
Transfer speed l0_l1_load_throughput_GB_per_second, l0_l1_store_…, l2_load_…(Histogram) Table 9-6
Eviction l1_evicted_chunks_total, l1_eviction_loop_triggered_total
Reuse interval real_reuse_gap_seconds, l1_chunk_reuse_gap_seconds

On the vLLM side, vllm:prompt_tokens_by_source_total{source="external_kv_transfer"} is the number of tokens received from LMCache ("external hit tokens" in this section). The documentation states that the management HTTP API has no authentication and is bound only to 127.0.0.1 by default. Since the route list includes /cache/clear, /reconfigure/…, /run_script , exposing this port outside the pod allows anyone to clear the cache or change configurations (default activation status of/run_script unverified).

9-8. Summary

Question Answers from 2026-10-27 MP Mode Measurement
Are evicted long documents re-read quickly? Yes. 6,000 tokens: 391 → 91 ms (4.3x)
For short documents, Gains are small. Tails shorter than a chunk (256 tokens) are recomputed; 1.2x at 1,000 tokens.
Compared to in-process mode Re-reading is about 9 ms slower, and storage overhead is small.
Does the cache persist if the engine is restarted? Yes (MP only)
Overhead of keeping hot documents on the GPU Actually slower (17.9 → 20 ms)
Disk tier Slower than recomputation for this small model

Titi's Word. You don't need to replay a game from scratch every time you want to watch the footage again. You can just play the recording. However, if the recording is in a distant warehouse and traffic is jammed, playing a new game from scratch can sometimes be faster. You have to measure how far away it is and how many times you will rewatch it first.

References

Sign in to like

Comments

No comments yet.

Sign in to leave a comment