Skip to content

[4/6] vLLM GDN/KDA state-only fake quantization - #2541

Draft
kaix-nv wants to merge 25 commits into
kaix/linear-attention-qat-examplefrom
kaix/linear-attention-vllm
Draft

kaix-nv wants to merge 25 commits into
kaix/linear-attention-qat-examplefrom
kaix/linear-attention-vllm

Conversation

@kaix-nv

@kaix-nv kaix-nv commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Linear-attention series — 6 PRs

Order PR Depends on
1/6 #2497 GDN state/W QAT foundation main
2/6 #2519 GDN/KDA state QAT + INT8 #2497
3/6 #2657 Megatron Bridge linear attention QAT/QAD example #2519
4/6 #2541 vLLM GDN/KDA state-only fake quantization #2657
5/6 #2503 GDN/KDA prefill GEMM quantization #2541
6/6 #2507 Experimental GDN/KDA approximate inverse #2503

The five open PRs form one native GitHub stack in the order shown. #2497 has landed, so #2519 targets main. #2541 now targets #2657. Rebase each remaining descendant after its immediate parent merges.

#2541 applies TensorQuantizer before native vLLM prefill/decode calls. Serving-time prefill-GEMM quantization remains deferred until an optimized fused kernel is available.

What does this PR do?

Type of change: New feature.

Stacked on #2657 (kaix/linear-attention-qat-example). Add a state-only ModelOpt fake-quant plugin for vLLM GDN and KDA. It applies TensorQuantizer to the incoming recurrent state immediately before each native prefill or decode call. Native kernels and cache management remain in use; this PR changes no CUDA or Triton kernels.

Prefill quantizes the initialized state tensor passed to the original chunk kernel. Decode gathers only active cache slots, applies QDQ, and writes them back before the original recurrent kernel. Each slot/head has its own dynamic scale over [Dk,Dv]. FP8 E4M3 and signed symmetric INT8 are supported, with FP32 dequantized state.

This replaces the earlier draft's custom attention execution and separate request cache. There is no additional persistent state allocation or worker memory reservation. Quantization happens once per native invocation, including scheduler-level prompt continuations; it does not round every internal prefill chunk or the final-state write. Quantizer configuration and state names survive save/restore through the HF-to-vLLM mapper.

The worker validates adapter policy before calibration or warmup and after loading quantizer state. It discovers adapters from the model instead of retaining a second list; shared runtime capability checks run once per binding.

The execution-config definition comes from #2519. This serving adapter continues to accept only the default execution policy and state quantizers; it does not enable the training prefill-GEMM or replay paths. The native integration test now propagates its import paths to spawned workers as well as through PYTHONPATH.

The serving example README includes the state-quantization launch command, numerical boundaries, and runtime requirements.

Prefill/decode quantization boundary

State QDQ applies to both prefill and decode, specifically at native vLLM invocation boundaries:

  • Prefill: quantize the incoming initialized recurrent state once before each native prefill invocation. With scheduler-level chunked prefill, each invocation creates one QDQ boundary; internal kernel chunks do not.
  • Decode: gather and quantize only the active recurrent-cache slots immediately before each native decode invocation. Inactive slots are unchanged.
  • State write: native kernels update and write recurrent state in FP32 without additional final-write quantization. The next prefill or decode invocation quantizes that state before reading it.

This is floating-point fake QDQ for numerical qualification. It does not store the persistent recurrent cache in packed INT8 or FP8, execute state updates with low-precision MMA, or establish memory-capacity, bandwidth, or latency savings.

Usage

PYTHONPATH=.:examples/vllm_serve \
RECIPE_PATH=examples/vllm_serve/linear_attention_state_int8.yaml \
python examples/vllm_serve/vllm_serve_fakequant.py /path/to/model \
  --tensor-parallel-size 2 --enforce-eager --no-async-scheduling \
  --no-enable-prefix-caching --mamba-cache-dtype float32

The recipe sets algorithm: null: dynamic state scales require no calibration dataset. Set state num_bits: [4, 3] for FP8. Existing worker weight/activation calibration remains available. Use MODELOPT_STATE_PATH instead of RECIPE_PATH to restore saved quantizer configuration.

The state-only adapter rejects nondefault execution policies, including the previous draft's replay policy. Use the new state-only recipe. See examples/vllm_serve/README.md#linear-attention-state-quantization for the exact rounding cadence and runtime limits.

Testing

Stack metadata has been updated to remove #2562. The branch history still needs to be replayed onto current #2657; the runtime results below belong to the earlier revision.

Compilation-fixture update: this branch is restacked on #2497's separate follow-up commit 95766de709ac. The changed test modules passed at #2497 (26 passed, 20 hardware skips in each cold/warm run) and at the #2507 stack tip (64 passed, 20 hardware skips on two RTX A6000 GPUs); intermediate PRs were not separately rerun. Functional calls created no new tracked kernel binaries and kept the default 120-second cap. All six source trees match the validated trees, and commit hooks passed. Native FP8-state/Hopper cases remain hardware-gated.

