Skip to content

[5/7] vLLM GDN/KDA state-only fake quantization - #2541

Draft
kaix-nv wants to merge 3 commits into
kaix/linear-attention-replayssmfrom
kaix/linear-attention-vllm
Draft

kaix-nv wants to merge 3 commits into
kaix/linear-attention-replayssmfrom
kaix/linear-attention-vllm

Conversation

@kaix-nv

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

Copy link
Copy Markdown
Contributor

Linear-attention series — 7 PRs

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

The six open PRs form native GitHub stack #2789 in the order shown. #2497 has landed, so #2519 targets main. #2788 follows #2657 and #2541 is rebased onto it. #2503 and #2507 retain their existing commits; their rebases remain deferred until their immediate parents merge.

#2788 owns the optional INT8/Hadamard ReplaySSM implementation and the two-precision design plan; two-precision support is not implemented. #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 #2788, which follows #2657. Add state-only fake quantization for vLLM 0.30 Qwen GDN and Kimi KDA layers. Before native layer execution, gather initialized active recurrent-cache slots, apply ModelOpt TensorQuantizer, and write them back. Fresh prefill and inactive slots are unchanged. No CUDA/Triton kernels are added or modified.

The default recipe uses dynamic INT8 blocks of 32 value channels per key row, shared with training. Native value-first cache storage is transposed for QDQ so value grouping remains consistent. FP8 E4M3 is also configurable; the cache remains FP32. QDQ runs before each native state read, including scheduler prompt continuations, not at internal prefill chunk boundaries.

The existing FakeQuantWorker skips calibration data for dynamic recipes and restores state-quantizer names and policies through the HF-to-vLLM mapper. A small TensorQuantizer fix lets dynamic blocks follow changing active-batch shapes. Megatron HF weight exports can be served with the state recipe; their Megatron metadata sidecar is not an HF fakequant export.

Usage

VLLM_GDN_DECODE_KERNEL=triton VLLM_ENABLE_FLA_PACKED_RECURRENT_DECODE=1 \
python examples/vllm_serve/vllm_serve_fakequant.py /path/to/model \
  --modelopt-recipe-path examples/vllm_serve/linear_attention_state_int8.yaml \
  --dtype bfloat16 --enforce-eager --no-async-scheduling --no-enable-prefix-caching \
  --mamba-cache-mode none --mamba-cache-dtype float32 \
  --additional-config '{"gdn_prefill_backend":"triton","kda_prefill_backend":"triton","kda_decode_backend":"triton"}'

Training alignment additionally requires matching prefill scheduling, BF16 projections, unbounded Kimi gates and packed GDN decode. Mixed batches retain native vLLM dispatch and are outside the training profile's arithmetic guarantee. The serving adapter supports eager synchronous execution with PP=DP=CP=1; speculative decoding, prefix caching, state transfer and ReplaySSM are excluded.

Testing

Validated source, rebased onto #2657 0b90b4fea6; published head ef9aa9bec0:

  • Pre-commit passed for the changed files.
  • 79 local CPU checks passed: TensorQuantizer, quantizer reload, linear-attention plugins, and saved-policy name mapping.
  • DFW H100, vLLM 0.30.0, Torch 2.13.0+cu130: 44 TensorQuantizer CPU tests passed. All three focused native-worker/mapping tests passed (job 20128119, 371.44 seconds including startup and compilation), using the compiled ModelOpt CUDA extension. The fixture explicitly selects the same Triton packed-decode settings observed in this run.

The two tiny offline worker cases cover GDN and Kimi-Linear KDA: active-cache QDQ before native execution, untouched inactive/fresh slots, disabled behavior, and restored generation. These are functional tests, not pretrained-quality, cache-compression or performance measurements. TP=1 is tested on this revision.

Earlier attempts are retained in local receipts: the first stopped before collection because the launcher lacked cuDNN discovery settings; the next passed KDA with the Torch QDQ fallback but hit a native GDN compiler error for unequal K/V dimensions. The final fixture uses K=V=32 and a corrected CUDA-header search path.

Before your PR is "Ready for review"

  • Backward compatible: opt-in; native behavior is retained when disabled.
  • Copied code or new dependencies: none.
  • Necessary tests: focused CPU checks and native worker integration.
  • Changelog: updated.
  • Claude approval: not requested for this revision; keep draft.

Additional Information

Prefill GEMM quantization remains deferred to an optimized-kernel follow-up. This PR adds only state fake quantization.

Stack update: rebased onto ReplaySSM head 291b2c1124; this head is 7c6dc6f1b3. Only documentation conflicts required resolution. 90 focused CPU tests passed across state QDQ, ReplaySSM policy, checkpoint lifecycle, recipe docs and TensorQuantizer; changed-file pre-commit and git diff --check passed. No GPU test was rerun for this rebase.

