fix: #552 int8 kv cache quantization cannot be enabled - #553
Open
BoBoDai wants to merge 1 commit into
Open
Conversation
BoBoDai
force-pushed
the
fix/kv-cache-int8-quant
branch
3 times, most recently
from
August 30, 2026 03:09
edd222b to
b95460c
Compare
Author
|
/retest |
|
⛔ Only repository members can run |
BoBoDai
marked this pull request as ready for review
August 30, 2026 03:23
BoBoDai
force-pushed
the
fix/kv-cache-int8-quant
branch
from
August 31, 2026 14:40
17123f9 to
e0b03b1
Compare
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
Fix
--kv-cache-dtype=int8end-to-end: the flag was silently dropped in the Python layer, so no quantization ever ran; once the chain is wired through, the INT8 path exposes several C++ correctness bugs. This PR makes INT8 KV-cache quantization actually usable.csrc/layers/attention/attention.cpp: initializekv_cache_k_scale/kv_cache_v_scalewithTensor::ones— previouslyParameter({1}, F32, device, 0, 0, 1)only sets TP config (tp_dim/tp_rank/tp_size), leaving the storage uninitialized (read as 0.0, so dequantized K/V became all zeros); also create/register the scale parameters before constructingAttentionLayer(it captures them by value, otherwise it keeps stale empty tensors).csrc/layers/attention/backends/static_attn.cpp: permute K/V in model precision first, then quantize — quantized dtypes must not pass through permute/rearrange.csrc/layers/quantization/kv_quant.cpp:zero_pointmust be F32 (the infiniop INT8 kernels read it asfloat*); it was created in the K dtype (bf16).examples/test_infer.py,python/infinilm/llm/llm.py,python/infinilm/config/engine_config.py,python/infinilm/llm/model_runner/model_runner.py,python/infinilm/llm/model_runner/speculative_runner.py,python/infinilm/server/inference_server.py,python/infinilm/server/pipeline_worker.py: threadkv_cache_dtypefrom CLI to the C++ engine (LLM / AsyncLLMEngine / server / worker / draft-runner paths).python/infinilm/modeling_utils.py: tolerate missingkv_cache_k_scale/kv_cache_v_scalekeys during checkpoint loading — these are registered in C++ only when INT8 is enabled and initialized to 1.0; HF checkpoints never contain them.examples/bench.py: KV-cache memory reporting now reflectskv_cache_dtype— the per-case estimate uses the actual cache dtype (int8/fp8 = 1 byte/element) and includes both K and V; the display unit auto-switches (B/KB/MB/GB); a measured value is printed from the live cache tensors ([bench] measured KV cache memory), which matches the estimate exactly.Motivation
--kv-cache-dtype=int8has no effect onmain:base_config.pyparses it but every hop below drops it, so the C++ engine receivesNone, the quant scheme staysNONE, and the INT8 path never executes. After wiring the chain, the path exposes correctness bugs: uninitialized scale (0.0) → dequantized KV all zero → uniform attention → degenerate output; stale empty scale tensors in the attention backend; bf16zero_pointmisread asfloat*; quantize-before-permute.Closes #552
Type of Change
feat— new feature / new modelfix— bug fixperf— performance improvement (no behavioral change)refactor— code restructuring without behavior changetest— adding or fixing tests onlydocs— documentation onlybuild/ci— build system or CI configurationchore— tooling, formatting, or other non-code changeskv_cache_dtypedefaults toNone; all pre-existing paths are unchanged when the flag is unset.Test Results of Involved Models on Supported Platforms (Please attach screenshots)
Single request test
--kv-cache-dtype=int8kv_cache_*_scaleparams readtensor([1.]); quantize → cache → dequantize round-trip matches element-wise--kv-cache-dtype=int8Supplementary: with a calibrated scale (0.05) the same INT8 pipeline produces coherent output for short generations (≤100 tokens) on TinyLlama-1.1B, while 1.0 collapses immediately — the degradation is monotonically tied to the scale step, not the pipeline.
Scale sensitivity (validation, not part of this PR's code change)
Same INT8 pipeline, only the static scale value differs (TinyLlama-1.1B-Chat, NVIDIA L40S).
The calibrated value was chosen from the measured K/V magnitude (
max|K| ≈ 4–5, somax/127 ≈ 0.03–0.04). The degradation is monotonically tied to the scale step, not to the pipeline: 1.0 collapses at once, while 0.05 keeps short generations coherent and only degrades on long contexts — the inherent ceiling of static-scale INT8 (error accumulation). Dynamic per-token scales are the follow-up (see Notes).Sanity test
TinyLlama-1.1B-Chat
NVIDIA L40S
Sanity test: test_benchmark.py, MMLU abstract_algebra, 5 samples, default bf16
1/5 = 20%, all 5 predictions identical to main baseline (no regression)
current branch

main branch

service test
Benchmark / Performance Impact
N/A for
perf. The INT8 KV cache is a memory optimization, so memory is measuredfirst.
Memory — measured from the live KV cache tensors (
get_kv_cache()) and printed byexamples/bench.py([bench] measured KV cache memory), input=32, output=128, B=1:The config-based estimate shown in the case line matches the measured value exactly. At the full
cache_len=4096allocation the same ratio applies (TinyLlama-1.1B-Chat: bf16 92.27 MB vs int8 46.14 MB; SmolLM-135M measured:90.0 → 45.0 MB).
Throughput (
examples/bench.py, NVIDIA L40S, input=32, output=128, B=1, single run; consistent across multiple runs):--kv-cache-dtype=int8INT8 halves KV-cache memory and roughly doubles prefill throughput (quantized cache writes), at the cost of ~7–10% decode throughput (per-step quantize/dequantize kernels). The default path (flag unset) is unchanged — no regression (CPU timing: main 4084 ms vs PR 3984 ms mean, 64 tokens, ×3).
Notes for Reviewers
deepseek_v2_attention.cpp,deepseek_v2_mla_attention.cpp,ernie4_5_attention.cppshare the same constructor-order pattern (scales created afterAttentionLayercaptures them); left out of this PR to keep the change minimal — follow-up PR.per_tensor_quant_i8kernels are NVIDIA/QY-only. A pure-CPU build will fault at the quant op, so the INT8 path is only runnable on NVIDIA/QY; verification was done on NVIDIA (E2E + round-trip) plus allocation-only checks on CPU.// 无需反量化inkv_quant.cppwas not touched by this PR.per_tensor_quant_i8/per_tensor_dequant_i8ops.CI / ChatOps
CI will be triggered manually from the Actions tab on this branch after the PR is opened.
Checklist
Title, Branch, and Commits
feat(nvidia): …,fix(cuda/gemm): …).<type>/xxx-yyyy-zzzzwhere<type>matches the PR title's Conventional Commits type and words are joined with hyphens (seeCONTRIBUTING.md§Branches).CONTRIBUTING.md§Pull Requests).main— the branch is rebased cleanly on top of the currentmain.fixup!/squash!/wipcommits remain.Scope and Design
CONTRIBUTING.md§Code/General).printf/std::cout/print(...)left behind, orTODOwithout an owner and issue link.General Code Hygiene (applies to all languages)
CONTRIBUTING.md§Code/General).CONTRIBUTING.md§Code/General).the `seqlens_k` tensor) (CONTRIBUTING.md§Code/General).CONTRIBUTING.md§Code/General).CONTRIBUTING.md§Code/General; §Python).C++ Specific (if C++ files changed)
CONTRIBUTING.md§C++).CONTRIBUTING.md§C++).new/delete; RAII / smart pointers / existing allocators are used.scripts/format.py.csrc/models/llama_legacy/.Python Specific (if Python files changed)
CONTRIBUTING.md§Python).CONTRIBUTING.md§Python).scripts/format.py.python/infinilm/auto_config.py.Testing
examples/test_infer.py), or specify the reason for skipping.examples/bench.py), or specify the reason for skipping.test/bench/test_benchmark.py), or specify the reason for skipping.python/infinilm/server/inference_server.py+scripts/test_perf.py), or specify the reason for skipping.Build, CI, and Tooling
/retestwas requested.Documentation
README.md,CONTRIBUTING.md, or inline docs updated when behavior, build flags, or developer workflow changed. — no build flags or developer workflow changed; the--kv-cache-dtypeflag already exists inbase_config.py(this PR fixes it rather than adding it), and docstrings for the newkv_cache_dtypeparameters were added inline (python/infinilm/config/engine_config.py,python/infinilm/server/inference_server.py).!orBREAKING CHANGE:footer. — no breaking change:kv_cache_dtypedefaults toNoneand all pre-existing paths are unchanged when the flag is unset.Security and Safety