Skip to content

Add vLLM skip-softmax and mask-reuse calibration - #2045

Draft
kaix-nv wants to merge 16 commits into
mainfrom
kaix/mask-reuse-calib
Draft

Add vLLM skip-softmax and mask-reuse calibration#2045
kaix-nv wants to merge 16 commits into
mainfrom
kaix/mask-reuse-calib

Conversation

@kaix-nv

@kaix-nv kaix-nv commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: new feature, new example, new tests, documentation.

This PR adds ModelOpt-owned sparse-attention calibration through vLLM V1 in two stages:

  • Skip-softmax calibration measures paged-KV tile counts while preserving dense attention output, merges tensor-parallel counts, and exports the canonical per-phase exponential threshold fit.
  • Cross-layer mask-reuse calibration collects compact policy-free sufficient statistics, binds them to exact checkpoint and evidence hashes, selects target sparsity plus donor/fallback mappings, and exports a fail-closed candidate artifact.

The serving boundary is explicit: mask-reuse output remains candidate-only until the grouped inner-CV, outer-group, and deployment-geometry promotion gates are implemented and pass. Decode remains dense, and calibration does not enable attention quantization.

The implementation also hardens fixed 128x128 tile compatibility, prefix/cache routing, tensor-parallel shape/count validation, atomic no-clobber publication, and exact-byte mutation detection.

Usage

The end-to-end workflows and command-line examples are documented in examples/vllm_serve/README.md:

  1. Run calibrate_sparse_attn.py to produce the vanilla threshold fit.
  2. Run create_checkpoint_manifest.py for the exact local checkpoint.
  3. Run collect_mask_reuse.py through the custom vLLM V1 backend.
  4. Run calibrate_mask_reuse.py to export a candidate policy and report.

Testing

  • Full pre-commit suite, including Ruff, mypy, Bandit, license, Markdown, and repository validators.
  • tests/unit/torch/sparsity/attention_sparsity: 232 passed.
  • Independent adversarial audit of checkpoint/evidence binding, mutation windows, no-clobber publication, candidate rejection, and schema-v2 cross-repository compatibility.
  • B200 calibration-mass correctness and actual pinned FA4 natural-log LSE integration were validated separately; the real TP>=2 Qwen3 and Nemotron 3 Ultra capture campaign is the next experimental gate.

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: N/A
  • Did you write any new necessary tests?: ✅
  • Did you update Changelog?: ✅
  • Did you get Claude approval on this PR?: N/A

Additional Information

This is intentionally a draft while the real-model TP>=2 capture and grouped promotion evidence are collected. No promoted serving policy or end-to-end speedup claim is included in this PR.

kaix-nv added 9 commits August 1, 2026 06:56
- Route active skip-softmax launches (prefill and paged decode) through the
  fixed 128x128 calibration tile instead of the autotuner, so realized
  sparsity matches the granularity thresholds were calibrated at
  (autotuned BLOCK_N=32 tiles skip differently than the 128x128
  measurement/calibration blocks). BLOCK_M steps down on shared-memory
  pressure (fp32 inputs on ~100KB-smem GPUs) with a warning; BLOCK_N -- the
  calibrated KV skip granularity -- is never reduced.
- flash_skip_softmax: exclude padded query rows from the block keep
  decision; fully-padded rows voted keep (block_diff == 0) and forced the
  last partial block row dense, under-counting sparsity.
- vLLM runtime: validate the calibrated-decode CUDA-graph guard for
  sparse-only installs, not just quantized ones.

Signed-off-by: Kai Xu <kaix@nvidia.com>
- attention_calibrate: read K/V through a paged cache (vLLM NHD layout) via
  block_table, reusing the shared paged tile loaders. Contiguous-KV behavior
  is unchanged; paged and contiguous launches produce identical counters and
  output.
- vLLM adapters: calibration mode (enable_calibration / disable_calibration /
  iter_sparse_impls). When active, forward routes to the paged calibration
  kernel -- full dense attention plus multi-threshold tile-skip counting --
  so generation is numerically unchanged while stats accumulate. Each
  scheduled request is measured independently (decode vs prefill phase per
  request), and raw per-threshold tile counts are recorded so
  tensor-parallel ranks can be aggregated by summing counts before the fit.