Earlier scope-specific validation follows.

For the earlier documentation/example amendment, pre-commit, Markdown links and anchors, command/Python syntax, source-manifest readability, and stale-path checks passed. Runtime kernels were not changed; model training, distributed integration, and quality studies were not rerun for this amendment.

Prior runtime validation on two RTX A6000 GPUs, Torch 2.9.1+cu128, and Triton 3.5.1:

  • 13 CPU export/reload tests passed.
  • All 5 native vLLM integration tests passed (578.19 seconds): GDN and KDA at TP=1/2 plus saved-state name mapping. Tests exercise INT8 and FP8 state QDQ, native-kernel controls, inactive cache slots, prompt continuations, repeated generation, calibration, checkpoint reload, and disabled-path agreement.
  • Pre-commit, diff checks, and commit-signature verification passed.

The integration run used the clean pinned vLLM checkout 930288170c31e8568290fff407dca8caf17d16ad, whose recurrent-state ABI is key-first, with existing local compiled artifacts. The initially selected editable vLLM checkout used value-first state and correctly failed the runtime guard; that run is not included in the passing result.

The passing run used the existing local pytest harness to set gpu_memory_utilization=0.04, with the same 128 MiB cache budget and unchanged numerical assertions. This avoids requiring nearly all GPU memory for tiny synthetic models. NCCL_P2P_DISABLE=1 is required on this host. These are validation settings, not changes to the serving worker or kernels.

PYTHONPATH=. python -m pytest -q \
  tests/unit/torch/export/test_vllm_fakequant_hf.py \
  tests/unit/torch/export/test_vllm_quantizer_reload.py

NCCL_P2P_DISABLE=1 \
PYTHONPATH=.:tmp/vllm-runtime:examples/vllm_serve:tmp/design-review \
python -m pytest -p low_memory_vllm -q \
  tests/gpu_vllm/torch/quantization/test_vllm_linear_attention.py

The local low_memory_vllm fixture wraps this test module's LLM constructor with functools.partial(LLM, gpu_memory_utilization=0.04). It is not part of the PR. Models use tiny offline Qwen3-Next/Kimi Linear configurations and synthetic weights; this is functional qualification, with no pretrained-quality or performance claim.

Before your PR is "Ready for review"

  • Is this change backward compatible?: Opt-in; disabled-path agreement is tested. The unmerged draft's replay-serving recipe and execution policies are intentionally replaced by state-only QDQ.
  • If you copied code or added a dependency, did you follow CONTRIBUTING.md?: N/A; no copied third-party kernel or new dependency. Wrappers call native vLLM functions.
  • Did you write necessary tests?: Yes; native-kernel and worker integration coverage.
  • Did you update Changelog?: Yes.
  • Did you get Claude approval?: No; keep draft.

Additional Information

The supported runtime is vLLM 0.15.x with key-first FP32 state, eager synchronous execution, TP=1/2 and PP=DP=CP=1. Speculative decoding, prefix caching, state transfer, and CUDA graphs remain unsupported.

A separate prefill-GEMM PR is deferred until an optimized fused kernel is available. It will reuse #2503's eight numerical sites; this adapter does not expose the materialized PyTorch prefill backend. Megatron training/backward qualification and vLLM forward/cache qualification remain separate.

@copy-pr-bot

copy-pr-bot Bot commented Sep 24, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@kaix-nv kaix-nv changed the title Add vLLM GDN/KDA decode fake quantization Add vLLM GDN/KDA state-only fake quantization Sep 24, 2026
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from ca9f302 to 087123e Compare September 24, 2026 06:06
@kaix-nv kaix-nv changed the title Add vLLM GDN/KDA state-only fake quantization [3/5] vLLM GDN/KDA state-only fake quantization Sep 24, 2026
@kaix-nv
kaix-nv added this pull request to stack #2543 September 24, 2026 06:31
@codecov

codecov Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 4.93827% with 77 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.00%. Comparing base (942115a) to head (d532b10).

Files with missing lines Patch % Lines
...orch/quantization/plugins/vllm_linear_attention.py 0.00% 76 Missing ⚠️
modelopt/torch/quantization/plugins/__init__.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@                          Coverage Diff                          @@
##           kaix/linear-attention-qat-example    #2541      +/-   ##
=====================================================================
- Coverage                              71.08%   71.00%   -0.09%     
=====================================================================
  Files                                    658      659       +1     
  Lines                                  72115    72195      +80     
