Skip to content

Adds skip-softmax calibration through the vLLM serving path#1992

Draft
kaix-nv wants to merge 8 commits into
mainfrom
kaix/vllm_skip_calib_upstream
Draft

Adds skip-softmax calibration through the vLLM serving path#1992
kaix-nv wants to merge 8 commits into
mainfrom
kaix/vllm_skip_calib_upstream

Conversation

@kaix-nv

@kaix-nv kaix-nv commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: ?

new feature
Adds skip-softmax threshold calibration through the vLLM serving path, so thresholds can be calibrated on the same inference engine they will be served with.

  • Active skip-softmax launches (prefill and paged decode) now run on the fixed 128×128 calibration tile instead of the autotuner's configs. The tile-skip decision depends on (BLOCK_M, BLOCK_N) geometry, and thresholds are calibrated at 128×128.

Usage

# End-to-end calibration:
python examples/vllm_serve/calibrate_sparse_attn.py <CKPT> \
  --prompts_file prompts.txt --target_sparse_ratio 0.7 --fit_logspace \
  --tensor_parallel_size 4 --decode_tokens 32 --update_checkpoint_config

Testing

Before your PR is "Ready for review"

Make sure you read and follow Contributor guidelines and your commits are signed (git commit -s -S).

Make sure you read and follow the Security Best Practices (e.g. avoiding hardcoded trust_remote_code=True, torch.load(..., weights_only=False), pickle, etc.).

  • Is this change backward compatible?: ✅ / ❌ / N/A
  • 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?: ✅ / ❌ / N/A
  • Did you update Changelog?: ✅ / ❌ / N/A
  • Did you get Claude approval on this PR?: ✅ / ❌ / N/A

Additional Information

kaix-nv added 4 commits July 17, 2026 20:49
- 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>
@copy-pr-bot

copy-pr-bot Bot commented Jul 19, 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 Jul 19, 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: 4c0d3c15-4ecb-4b4f-b083-f66e1f2890fc

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
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kaix/vllm_skip_calib_upstream

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

@github-actions

github-actions Bot commented Jul 19, 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-1992/

Built to branch gh-pages at 2026-07-20 04:38 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@codecov

codecov Bot commented Jul 19, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 41.46341% with 144 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.64%. Comparing base (9392dfe) to head (276e0c5).

Files with missing lines Patch % Lines
.../torch/sparsity/attention_sparsity/plugins/vllm.py 0.00% 79 Missing ⚠️
...parsity/attention_sparsity/plugins/vllm_runtime.py 0.00% 43 Missing ⚠️
...lopt/torch/kernels/sparsity/attention/calibrate.py 4.76% 20 Missing ⚠️
...delopt/torch/kernels/common/attention/triton_fa.py 75.00% 1 Missing ⚠️
...y/attention_sparsity/methods/flash_skip_softmax.py 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1992      +/-   ##
==========================================
- Coverage   77.79%   77.64%   -0.15%     
==========================================
  Files         518      519       +1     
  Lines       58204    58437     +233     
==========================================
+ Hits        45279    45375      +96     
- Misses      12925    13062     +137     
Flag Coverage Δ
unit 54.75% <41.46%> (-0.05%) ⬇️

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 4 commits July 18, 2026 21:27
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>
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