- FlashInfer adapter writes the current K/V to the cache before the
  calibrate kernel reads it (releases that update the cache inside forward);
  fp16/bf16-only and NHD-only, both validated explicitly.

Signed-off-by: Kai Xu <kaix@nvidia.com>
…mple

- install_vllm_skip_softmax_calibration: validation-before-mutation install
  of calibration adapters on every attention layer -- requires eager
  execution, fp16/bf16 model and KV dtypes, no active attention Q/K/P/V
  fakequant, and a FlashAttention/FlashInfer backend; disables cascade.
  Measurement starts separately (enable_calibration RPC), so warmup
  launches are never recorded.
- DynamicThresholdCalibrator.calibrate_from_stats: backend-agnostic fit
  stage extracted from calibrate(), preserving result fields
  (fit_logspace, log_a) and reporting per-sample sparsity; the HF path
  delegates to it unchanged.
- plugins/sparse_attn_calibration (vLLM-free): raw tile-count merging
  across layers and tensor-parallel ranks (counts are additive; ratios are
  formed only after the global merge, one fit per phase -- never per rank,
  never by averaging fitted coefficients), plus a canonical
  sparse_attention_config builder matching export_sparse_attention_config
  so the serving loader round-trips it; existing N:M groups are preserved.
- Thin example: SkipSoftmaxCalibWorker (load hook + enable/status/counts
  RPCs) and calibrate_sparse_attn.py driver (CLI, prompts, rank-count
  aggregation, fit, checkpoint-config writing).

Signed-off-by: Kai Xu <kaix@nvidia.com>
Tests:
- Paged calibrate kernel: paged == contiguous (counters bit-equal, output
  match) across aligned and non-128-aligned lengths with shuffled block
  tables; decode-shaped measurement covers the full cache; high-block-ID
  pointer regression (int64 paged offsets, memory-gated).
- Calibration/serving tile contract: an active skip-softmax launch skips
  exactly the tiles the calibration kernel counted at the same threshold
  (same 128x128 geometry, same prefix-max criterion); skip composes with
  P/V QDQ (sm89+).
- Adapter forward: mixed prefill/decode batches record per-request phases
  and raw counts while output stays dense vs an SDPA reference; NHD and
  fp16/bf16 cache validation; layer count summing; FlashInfer cache write
  ordered before the calibrate-kernel read.
- Installer: install-without-measure lifecycle, eager/quantizer/fp8-KV/
  no-layer rejections with validation-before-mutation, and the sparse-only
  CUDA-graph guard for calibrated decode configs.
- vLLM-free helpers: count merging/alignment, TP-rank aggregation,
  synthetic exponential fit recovery, calibrate_from_stats field contract,
  canonical config round trip through the serving loader with N:M group
  preservation.

Docs: vLLM calibration workflow section in examples/vllm_serve/README.md.
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Build calibration prompts with RulerDatasetBuilder — the same dataset the
PyTorch (HF) calibration path uses — so vLLM- and HF-calibrated thresholds
are fit on identical data (defaults mirror the HF path: 24 samples,
max_seqlen 32768; NIAH essay haystack via download_ruler_data.sh and
--calib_data_dir). The built-in demo prompt fallback is removed;
--prompts_file remains as an explicit override for custom calibration data.

Signed-off-by: Kai Xu <kaix@nvidia.com>
…a-flow hardening

- Reject skip-softmax combined with ANY attention quantization at plan time:
  quantized Q/K/P change the score distribution the thresholds were
  calibrated on (N:M sparse softmax still composes with quantization).
- The 128x128 skip tile is now a hard contract: configurations that cannot
  compile it (fp32 on ~100KB-smem GPUs) are rejected with a clear error
  instead of re-tiled (the BLOCK_M step-down changed realized sparsity).
- FlashInfer layout: query vLLM layout metadata and reject HND at install
  and before the calibration cache write; the NHD shape check remains as a
  fallback (it is ambiguous when page_size equals the per-rank KV heads).
- One canonical threshold sweep shared by the HF and vLLM calibration paths
  (DEFAULT_THRESHOLD_TRIALS hoisted from DynamicThresholdCalibrator);
  ignore_eos forces the full decode length; the driver exits nonzero unless
  every requested phase produced a valid fit (no silent partial export).
