Skip to content

feat: opt-in INT8 KV-cache quantization - #183

Open
drunkcoding wants to merge 11 commits into
mainfrom
plan/kv-cache-quantization
Open

feat: opt-in INT8 KV-cache quantization#183
drunkcoding wants to merge 11 commits into
mainfrom
plan/kv-cache-quantization

Conversation

@drunkcoding

@drunkcoding drunkcoding commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Description

Implements the Momus-approved plan (docs/superpowers/plans/2026-08-21-kv-cache-quantization.md) for an opt-in, correctness-gated int8_sym paged KV-cache format behind a format abstraction, preserving existing attention execution dtypes and deterministic fallback to the native/SDPA paths.

What landed

  • Format contract (moe_infinity/runtime/kv_cache_format.py): KVCacheFormat, resolve_kv_cache_format capability resolver, tokenwise symmetric INT8 quantizer with upward-rounded FP16 scales (no -128, exact zero, element error <= stored_scale/2 + 1e-6), LayeredPagedKVStore/LayeredKVPageChunk, and model_info_from_config MLA/GQA detection.
  • Runtime integration (attention_backend.py, paged_attention_ops.py, qwen3_paged_attention.py): store-bound PagedAttentionBackend with bind_store/write_chunk, INT8 decode via native CUDA dispatch or FP32 dequantized SDPA fallback, probe_native_int8_binding capability check, layer_idx threading.
  • Native INT8 CUDA kernel (extensions/kernel/paged_attention_int8.{cuh,cu}, paged_attention.cu, setup.py): reusable INT8+FP16-scale paged decode kernel with FP32 accumulation, registered in the single existing PYBIND11_MODULE, linked via _PAGED_ATTN_SOURCES, plus a dedicated tests/cuda target. Verified MHA/GQA parity vs an independent FP32 dequantized SDPA oracle (max abs diff ~1e-4, cosine 1.0).
  • Serving lifecycle (kv_cache.py, model_runner.py, scheduler.py, kv_cache_offload_coordinator.py): single-owner LayeredPagedKVStore, RESIDENT/SWAPPED residency state machine (swap_out(release_gpu_blocks=True), transactional swap-in, page-chunk truncation that slices INT8 scales, free_gpu_blocks rejects SWAPPED), single-call scheduler preemption, ModelRunner store-identity validation, coordinator.set_kv_store. Additive design preserves the legacy constructor and every existing test.
  • Config + memory + engine handoff (config.py, memory_manager.py, api_server_v2.py, engine.py): kv_cache_format/kv_cache_allow_fallback fields, descriptor-driven get_max_kv_blocks (native unchanged, int8 counts FP16 scales), --kv-cache-format/--no-kv-cache-format-fallback CLI, and end-to-end consumption in ContinuousBatchingEngine — it resolves the decision from the model config + capabilities, allocates the effective-format store, builds a store-backed PagedKVCache, and exposes requested/effective_kv_cache_format, kv_cache_execution_backend, and kv_cache_format_decision_reason via engine stats and /v1/config, so a native fallback (e.g. MLA) is never reported as quantized.
  • Quality gates: deterministic tensor-level INT8-vs-native decode equivalence (fp16/bf16, MHA/GQA, page boundaries, zero and non-contiguous cases), a model-free 2048-step logit/token-agreement parity gate, exact INT8 storage byte/ratio assertions, and an env-gated WikiText-2 perplexity release gate.
  • Benchmark + docs: benchmarks/serving/kv_cache_quantization.py A/B matrix with separate storage/transfer/execution precision reporting and a strict-fallback guard; docs for the precision contract, memory formula, MLA/FlashInfer fallback, one-setting rollback, and effective-format stats.

Precision & memory

int8_sym stores INT8 payload + one FP16 scale per (layer, page, KV head, token); for the canonical (block_size=16, kv_heads=8, head_dim=128) page this is 33,280 bytes vs 65,536 native FP16 (0.5078125). Storage/transfer/execution precisions are reported separately so a fallback is never mistaken for a quantized run.

Scope & fallback

MHA/GQA validated; MLA visibly falls back (mla_not_validated) or errors under strict mode. FlashInfer stays active for native stores; an int8_sym request bypasses FlashInfer (flashinfer_no_int8_sym_contract) into the built-in validated path. No universal low-bit or 2-bit claim is made; KIVI/KVQuant are motivation only.

Verification

  • Full KV suite: 132 passed, 1 skipped (FlashInfer unavailable) with the in-tree native binding; ruff clean on all changed files.
  • Native INT8 kernel built (arch sm_120) and MHA/GQA parity verified vs FP32 oracle.
  • End-to-end: an engine built with --kv-cache-format int8_sym allocates an INT8 store and reports effective_kv_cache_format=int8_sym; an MLA model reports effective_kv_cache_format=native, reason=mla_not_validated.

