Navigation
⚡ Inference Systems· 10 min read

Distributed KV Cache

Cross-instance prefix sharing, cache-affinity routing, distributed KV stores, and building a cluster-wide KV cache layer.

distributed-kv-cacheprefix-sharingcache-affinityroutingcluster

At cluster scale with multiple serving instances, each instance maintains its own KV cache — leading to redundant prefill of common prefixes. Distributed KV cache enables cross-instance sharing, turning prefix caching from a per-instance optimization into a cluster-wide one.


1. The Redundancy Problem at Scale

With NN independent vLLM instances serving the same model:

Instance 0: caches "System prompt A" → serves users 0-99
Instance 1: caches "System prompt A" → serves users 100-199  (REDUNDANT!)
Instance 2: caches "System prompt A" → serves users 200-299  (REDUNDANT!)
...

Without coordination, every instance independently prefills the same system prompt. With 100 requests/second and 10 instances: 10× redundant prefill of the same tokens.

The fix requires two capabilities:

  1. Shared KV storage: A place where KV cache computed by one instance is accessible to others
  2. Cache-aware routing: Direct requests to the instance most likely to have the relevant KV cached

2. Architecture

┌─────────────────────────────────────────────────┐
│  Router (cache-aware)                           │
│  Maintains: prefix → instance mapping           │
│  Routes: new request → instance with best match │
├─────────────────────────────────────────────────┤
│  Global KV Index                                │
│  Hash table: token_hash → {instance, blocks}    │
│  Updated on: prefill completion, eviction        │
├──────────┬──────────┬──────────┬────────────────┤
│ Instance │ Instance │ Instance │   Shared KV    │
│    0     │    1     │    2     │   Store        │
│ (local)  │ (local)  │ (local)  │ (CPU/disk/     │
│          │          │          │  network)      │
└──────────┴──────────┴──────────┴────────────────┘

3. Cache-Affinity Routing

Distributed KV Routing

The router’s most important job: send requests to the instance that already has their prefix cached.

def route_request(request, instances, kv_index):
    prefix_hash = hash(request.prompt[:system_prompt_length])
    
    # Find instances with this prefix cached
    cached_on = kv_index.lookup(prefix_hash)
    
    if cached_on:
        # Route to least-loaded instance that has the cache
        target = min(cached_on, key=lambda i: i.queue_depth)
    else:
        # No cache hit — route to least-loaded instance
        target = min(instances, key=lambda i: i.queue_depth)
    
    return target

Impact of Cache-Affinity Routing

Routing strategyCache hit rateThroughput relative to random
Random (round-robin)1/N1/N per instance1×
Cache-affinity80-95%2-3×
Cache-affinity + load-aware75-90%2.5-3.5×

llm-d reports 3× higher throughput and 2× faster TTFT with prefix-cache-aware routing vs round-robin on Llama-3.1 70B.


4. Distributed KV Storage Options

StorageBandwidthLatencyCapacityUse case
GPU HBM (local)3,350 GB/sns80-192 GBHot cache (current requests)
CPU DRAM (local)64 GB/sμs512-2TBWarm cache (recent evictions)
Remote GPU (RDMA)50 GB/sμsOther instance’s HBMCross-instance sharing
Distributed store10-50 GB/smsUnlimitedPersistent, cluster-wide

LMCache Approach

Tiered storage within each node, with a shared remote tier:

Request arrives → check local GPU cache → check local CPU → check remote store → prefill

Mooncake Approach

Centralized distributed store with RDMA transfer:

Request arrives → router checks global index → if cached on another instance:
  Transfer KV via RDMA (25-50ms for 4K context) vs full prefill (50-100ms)

5. Consistency and Eviction

Consistency Model

KV cache is immutable once written (the same token sequence always produces the same KV). This simplifies consistency — no need for write locks or invalidation. The only operations are:

  • Write: After prefill, register KV blocks in the global index
  • Read: On cache hit, read KV blocks (local or remote)
  • Evict: When memory pressure rises, remove entries from index + storage

Eviction Strategy

# Global LRU eviction
# When any instance runs low on memory:
# 1. Evict least-recently-used entries from local GPU cache → move to CPU
# 2. If CPU full → move to remote store or discard
# 3. Update global index to reflect new location (or removal)

# Eviction priority: unique prefixes > shared prefixes
# (shared prefixes are more likely to be needed again)

6. When Distributed KV Cache Matters

ScenarioBenefitWhy
Many instances, shared system promptsHighEliminate N×N\times redundant prefill
RAG with repeated documentsHighDocument KV reused across queries
Multi-turn chatMediumSession KV shared across turns (already handled per-instance)
All unique promptsNoneNo sharing opportunity
Single instanceNoneAlready has its own prefix cache

For the systems implementing distributed KV, see LMCache & Mooncake and llm-d.


Self-Check

  • Explain the redundant prefill problem with multiple independent instances
  • Describe cache-affinity routing and its throughput impact
  • Compare distributed KV storage options (local GPU/CPU, remote RDMA, distributed store)
  • Explain why KV cache immutability simplifies consistency
  • Design an eviction strategy for a distributed KV store
  • Identify workloads where distributed KV cache provides the most benefit

References