@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
  • Autofix · 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 141688f to bae8fa2 Compare September 29, 2026 01:29
kaix-nv added a commit that referenced this pull request Oct 2, 2026
<!-- linear-attention-stack:start -->
**Linear-attention PR stack — 6 PRs**

| Order | PR | Depends on |
| --- | --- | --- |
| 1/6 | [#2497 GDN state/W QAT
foundation](#2497) | main
|
| 2/6 | [#2519 Torch GDN/KDA decode QAT +
INT8](#2519) | #2497 |
| 3/6 | [#2562 Fused Triton GDN/KDA decode
QAT](#2562) | #2519 |
| 4/6 | [#2541 vLLM GDN/KDA state-only fake
quantization](#2541) |
#2562 |
| 5/6 | [#2503 GDN/KDA prefill GEMM
quantization](#2503) |
#2541 |
| 6/6 | [#2507 Experimental GDN/KDA approximate
inverse](#2507) | #2503 |

All six PRs form native GitHub stack #2563 in the order shown above.
#2541 applies TensorQuantizer before native vLLM prefill/decode calls. A
separate vLLM prefill-GEMM PR waits for an optimized fused kernel. #2506
and #2509 are superseded and closed.
<!-- linear-attention-stack:end -->

### What does this PR do?

Type of change: new feature

GatedDeltaNet training keeps recurrent states inside a chunked kernel,
so projection quantizers cannot emulate rounding at state boundaries.
This PR adds dynamic per-tile FP8 E4M3 fake QDQ to the recurrent state
and independent dynamic FP8 fake QDQ to WY-transformed W activations,
with identity straight-through gradients for QAT/QAD.

Both sites use the standard `quant_cfg` interface and start disabled.
State QDQ uses 64-token chunks and recomputes `amax` at each boundary
over each full-key by 64-value-column tile, independently per sequence
and head. Each tile has its own scalar scale (`amax / 448`, with a zero
guard); `fp8_scalar_qdq` applies that supplied scale rather than
choosing tensor-wide grouping. W grouping is applied by
`TensorQuantizer`. Quantizer settings use normal ModelOpt checkpoint
state. There is no `QuantizeConfig.linear_attention` field in this PR;
#2519 introduces execution policies for decode and ReplaySSM, and later
PRs extend them for prefill and approximate inverse. Configurations or
checkpoints from earlier experimental drafts that use those execution
policies require #2519; those selecting Triton decode also require
#2562.

The Megatron adapter supports the direct-forward and older split-forward
call layouts, restores the original kernel when disabled, and removes
temporary quantizer attributes on export. Independent recurrent/chunk
numerical references live under `tests/_test_utils/torch/quantization/`;
shared runtime capability checks live in `linear_attention/utils.py`.

The fused path requires `fla-core==0.5.1` and chunk size 64. State FP8
emulation requires SM89 or newer. The Hopper path has additional
dtype/TileLang restrictions enforced before launch. This PR simulates
numerical error; it does not add compressed state storage or faster
inference.

### Usage

```python
import modelopt.torch.quantization as mtq

model = mtq.quantize(model, {
    "quant_cfg": [
        {"quantizer_name": "*", "enable": False},
        {"quantizer_name": "*gdn_state_quantizer",
         "cfg": {"num_bits": (4, 3), "type": "dynamic", "axis": (0, 1)}},
        {"quantizer_name": "*gdn_w_quantizer",
         "cfg": {"num_bits": (4, 3), "type": "dynamic", "axis": (0, 1, 2)}},
    ],
    "algorithm": None,
})
# Continue with the framework's normal forward/backward/optimizer steps.
```

Dynamic scales require no calibration.

### Testing

The focused GPU suite contains four cases: three BF16 numerical
forward/backward checks (disabled, W QDQ, and state+W QDQ) using one
shared shape, plus one single-rank, one-layer Megatron QAT/checkpoint
test. The Megatron test checks quantizer enable/disable behavior,
checkpoint restore, gradients, and an optimizer update; it enables state
QDQ when the GPU supports native FP8 conversion. Compilation runs in
setup fixtures, and functional calls retain the normal 120-second
timeout. There are no dtype, layout, tile-width, or parallelism sweeps.

The pinned FLA/TileLang/TVM-FFI dependencies live in the `dev-fla`
optional extra, installed by both GPU nox sessions.

Validation of the consolidated changes on RTX A6000 (SM86), Python
3.12.8, Torch 2.9.1+cu128, Triton 3.5.1, fla-core 0.5.1, TileLang 0.1.8,
Megatron Core 0.19.2, and Transformer Engine 2.16.0:

- Cold and warm focused runs: **3 passed, 1 hardware skip** each. The
state+W numerical case requires SM89+; the local Megatron test exercised
W QDQ.
- Fresh Triton/TileLang cache: **363.09s total**, including setup and
teardown. Kernel setup took 66.38s + 44.46s; Megatron setup, including
shared extension setup and worker startup, took 245.26s. Functional
calls totaled about 2.56s.
- Same cache, new pytest process: **38.20s total**, with about **2.41s
in functional calls**.
- Pre-commit checks passed for the four changed files. Dependency-group
wiring and installed pinned versions were checked.

```bash
PYTHONPATH=. python -m pytest -q \
  tests/gpu/torch/kernels/quantization/linear_attention/test_fla_chunk_gated_delta_rule.py \
  tests/gpu_megatron/torch/quantization/plugins/test_megatron_gated_delta_net.py \
  --durations=0
```

These timings describe local test setup and execution, not inference
performance. Native FP8 state QDQ and Hopper still require suitable
GPU/CI runs. This minimal suite does not qualify tensor/context/pipeline
parallelism, checkpoint resharding, or model-quality recovery. Mamba
compilation coverage is tracked separately in #2572.

### Before your PR is "*Ready for review*"

Contributor and security guidance reviewed. Commits are signed and
signed off.

- Is this change backward compatible?: ✅ Disabled-by-default quantizers,
standard-recipe exclusions, and legacy-checkpoint coverage; enabled
experimental configurations have explicit capability restrictions.
- If you copied code from any other sources or added a new PIP
dependency, did you follow guidance in `CONTRIBUTING.md`: ❌ Internal
third-party approval tracking still needs confirmation. Upstream
attribution, MIT/Apache headers, `LICENSE` notice, and license-hook
exclusions are included. FLA/TileLang and TVM-FFI license files were
reviewed.
- Did you write any new necessary tests?: ✅ Numerical, gradient,
conversion/checkpoint, and real framework tests.
- Did you update Changelog?: ✅ Experimental quantization feature entry.
- Did you get Claude approval on this PR?: ❌ Bot feedback addressed or
discussed; renewed approval pending.

### Additional Information

Related: #2455. This is the first integration slice and does not assume
#2455 has merged. Later milestones will extend the numerical boundaries
after choosing their approximation contracts.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added experimental dynamic FP8 fake quantization for GatedDeltaNet
recurrent states and WY activations during training.
* Added PTQ configuration options for state and WY activation
quantization. State quantization requires an SM89-or-newer GPU; the
fused path requires `fla-core==0.5.1` and a chunk size of 64.
* **Bug Fixes**
* Improved quantizer configuration validation and restoration for
linear-attention models.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Signed-off-by: Kai Xu <kaix@nvidia.com>
@kaix-nv
kaix-nv removed this pull request from stack #2563 October 5, 2026 06:09
@kaix-nv
kaix-nv added this pull request to stack #2563 October 5, 2026 06:11
@kaix-nv
kaix-nv removed this pull request from stack #2563 October 5, 2026 06:15
@kaix-nv
kaix-nv added this pull request to stack #2658 October 5, 2026 06:15
@kaix-nv kaix-nv changed the title [4/6] vLLM GDN/KDA state-only fake quantization [5/7] vLLM GDN/KDA state-only fake quantization Oct 5, 2026
@kaix-nv
kaix-nv removed this pull request from stack #2658 October 8, 2026 18:41
@kaix-nv
kaix-nv changed the base branch from kaix/linear-attention-decode-triton to kaix/linear-attention-qat-example October 8, 2026 18:41
@kaix-nv
kaix-nv added this pull request to stack #2714 October 8, 2026 18:41
@kaix-nv kaix-nv changed the title [5/7] vLLM GDN/KDA state-only fake quantization [4/6] vLLM GDN/KDA state-only fake quantization Oct 8, 2026
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch 2 times, most recently from 7eec842 to d532b10 Compare October 8, 2026 22:40
@kaix-nv
kaix-nv force-pushed the kaix/linear-attention-vllm branch from d532b10 to ef9aa9b Compare October 13, 2026 19:23
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 ef9aa9b to 7c6dc6f Compare October 14, 2026 06:27
@kaix-nv
kaix-nv removed this pull request from stack #2714 October 14, 2026 06:28
@kaix-nv
kaix-nv changed the base branch from kaix/linear-attention-qat-example to kaix/linear-attention-replayssm October 14, 2026 06:29
@kaix-nv
kaix-nv added this pull request to stack #2789 October 14, 2026 06:29
@kaix-nv kaix-nv changed the title [4/6] vLLM GDN/KDA state-only fake quantization [5/7] vLLM GDN/KDA state-only fake quantization Oct 14, 2026

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