Environment blockers (recorded, not worked around)

  • compute-sanitizer is not installed in this environment, so the sanitizer memcheck steps could not be executed.
  • tests/cuda CMake harness cannot configure here (its supported-Python gate is 3.8–3.11 vs installed 3.13, and cmake 3.28 + CUDA 13.1 fail Torch's enable_language(CUDA) toolkit-root detection). The kernel itself compiles via nvcc (JIT + setuptools); the added CMake target is syntactically valid.
  • The pinned Qwen3-30B-A3B checkpoint and WikiText-2 snapshot are not present, so the perplexity/model-parity gates and the full long-context benchmark matrix skip; the model-free logit/token and tensor quality gates run and pass.

Type of Change

  • Production implementation (opt-in, native default)
  • Tests + docs

Checklist

  • Native default and one-setting rollback retained
  • Storage/transfer/execution precision separately visible
  • Quality gates specified and (non-model) executed
  • No universal low-bit/2-bit claim

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
@drunkcoding

Copy link
Copy Markdown
Contributor Author

Momus plan review: [OKAY] on the first review round.

drunkcoding added 9 commits August 28, 2026 22:20
Add KVCacheFormat/KVCacheFormatName, resolve_kv_cache_format capability
resolver, tokenwise symmetric INT8 quantizer with upward-rounded FP16
scales, and format-aware KVCacheSpec.page_size_bytes accounting.
Add LayeredPagedKVStore, LayeredKVPageChunk, allocate_layered_paged_kv_store
with chunk writes, logical prefix reads, blocking page snapshot/restore, and
layer/kernel/flashinfer views over a single layer-major payload.
Bind PagedAttentionBackend to a LayeredPagedKVStore for int8_sym, resolve the
execution backend/format decision, quantize chunk writes, and route decode via
native INT8 dispatch or FP32 dequantized SDPA. Extend paged_attention_fwd with
key/value scale tensors and a probe_native_int8_binding capability check; pass
layer_idx through Qwen3PagedAttention.
Add reusable paged_attention_int8.cuh/.cu implementing an INT8+FP16-scale
paged-attention decode kernel with FP32 accumulation, register its wrapper in
the single existing PYBIND11_MODULE, link the source via _PAGED_ATTN_SOURCES,
and add a dedicated tests/cuda target plus Python MHA/GQA parity against an
independent FP32 dequantized SDPA oracle.
Back PagedKVCache with a single LayeredPagedKVStore (legacy dimension
constructor preserved), add a RESIDENT/SWAPPED residency state machine with
swap_out(release_gpu_blocks=True), transactional swap-in, page-chunk truncation
that slices INT8 scales, and free_gpu_blocks rejection of swapped sequences.
Replace the scheduler two-call preemption with one ownership-transferring call,
add ModelRunner store-identity validation, and add coordinator.set_kv_store.
Add ArcherConfig.kv_cache_format/kv_cache_allow_fallback (validated via
KVCacheFormat.parse), descriptor-driven MemoryManager.get_max_kv_blocks
format_name accounting (native unchanged, int8 counts FP16 scales),
model_info_from_config MLA/GQA detection, and OpenAI server
--kv-cache-format/--no-kv-cache-format-fallback flags wired into the engine
config, with fail-closed native-binding capability tests.
Add deterministic tensor-level INT8-vs-native decode equivalence gates
(fp16/bf16, MHA/GQA, page boundaries, zero and non-contiguous cases), a
model-free 2048-step logit/token-agreement parity gate, exact INT8 storage
byte/ratio assertions, and an env-gated WikiText-2 perplexity release gate.
Add a native vs int8_sym long-context A/B benchmark CLI with a
BenchmarkResult schema that reports storage, transfer, and execution
precision separately, a parent-directory-creating JSON writer, and a
strict-fallback guard so a fallback cannot be reported as a quantized run.
Document the opt-in kv_cache_format contract: storage/transfer/execution
precision table, exact memory formula, MHA/GQA scope with visible MLA and
FlashInfer-bypass fallbacks, one-setting rollback commands, effective-format
stats, and the long-context benchmark runbook. State that KIVI/KVQuant are
motivation only and no universal 2-bit support is claimed.
@drunkcoding drunkcoding changed the title WIP: plan KV-cache quantization WIP: implement opt-in INT8 KV-cache quantization Aug 28, 2026
Wire the opt-in format end-to-end: ContinuousBatchingEngine resolves the
format decision from model config + capabilities, allocates the effective
LayeredPagedKVStore, builds a store-backed PagedKVCache, sizes num_blocks per
the effective format, and exposes requested/effective_kv_cache_format,
execution backend, and decision reason via get_stats and /v1/config so a
native fallback (e.g. MLA) is never reported as quantized.
@drunkcoding

Copy link
Copy Markdown
Contributor Author

Independent implementation verification:

  • Focused KV format/capability/handler/storage suites: 49 passed.
  • Python LSP scan: 0 errors across 50 files.
  • Branch clean and synchronized.

PR remains draft for native CUDA and model-quality qualification.

@drunkcoding
drunkcoding marked this pull request as ready for review September 2, 2026 21:53
@drunkcoding drunkcoding changed the title WIP: implement opt-in INT8 KV-cache quantization feat: opt-in INT8 KV-cache quantization Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant