feat(kvcache): prefix KV caching behind --prefix-cache #48
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/prefix-kv-caching"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds content-addressed KV block reuse across requests sharing a prompt prefix, per the design doc landed with this PR (
docs/prefix-kv-caching.md): a request whose leading tokens were already computed prefills only its uncached suffix. Off by default behind--prefix-cacheuntil validated on a real workload.What's in it
prompt_logprobsrequests skip reuse (cached positions never run the forward pass).Batch.Prefillmarks admission spans so prompt-token accounting stays exact for suffix-only prefills.gllm_prefix_cache_hit_tokens_totalandgllm_prefix_cache_prompt_tokens_total(hit rate = ratio),gllm_prefix_cache_evictions_total, andgllm_prefix_cache_blocks{state=live|reclaimable|free}on the existing/metricsregistry, refreshed per scrape; the perfstats log gainsprefix_cache_hit_tokensandprefix_cache_hit_rateper interval. Grafana hit-rate panel:rate(gllm_prefix_cache_hit_tokens_total[5m]) / rate(gllm_prefix_cache_prompt_tokens_total[5m]).Testing
prompt_logprobsfull prefill.TestPrefixCacheEquivalenceruns real forwards on the tiny CPU checkpoint and asserts greedy generation is token-identical with the cache on vs. off across cold, warm, and multi-turn-shaped runs, plus suffix-onlygllm_prompt_tokens_totaladvancement and exact hit counts.go build/go vet/gofmtclean; full suite green;make cudaand the cuda-tagged tests pass on the Blackwell box (no kernel changes -- paged attention already reads arbitrary block tables).🤖 Generated with Claude Code
Reuse computed KV blocks across requests sharing an exact leading token run, so a new request prefills only its uncached suffix (multi-turn chat, shared system prompts, post-preemption recompute). Per the design doc: - kvcache: refcounted pool replacing the free-stack ownership model, with a content index over chained FNV-1a prefix hashes and lazy LRU eviction of released-but-registered blocks. Lookups verify identity (parent block + registration generation + token memcmp), never the hash alone, so a collision can only miss; releases push chain tails ahead of their parents so eviction reclaims leaves first and never orphans a chain. - scheduler: admission peeks the longest cached prefix, sizes the batch budget and oversized-request checks by the uncached suffix, acquires the match only on admission (rolled back if the suffix cannot fit), and caps the match so at least one token always prefills. The larger-than-cache rejection now tests capacity rather than an entirely-free cache, which resident reclaimable blocks would prevent forever. prompt_logprobs requests skip reuse (cached positions never run the forward pass). - engine: newly completed full blocks are registered at the end of each successful step -- the one point where their KV is guaranteed computed (release-time registration would publish uncomputed blocks on the cancellation and forward-failure paths). Batch.Prefill marks admission spans so prompt-token accounting stays exact when a prefill starts at a nonzero position. - metrics: gllm_prefix_cache_{hit,prompt}_tokens_total (hit rate), gllm_prefix_cache_evictions_total, and gllm_prefix_cache_blocks{state} on the existing /metrics registry, plus an interval hit rate in the perfstats log. Guarded by a greedy-decode equivalence test on the tiny CPU checkpoint: cold, warm, and multi-turn-shaped runs must be token-identical with the cache on vs. off. Off by default until validated on a real workload. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Automated review by pr-reviewer v0.37.0 | Safety Check | Claude Sonnet 5 | tracking id
r-609d18-7d74eaThis is an AI-generated review and may contain mistakes.
Status: ✅ Completed
✅ Verdict: LGTM — Well-scoped, off-by-default feature with careful correctness guards (identity verification, eviction ordering) and matching test coverage — nothing alarming found.
Reviewed the prefix-KV-caching PR (kvcache manager refactor, scheduler admission changes, engine registration, metrics, docs, tests).
Observations:
--prefix-cache, default false — low blast radius even if there's a latent bug, since existing behavior (PrefixCache=false) is preserved via early-return checks (matchBlocks,MatchPrefix,AcquirePrefix,RegisterComputedall no-op whencfg.PrefixCacheis false), and there's an explicitTestPrefixCacheOffUnchangedtest plusTestPrefixCacheEquivalenceasserting cache-off vs cache-on greedy generation is token-identical.TestEvictLeavesFirst) plus a white-box regression guard (TestStaleParentGenGuard) simulating what happens if that ordering ever breaks.TokenCapacity()instead ofFreeBlocks() == NumBlocks(), addressing a real starvation bug that resident reclaimable blocks would otherwise cause (a request could wait forever). This is called out explicitly and tested (TestLargerThanCacheFailsWithResidentBlocks).cmd/gllm/main.go,engine.go,kvcache.go,metrics.golisted as changed-but-untested files, but this is because these are wiring/plumbing changes (flag definition, metrics counter registration) exercised indirectly by the scheduler/engine tests, not core logic paths.Nothing here looks like it shouldn't have been pushed — it's a carefully scoped, off-by-default feature with commensurate test coverage and explicit reasoning about the tricky invariants (eviction ordering, identity verification, admission accounting).