skip to content

In vLLM, what does automatic prefix caching hash to match a reused prefix?

level: middleimportance: should knowfreq 48%

answer

  1. hash chain, not prompt equality
  2. each key includes the previous
  3. prefix only, from token zero
  4. complete blocks are cacheable
  5. adapter and media join the key

basics

~20 s

Each full KV block gets a chained hash: the previous block's hash combined with that block's token ids, plus keys such as the LoRA adapter id. A new request reuses a cached block only when the chain matches from token 0, so matching is prefix-only and block-granular.

solid answer

~50 s

vLLM identifies cached KV blocks by a hash chain rather than by prompt strings. Block *n*'s key is computed from block *n-1*'s hash together with block *n*'s own token ids, plus extra keys for anything that would change the KV values — the LoRA adapter in use, multimodal input hashes. Because each key depends on every preceding block, a match is only possible for an unbroken prefix starting at token 0: a shared middle or suffix cannot hit, and one different early token invalidates everything after it. Only *full* blocks are hashed, so the partially filled trailing block of a prompt is never cached until it fills. On a hit the engine skips prefill for those tokens and simply references the existing blocks, which are reference-counted and shared rather than copied. In vLLM 0.27 this is on by default (`enable_prefix_caching`); disable it with `--no-enable-prefix-caching`, and watch `vllm:prompt_tokens_cached` against `vllm:prompt_tokens` to see how much it is buying.

go deeper

for a junior

Know that vLLM can skip recomputing a prompt prefix it has already seen, that this only works for a prefix starting at the very beginning, and that it is on by default.

for a middle

Explain the chained block hash and why chaining rather than per-block hashing is required for correctness. Know that only full blocks are cacheable and that matched blocks are shared by reference, not copied.

for a senior

Reason about what belongs in the hash beyond tokens — adapter id, multimodal inputs — as a tenant-isolation concern, and read prompt_tokens_cached against prompt_tokens to prove the feature is earning its keep on a real workload.

for a principal

Own the prompt-construction convention that makes reuse possible fleet-wide: stable prefixes first, all volatile content last, and a policy on whether adapter or tenant boundaries are allowed to share a pool at all.

## Why hashing at all The KV entries for a token depend on that token *and every token before it*. Two requests can therefore share KV state only when they share an exact prefix. vLLM's job is to recognize that cheaply, at block granularity, without comparing whole prompts token by token against every cached sequence. The answer is a content-addressed block pool: give each block an identifier derived from its contents *and* its position in the sequence, and look that identifier up. ## The chained key For the first block, the key is derived from its token ids. For every block after it, the key combines the **previous block's hash** with **this block's token ids**. The result is a chain: block 5's identity encodes tokens 0 through 5x`block_size`, not just its own 16 tokens. That chaining is the part worth explaining, because the naive alternative — hash each block's tokens alone — is broken. The same 16 tokens can occur at different positions in different prompts, preceded by entirely different context, and their KV values would be completely different. Hashing tokens alone would happily hand you another request's numbers. The chain pins each block to its entire preceding prefix, so a hit is only granted when the KV values genuinely would be identical. The direct consequence is that matching is **prefix-only**. Two prompts sharing a 2,000-token suffix but differing in their first token share nothing: the first block's hash differs, so every downstream hash differs. ## Extra keys Token ids are not the only thing that determines KV values. vLLM folds additional keys into the hash for anything else that does: - **LoRA adapter id** — a different adapter changes the projections, so the same tokens under a different adapter must not collide onto the same block. - **Multimodal input hashes** — an image placeholder token is meaningless without knowing which image it stands for. This is a correctness mechanism, not an optimization. Without it, prefix caching would silently return another tenant's activations for the same visible token sequence. ## Full blocks only A block is hashed and made reusable only once it is complete. A 100-token prompt with a block size of 16 yields six full blocks (96 tokens) plus a partial block holding the last 4, and only the six are cacheable. Practical effect: reuse is quantized to the block size, so of a shared 1,000-token system prompt you reuse 992 tokens, not 1,000. Nobody cares about the remainder — but it explains why cached-token counts never quite equal the prefix length. ## Hits, sharing, and eviction On a hit, the engine does not copy anything. It maps the existing blocks into the new request's block table and increments their reference counts, then prefills only the tokens after the matched prefix. So a hit saves *compute* (skipped prefill, hence lower TTFT for long shared prefixes) and simultaneously saves *memory*, because a hundred requests on one system prompt hold one physical copy of its KV state rather than a hundred. Cached blocks whose reference count drops to zero are not freed immediately; they stay in the pool as reuse candidates until the pool needs them for a live allocation. The eviction policy that governs that, and what happens more broadly when free blocks run out, belongs to the paged-KV-cache topic — here the relevant point is that caching costs nothing extra in memory, because it only retains blocks that would otherwise sit free. ## Defaults and observation In vLLM 0.27 the V1 engine enables prefix caching by default; you turn it *off* with `--no-enable-prefix-caching` (the auto-generated negation of the boolean flag). Turning it off is occasionally right — a workload with no shared prefixes at all pays a small hashing cost for zero hits, and disabling it removes a variable when benchmarking cold prefill. To see whether it is earning its keep, compare `vllm:prompt_tokens_cached` with `vllm:prompt_tokens`: the ratio is the share of prompt tokens that never had to be prefilled. A ratio near zero on a workload you believed had a big shared system prompt is a signal to look at what varies early in your prompts — a timestamp, a session id, a shuffled tool list at the top of a system message will each kill every hit downstream of it. ## The misconception to avoid Prefix caching is not response caching. It does not remember what the model *said*; it remembers the intermediate attention state for a token prefix, and generation proceeds normally from there. Two identical prompts with a non-zero temperature will still produce different outputs — they just get to the first sampled token faster.

  • Why chain each block's hash to the previous block instead of hashing its own tokens?
    Because identical token windows appear at different positions preceded by different context, and their KV values differ completely. Hashing tokens alone would let one request adopt another's activations. Chaining makes a block's identity encode the whole prefix leading to it, so a hit implies the cached numbers really are the ones this request would have computed.
  • What besides token ids goes into the block key, and why is that a correctness issue?
    Anything that changes the resulting KV values: the LoRA adapter id and hashes of multimodal inputs. A different adapter produces different projections for the same tokens, and an image placeholder token means nothing without knowing the image. Omitting these would not merely lose hits — it would serve one request another's cached state.
  • Does a prefix-cache hit save memory as well as prefill time?
    Both. The matched blocks are shared by reference rather than copied, so a hundred concurrent requests behind one long system prompt hold a single physical copy of that prefix's KV state. That frees pool capacity for the parts of each request that genuinely differ, which often matters more than the TTFT saving.
  • Is prefix caching the same as caching the model's responses?
    No. It caches intermediate attention state for a token prefix, not generated text. Decoding still runs normally after the matched prefix, so with temperature above zero two identical prompts still produce different completions — they simply reach the first sampled token sooner.

saying these in an interview costs you the question

  • Thinks matching is by prompt string equality
  • Believes a shared suffix or middle can hit
  • Assumes a partially filled block is cached
  • Says prefix caching must be switched on in 0.27
  • Confuses it with caching the model's text responses

context