- Count merging treats alignment as a contract: mismatched sample counts,
  lengths, threshold widths, or per-rank/per-layer phase coverage raise
  instead of silently truncating.
- Preserve legacy top-level sparse_softmax metadata through recalibration.
- Docs: no-sparsification wording (kernel numerics differ from the native
  backend); clarify prefix-caching support (sparse-only serving supports
  suffix attention; quantized installs and calibration reject it).
- High-block-ID regression test halves its allocation (V aliases K storage).

Signed-off-by: Kai Xu <kaix@nvidia.com>
- Skip-softmax + attention quantization is now unreachable from every
  direction: sparse-only installs reject calibrated skip onto layers with
  active attention quantizers (not just quantized installs adding skip),
  and the raw kernel API itself rejects P/V QDQ with an active skip
  threshold -- which makes the P-QDQ 16-row measurement tile dead code, so
  the fixed skip tile is unconditionally 128x128.
- FlashInfer layout helper preserves a genuine None from
  get_kv_cache_layout (str(None) became the truthy string None and both
  guards hard-rejected valid configurations instead of using the shape
  fallback).
- Counter vectors are validated against len(threshold_trials) in both
  calibrate_from_stats and fit_from_counts: consistently short vectors
  previously zipped silently and could misattribute sparsities.
- Driver decode semantics match vLLM: the first output token comes from
  the prefill forward, so --decode_tokens now means decode-attention steps
  and generation runs decode_tokens + 1 output tokens.
- Calibrate kernel mirrors the serving kernel IEEE fp32 QK dot so raw fp32
  calibration and serving round near-threshold scores identically.
- target_sparsity validated to [0, 1] (same range as the HF config).

Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
@copy-pr-bot

copy-pr-bot Bot commented Aug 1, 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 Aug 1, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: cdfae0fc-33c3-420b-af31-b98db92b5477

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://NVIDIA.github.io/Model-Optimizer/pr-preview/pr-2045/

Built to branch gh-pages at 2026-08-01 18:04 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

kaix-nv added 3 commits August 1, 2026 07:47
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 78.13688% with 575 lines in your changes missing coverage. Please review.
✅ Project coverage is 70.31%. Comparing base (9d360af) to head (86c0306).

Files with missing lines Patch % Lines
...y/attention_sparsity/plugins/mask_reuse_capture.py 79.15% 108 Missing ⚠️
...rsity/attention_sparsity/calibration/mask_reuse.py 84.23% 99 Missing ⚠️
...tention_sparsity/calibration/mask_reuse_compact.py 85.09% 86 Missing ⚠️
.../torch/sparsity/attention_sparsity/plugins/vllm.py 0.00% 80 Missing ⚠️
.../attention_sparsity/calibration/source_manifest.py 80.68% 68 Missing ⚠️
...ention_sparsity/calibration/checkpoint_manifest.py 79.91% 50 Missing ⚠️
...parsity/attention_sparsity/plugins/vllm_runtime.py 0.00% 49 Missing ⚠️
...lopt/torch/kernels/sparsity/attention/calibrate.py 4.16% 23 Missing ⚠️
...ention_sparsity/plugins/vllm_mask_reuse_capture.py 72.22% 10 Missing ⚠️
...delopt/torch/kernels/common/attention/triton_fa.py 80.00% 1 Missing ⚠️
... and 1 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2045      +/-   ##
==========================================
+ Coverage   69.97%   70.31%   +0.34%     
==========================================
  Files         519      526       +7     
  Lines       59399    62014    +2615     
==========================================
+ Hits        41565    43607    +2042     
- Misses      17834    18407     +573     
Flag Coverage Δ
unit 56.13% <78.13%> (+0.97%) ⬆️

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 added 2 commits August 1, 2026 09:05
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
@kaix-nv
kaix-nv force-pushed the kaix/mask-reuse-calib branch from fa89806 to f3de073 Compare August 1, 2026 16:34
kaix-nv added 2 commits August 1, 2026 10:07
Signed-off-by: Kai Xu <kaix@nvidia.com>
Signed-off-by: Kai Xu <kaix@nvidia.com>
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