HomeJournalThis post

KV Cache Optimization for Predictable Speed

Benchmark safe prefix reuse, scoped cache identity, eviction value, and cold-path latency before turning KV blocks into production capacity.

JP
JP Casabianca
UI/UX designer and full-stack engineer · Bogotá

KV cache optimization can remove repeated prompt work, yet a high hit rate means little if one tenant can observe another tenant's context or an eviction storm makes response time erratic. This guide builds a safe benchmark, a tenant-bound key, and an eviction policy that make speed gains predictable rather than anecdotal.

The intended reader runs an LLM service with recurring system prompts, documents, or conversation prefixes. You will leave with a traffic fixture, three useful measurements, and a release boundary that treats privacy and the slowest requests as first-class outcomes.

The operating vocabulary joins prefix caching, PagedAttention, prefill latency, and cache isolation; each names a different part of the same serving contract.

KV cache optimization: blocks reused inside explicit tenant walls An authored diagram connects Cold blocks, Shared prefix, Private tail, and p95 proof as distinct parts of the article's method. Cold blocksShared prefixPrivate tailp95 proof
Figure 1: A GPU block atlas separates reusable prefix pages from tenant-private continuation pages, then measures the latency outcome instead of celebrating allocation alone.

KV cache optimization starts with cache anatomy

KV cache optimization targets the keys and values produced for earlier tokens in every transformer layer. Reusing them avoids recomputing an identical prefix, but the continuation still needs decoding and every stored page consumes finite accelerator memory. That makes the cache a scheduling resource, not a free memo table.

Separate four costs in measurements: tokenization, queue wait, prompt processing, and decode. A faster prompt stage can disappear behind a long queue, while a large resident cache can reduce batch capacity. The useful result is end-to-end time to the first useful token under realistic concurrency.

The paged KV memory paper describes paging the key-value state into non-contiguous blocks to reduce fragmentation. The paper supports the memory-layout claim; it does not choose your sharing scope, eviction rule, or product SLO.

My position is that block utilization is a diagnostic, never a launch metric. The boundary is a serving change that improves user-facing latency without increasing isolation failures, out-of-memory retries, or the slowest-cohort delay. Those conditions prevent a local GPU win from becoming a system regression.

Give every reusable prefix a scoped identity

KV cache optimization needs a key derived from canonical token IDs, model revision, tokenizer revision, adapter identity, and an explicit sharing scope. Text hashes are insufficient because tokenization can change, hidden control tokens can differ, and two model revisions may interpret the same bytes differently.

Add a tenant or policy salt before hashing when context must not cross an account boundary. Public, immutable instructions can use a documented shared scope; private documents, tool results, and conversation history should default to the narrowest owner. Never infer shareability from identical content alone.

Store only opaque digests in logs, and keep a reason code for every miss: unseen block, version mismatch, expired page, scope mismatch, or eviction. That taxonomy turns a vague hit-rate drop into an actionable diagnosis while avoiding raw prompt retention in routine telemetry.

The failure mode is cache aliasing: two contexts accidentally map to one identity and one request continues from another's state. Its consequence is both incorrect output and potential disclosure. Mitigate it with length-delimited key fields, versioned serialization, adversarial collision fixtures, and a cold fallback on any ambiguous metadata.

Illustrative TypeScript — a reviewable design sketch, not a compiled production implementation.

type PrefixIdentity = {
  modelRevision: string;
  tokenizerRevision: string;
  adapterDigest: string;
  scopeSalt: string;
  tokenDigest: string;
};

const cacheKey = (value: PrefixIdentity) =>
  hash(lengthDelimitedCanonicalJson(value));
SignalDecisionProof
Exact scoped prefixReuse blocksDigest and tenant salt match
Memory pressureEvict cold pagesAge and recompute cost recorded
Scope mismatchForce cold prefillCross-tenant fixture stays zero
Figure 2: Reuse is allowed only when identity and scope agree; pressure changes eviction order, not the privacy boundary.

Run a traffic trace, not a toy prompt

KV cache optimization should be evaluated with a trace containing prompt length, repeated-prefix length, tenant, arrival gap, requested output tokens, and cancellation point. Preserve shape rather than private text by replacing content with deterministic token sequences that keep lengths and repetition patterns stable.

Build four cohorts: entirely cold prompts, exact warm prefixes, near matches that diverge near the end, and malicious cross-scope matches. Replay each at low and saturated concurrency. The adversarial cohort must record zero reused private blocks even when every token happens to match.

A worked example uses 1,024 prompt tokens, an 896-token repeated system-and-document prefix, and a 128-token private tail. The warm request reuses the first pages, processes only the tail, and produces the same token output as the cold control under a deterministic seed.

The inspectable result is one row per request: eligible tokens, reused tokens, miss reason, queue duration, prompt duration, first-token time, peak resident pages, and output digest. Compare paired cold and warm rows rather than unrelated averages. Pairing isolates the cache decision from prompt difficulty and output length.

Choose eviction by value, not recency alone

KV cache optimization becomes unstable when least-recently-used eviction removes an expensive shared prefix just before a burst, while retaining cheap short pages. Rank candidates using recomputation cost, expected reuse, memory footprint, age, and isolation class. Keep the scoring rule inspectable and bounded.

A practical score can multiply predicted reuse probability by saved prompt tokens, then divide by page count. Apply maximum-age and per-scope quotas before that score so one noisy tenant cannot crowd out everyone else. Quotas also make capacity behavior easier to explain during an incident.

