Add fp8 KV-cache quantization (--kv-dtype) - #126
Open
mkornreich wants to merge 1 commit into
Open
Conversation
New --kv-dtype auto|bfloat16|fp8_e4m3|fp8_e5m2 stores the paged KV cache in fp8 while queries stay bf16 and the FlashInfer fp8 kernels dequantize on read (scale 1.0 -- post-rope k/v magnitudes sit inside e4m3 range). Halves KV bytes/token, ~doubling the token budget that fits in memory, so long prompts that overflowed the bf16 ceiling fit; e4m3 preserves quality in testing. Threaded via EngineConfig.kv_dtype/resolved_kv_dtype (the cost model in spec_kv_bytes_per_token prices the KV dtype's itemsize), MHAKVCache.store_kv (casts bf16->fp8 before the same-width store), and the fi backend (splits q_data_type/kv_data_type in plan() via a new Context.compute_dtype, dropping the deprecated data_type alias). Default (no flag) path is unchanged. Scope: the fp8 store-quantize lives in MHAKVCache.store_kv and the dequant-on-read in the FlashInfer backend, so fp8 KV is full-attention (MHA) + FlashInfer only for now; the engine rejects fp8 on an SWA/MLA/other pool or a non-fi backend up front with a clear error rather than silently corrupting KV. Benchmark (Qwen3-1.7B, RTX 5070 8GB, memory_ratio 0.85, bf16 vs fp8_e4m3): KV budget (same 2.90 GiB): 27,142 -> 54,284 tokens (2.00x) needle @ 10/50/90% depth (14.7k ctx): 3/3 -> 3/3 (retrieval preserved) decode throughput: 98.6 -> 98.3 tok/s (unaffected) prefill throughput: 10,032 -> 9,880 tok/s (-1.5%, quantize-on-store) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
--kv-dtype auto|bfloat16|fp8_e4m3|fp8_e5m2, storing the paged KV cache in fp8 while queries stay bf16 and the FlashInfer fp8 kernels dequantize on read. This halves KV bytes/token, roughly doubling the token budget that fits in a given amount of VRAM — enough to hold long prompts that otherwise overflow the bf16 KV ceiling on small GPUs.Default behavior is unchanged: with no flag (
auto), the KV cache stays at the model dtype.How it works
EngineConfig.kv_dtype/resolved_kv_dtype; the token-budget cost model (spec_kv_bytes_per_token) prices the KV dtype'sitemsize, sosolve_num_pagesallocates ~2× the tokens for the same bytes.MHAKVCache.store_kvcasts the incoming bf16 k/v to the pool dtype (fp8) before the same-widthstore_cachecopy (scale 1.0 — post-RoPE k/v magnitudes sit well inside e4m3 range).q_data_type(bf16) fromkv_data_type(fp8) inplan()(via a newContext.compute_dtype), dropping the deprecateddata_typealias; FlashInfer's fp8 kernels dequantize on read.Benchmark
Qwen3-1.7B, RTX 5070 Laptop (8 GB),
--memory-ratio 0.85, bf16 vsfp8_e4m3:Capacity doubles; long-context retrieval is preserved at every needle depth; decode is unaffected (fp8 reads half the KV bytes); prefill is ~1.5 % slower from the quantize-on-store. On 12 short greedy prompts, fp8 vs bf16 outputs are token-identical or differ only in cosmetic phrasing (all answers correct).
Scope / limitations
fp8 is implemented for full-attention (MHA) models on the FlashInfer backend only — that is where the store-quantize (
MHAKVCache.store_kv) and the dequant-on-read (fiplan()) live. The engine rejects fp8 KV on an SWA / MLA / other pool, or a non-fi backend, up front with a clearNotImplementedErrorrather than silently corrupting KV.fp8_e5m2is wired through the same path; the benchmark above usese4m3(more mantissa precision).Testing
resolve_pool_classreturnsMHAKVCachefor Qwen3 (accepted) andHybridSWAKVCachefor gemma-4-E2B (rejected with the clear error).🤖 Generated with Claude Code