=====================================================================
- Hits                                   51264    51261       -3     
- Misses                                 20851    20934      +83     
Flag Coverage Δ
unit 59.97% <4.93%> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from 087123e to f9d3b35 Compare September 24, 2026 18:17
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from f9d3b35 to 9ccae1f Compare September 25, 2026 01:54
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from 9ccae1f to 3c932d0 Compare September 25, 2026 04:26
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from 3c932d0 to 830a114 Compare September 25, 2026 05:11
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from 830a114 to 1133ecf Compare September 25, 2026 20:57
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch 2 times, most recently from cf7c163 to 2989a37 Compare September 27, 2026 06:41
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from 2989a37 to e22ded5 Compare September 28, 2026 03:29
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from e22ded5 to 2b9e3a3 Compare September 28, 2026 06:05
@kaix-nv
kaix-nv removed this pull request from stack #2543 September 28, 2026 06:06
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from bae8fa2 to 7eec842 Compare October 8, 2026 21:40
kaix-nv added 25 commits October 8, 2026 15:26
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Use registered state quantizers for format and last-axis grouping, with INT8 groups of 16, 32, or 64 independent of execution tile width. Preserve existing tile and Hadamard codecs and document the blockwise configuration.

Keep minimal output/gradient, configuration, and checkpoint coverage; omit redundant mocked block routing and quantizer call-count checks.

Signed-off-by: Kai Xu <kaix@nvidia.com>
Make recurrent_decode the single Torch implementation. Process each prepared prefix directly and remove duplicate packing, shape preparation, and the obsolete helper without changing supported numerical policies.

Validated 40 focused CPU tests, the existing Bridge QAT/QAD GPU smoke tests, and 12 before/after output, state, and gradient comparisons.

Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Unify prefix, decode, and replay execution settings in LinearAttentionConfig with migration for supported nested configs and saved policy objects. Keep formats and grouping in TensorQuantizer.

Retire W-only and reference training paths, move mathematical references under tests, and remove the copied FLA W-QAT implementation and unused recipes.

Validation: 44 focused CPU tests, 6 native and Megatron GPU tests, legacy checkpoint loading, recipe validation, and pre-commit hooks passed.
Signed-off-by: Kai Xu <kaix@nvidia.com>
Normalize recurrent working values to FP32 and require an explicit serving policy for both GDN and KDA state quantization. Remove unpublished config migrations, unused replay-factor plumbing, and the unreferenced Triton INT8 helper while retaining native INT8/Hadamard replay.

Align GDN/KDA API and runtime-state names, document the state handoff, and keep shared TensorQuantizer/FP8 corrections outside this PR. Update recipe guidance and retain minimal real-path tests.

Validation: 28 focused CPU tests, 5 native GPU cases, 2 Megatron QAT/sharded-restore tests, scoped pre-commit hooks, and git diff --check passed.
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Adapt native imports, state layouts, recurrent indexing, and KDA gate arithmetic while keeping ModelOpt state QDQ in canonical layout. Use the vllm profile name and retain vllm_0_15 as a legacy alias. Keep lazy gate discovery outside compiled graphs.

Validate policy matches across distributed stages and correct the optional dependency message. Verified 94 CPU tests, native GDN/KDA GPU checks on vLLM 0.15.1, 0.20.0, and 0.30.0, ReplaySSM cases, Megatron backward/checkpoint restore, and pre-commit hooks. Full serving-engine and model-quality qualification remain separate.

Signed-off-by: Kai Xu <kaix@nvidia.com>
Cache state-layout and kernel-signature checks at first use so Sphinx can import serving adapters with mocked optional dependencies. Preserve runtime dispatch and kernel arithmetic.

Validation: focused recursive Sphinx HTML build using repository configuration and warnings as errors; native metadata and CPU state-layout checks on vLLM 0.15.1, 0.20.0, and 0.30.0; scoped pre-commit and git diff --check.
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Update the linear-attention example for the flat execution config, native state-only training, and checkpoint migration. Remove the retired CPU reference script while retaining its historical results and source link.

Record the focused CPU, native GPU, Megatron checkpoint, and config migration validation. Documentation pre-commit hooks passed.

Signed-off-by: Kai Xu <kaix@nvidia.com>
Document the QDQ scheduling and arithmetic mismatch, the native prefix/handoff/suffix solution, gradient flow, and the limits of current validation. Align configuration guidance with the current state QAT API.

Signed-off-by: Kai Xu <kaix@nvidia.com>
Remove raw experiment JSON from the example and shorten the usage and state-alignment documents. Retain the mismatch derivation, supported policies, concise numerical results, and validation limits. Preserve development evidence in an untracked local archive.

Training code and minimal QAT/QAD tests are unchanged. Validation: scoped pre-commit, documentation links and anchors, Python snippet syntax, archive integrity, and git diff --check.

Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from 7eec842 to d532b10 Compare October 8, 2026 22:40

This branch has not been deployed

No deployments
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