Do not train the first policy. Begin with fixed buckets derived from the trace: pinned public prefix, recently reused medium prefix, private session prefix, and cold remainder. A transparent policy is easier to stress, compare, and roll back than an opaque predictor coupled to traffic drift.

Measure eviction regret by asking which removed blocks were requested again within a defined window and what recomputation they caused. High regret with low memory pressure suggests a poor order; high regret at full pressure may reveal insufficient capacity or unrealistic retention expectations. Both need different remedies.

Read hit rate beside the latency distribution

KV cache optimization needs a dashboard that connects eligible reuse, actual reuse, saved prompt tokens, memory occupancy, evictions, queue age, and response-time percentiles. A single percentage hides whether hits are tiny and misses are expensive. Weighting reuse by avoided work provides a more faithful signal.

Segment by prompt-length band, workload class, tenant scope, model revision, and concurrency. Report both cold and warm paths. If the warm cohort improves while the cold cohort slows, the likely issue is memory pressure or scheduler interaction rather than the identity function.

Set an explicit protection rule: the change must not worsen p95 first-token time for cache-ineligible traffic beyond the agreed error budget. Also cap eviction churn and out-of-memory recovery. These guardrails allow aggressive reuse where it helps without making uncached requests second-class.

The vLLM design note documents hash-based block matching and isolation considerations. Use its mechanics as an implementation reference, then validate the deployed engine version because flags and behaviors can change independently of this article.

  1. RecordRecord

    Capture prompt digest, scope, tokens, and arrival time.

  2. ReplayReplay

    Send cold, warm, and adversarial prefix cohorts.

  3. StressStress

    Add memory pressure and skewed tenant traffic.

  4. CompareCompare

    Report hit rate beside median and p95 time to first token.

Figure 3: The benchmark pairs cache outcomes with service outcomes so a warm average cannot conceal cold or isolated regressions.

Test pressure, churn, and cancellation

KV cache optimization must survive more than a steady warm loop. Alternate long and short prefixes to induce fragmentation, rotate adapters, deploy a tokenizer revision, cancel requests after prompt processing, and create a burst from one scope. Every fixture should state the expected page ownership and miss reason.

Cancellation is subtle because the request may release private tail pages while shared pages remain referenced by other work. Model ownership with reference counts or immutable page handles, then assert that aborted work cannot free a live shared block or pin an unreachable private one.

Version rollover deserves a deliberate drain plan. New keys should make old entries unreachable without interpreting them under the new model, while a bounded cleanup removes obsolete pages. Observe temporary capacity loss during coexistence and keep a one-switch cold-cache rollback.

The test evidence is a page ledger at selected checkpoints: allocated owner, sharing scope, reference count, last use, estimated saved work, and eviction reason. It catches leaks that a latency-only test misses and gives operators a compact picture when capacity behaves strangely.

Roll out with a cache decision receipt

KV cache optimization should emit a compact receipt for each request: policy version, scope class, eligible tokens, reused tokens, miss or eviction reason, and protected service metrics. Avoid raw context. Join the receipt to the normal request trace through an opaque identifier.

Start in shadow mode by computing identities and decisions without reusing pages. Then enable a small low-consequence cohort, compare output digests where determinism permits, and widen only after cold-path guardrails stay healthy. Treat every model or tokenizer upgrade as a new cohort.

Rollback means disabling reuse and allowing the service to operate cold, not purging memory during peak demand. A kill switch should stop new matches while normal reference release drains existing pages. This keeps the recovery action from triggering the very capacity event it is meant to fix.

Preserve the trace generator, policy file, paired results, and scope-negative fixture in version control. Those artifacts make the serving claim reproducible and keep future tuning honest when traffic composition, hardware, or engine behavior changes.

Use a release checklist that protects the cold path

KV cache optimization is ready when keys include every interpretation-changing field, scopes are explicit, the cross-tenant cohort reports zero private reuse, and warm output matches the cold control. The benchmark must include saturation, cancellation, churn, and a version rollover.

Review the dashboard for saved work, not only hits. Confirm that p50 and p95 first-token times improve for eligible traffic while cache-ineligible traffic remains inside its budget. Check peak pages, eviction regret, recovery counts, and per-scope fairness before calling the change successful.

Document which prefixes may be globally shared, which require an owner salt, and how long pages can live. Security review should challenge the canonicalization and scope boundary; performance review should challenge the traffic fixture and capacity assumptions. Both reviews inspect the same receipt.

Finally, rehearse the cold switch during load. If the service cannot tolerate a disabled cache, reuse has become hidden mandatory capacity rather than an optimization. The safest design earns speed when available and remains correct, isolated, and understandable when every entry misses.

Put the method into practice

KV cache optimization is a scoped reuse policy wrapped around scarce accelerator memory. The safe version identifies token prefixes precisely, refuses ambiguous sharing, evicts according to saved work and fairness, and proves the outcome with paired cold and warm traffic.

Begin with one synthetic trace and the cross-scope negative case. If the service saves prompt work, holds the uncached path inside its budget, and produces zero private reuse, you have evidence for a controlled rollout instead of a benchmark screenshot.

The narrow method connects to four existing Journal notes: LLM routing by cost, risk, and latency, backpressure and flow control, SLOs that follow user journeys, HTTP caching without superstition. They cover adjacent implementation boundaries without changing this article's single search intent. Preserve the worked fixture, its visual evidence, and the release receipt so a later update can compare behavior instead of relying on memory.