Skip to content

feat: collect vLLM w4a16_nvfp4 GEMM rows for Nemotron 3.5 Lightning - #645

Draft
milesial wants to merge 6 commits into
mainfrom
ao/w4a16-nvfp4-gemm/lightning-w4a16-gemm
Draft

milesial wants to merge 6 commits into
mainfrom
ao/w4a16-nvfp4-gemm/lightning-w4a16-gemm

Conversation

@milesial

Copy link
Copy Markdown
Contributor

Nemotron 3.5 Lightning NVFP4 runs its shared-expert projections as ModelOpt W4A16_NVFP4: 4-bit NVFP4 weights with BF16 activations. The performance model already asks for these GEMMs in the w4a16_nvfp4 mode. Before this PR, the vLLM GEMM collector did not measure that mode. Strict SILICON queries for Lightning failed with "GEMM perf data missing for quant 'w4a16_nvfp4'". After this PR, the collector measures the mode through vLLM's own ModelOpt dispatcher. The PR also adds measured rows for B200 and GB200. On these two GPUs, the Lightning shared-expert GEMM lookups at vLLM 0.30.0 now return measured data.

What changed

  • collector/vllm/collect_gemm.py: a new w4a16_nvfp4 lane. It builds the layer with ModelOptMixedPrecisionConfig, as a Lightning MIXED_PRECISION checkpoint does, so vLLM selects the kernel. kernel_source records the kernel that vLLM ran. If vLLM builds a layer that quantizes activations, the run fails.
  • Synthetic weights use the checkpoint layout that the ModelOpt loader fills. On SM100 and newer GPUs, vLLM's scaled_fp4_quant makes them. On older GPUs, vLLM's PyTorch reference quantizer makes them, because scaled_fp4_quant needs an SM100 kernel.
  • collector/cases/models/NemotronHForCausalLM_cases.yaml: the six Lightning shared-expert (n, k) sites for TP1, TP4 and TP8.
  • collector/cases/capabilities.yaml: w4a16_nvfp4: 80 (BF16 activations).
  • collector/kernel_source_backends.yaml and collector/op_backend_facts.yaml: entries for the new kernel labels and data slices.
  • vLLM 0.30.0 is admitted for the GEMM collector, because it is the current vLLM query version.
  • Synthetic GEMM inputs and weights use a fixed seed.

Evidence

Each cell ran the unchanged collect.py entry point with 444 exact case filters (6 sites x 74 token counts). Each cell kept 444 cases and recorded 0 failures. All rows have the collector commit 9d81c45, collector hash sha256:3e50fb87, and case plan hash sha256:0da6da79.

System vLLM Image (architecture digest) Kernel that vLLM selected In this PR
b200_sxm 0.24.0 f9de5cd9 MarlinNvFp4LinearKernel No (see limits)
gb200 0.24.0 32445b36 MarlinNvFp4LinearKernel No (see limits)
b200_sxm 0.30.0 5f5e5352 FlashInferCuteDslNvFp4W4A16LinearKernel Yes, merged into the shipped table
gb200 0.30.0 4864d466 FlashInferCuteDslNvFp4W4A16LinearKernel Yes, merged into the shipped table
b200_sxm 0.31.0 a4a4c043 FlashInferCuteDslNvFp4W4A16LinearKernel Yes, new w4a16_nvfp4-only table
gb200 0.31.0 3f7dd5b7 FlashInferCuteDslNvFp4W4A16LinearKernel No (see limits)
  • In every cell, the kernel received BF16 input, and the layer kept no activation scale.
  • The two merged 0.30.0 tables went from 148962 to 149406 rows. Every shipped row and every earlier collection event is unchanged. collect.py appended one event. The text columns are now stored as large_string, and their values are unchanged.
  • Strict SILICON queries (shared layer off) of the two TP1 keys at the eight token counts that the gate uses return the stored rows exactly: 16 of 16 on b200_sxm 0.30.0, gb200 0.30.0 and b200_sxm 0.31.0.
  • check_collector_data.py and backend_facts.py --check pass.
  • A short validation on an H100 (SM90) checked the weight preparation for GPUs older than SM100, on vLLM 0.24.0 and 0.30.0. The reference quantizer made a uint8 weight of shape (n, k/2) and float8 scales of shape (n, k/16). vLLM selected MarlinNvFp4LinearKernel at both versions, and both cases ran with 0 failures. These rows are evidence only and are not in this PR.

Prediction Regression Gate, run locally on vLLM 0.30.0 for b200_sxm, b300_sxm, gb200, gb300 and rtx_pro_6000_server, with #617's corrected candidate plus this PR:

  • The 45 blocking rows stay blocking. On b200_sxm and gb200, the first failure moves from the dense GEMM to the MoE w4a16_nvfp4 lookup (TP1, EP8). On the other three systems, the GEMM lookup still fails.
  • No other gate row changes.

Known limits

  • vLLM 0.24.0 rows are measured but not in this PR. The 0.24.0 GEMM tables have a legacy provenance sidecar, and the collector refuses to add a fresh collection to a legacy sidecar.
  • GB200 vLLM 0.31.0 rows are measured but not in this PR. The shipped table stores text as large_string, and the collector merge replaces such a table instead of adding to it. A separate fix is needed first.
  • B300, GB300 and RTX PRO 6000 have the same Lightning gap on vLLM 0.30.0. This PR does not collect them.
  • After feat: price checkpoint precision per layer for Nemotron-H, Qwen3.5 and Gemma 4, and collect vLLM NVFP4 MoE #617 keeps Lightning experts on w4a16_nvfp4, the MoE lookups also need measured W4A16 MoE rows. This PR does not add them.
  • The tests for the pre-SM100 weight preparation need torch. The default development environment does not install torch, so CI skips them.
  • The evidence policy flags many collector families, because the model case YAML is shared by every collector. Only the GEMM case plans change: +444 cases (+126 for vLLM XPU).

Nemotron 3.5 Lightning NVFP4 declares its shared-expert projections as
ModelOpt W4A16_NVFP4. The SDK already queries them as the w4a16_nvfp4
GEMM mode, but the vLLM GEMM collector could not measure that mode.

The collector now builds this lane through vLLM's own ModelOpt
MIXED_PRECISION dispatcher. Activations stay BF16. kernel_source records
the kernel that vLLM selects: Marlin at 0.24.0-0.27.1, and FlashInfer
CuTe-DSL W4A16 or Marlin at 0.30.0/0.31.0. The run raises if vLLM builds
an activation-quantized layer.

- Add the six Lightning shared-expert (n, k) sites at TP1/4/8 to the
  NemotronH GEMM model cases.
- Add the w4a16_nvfp4 SM80 capability floor and the two kernel-source
  backend entries.
- Admit vLLM 0.30.0, the current query-slot version.
- Seed the synthetic GEMM inputs and weights.

Signed-off-by: Alexandre Milesi <milesial@users.noreply.github.com>
The GEMM collector now admits vLLM 0.30.0. Update the three tests that pin
its exact admitted release set. collect_moe keeps its own range.

Signed-off-by: Alexandre Milesi <milesial@users.noreply.github.com>
…00 and GB200

Add 444 measured w4a16_nvfp4 GEMM rows per table: the six Lightning
shared-expert (n, k) sites at TP1/4/8, each over the 74 base token counts.

- b200_sxm and gb200, vLLM 0.30.0: collect.py merged the rows into the
  shipped tables (148962 -> 149406 rows) and appended one collection event.
  The shipped rows, runtime and earlier events are unchanged. The text
  columns are now stored as large_string; their values are unchanged.
- b200_sxm, vLLM 0.31.0: a new table with only the w4a16_nvfp4 rows. Other
  GEMM dtypes for this version are not measured here.

vLLM selected FlashInferCuteDslNvFp4W4A16LinearKernel at both versions on
both GPUs. Collector commit 9d81c45, collector_hash sha256:3e50fb87,
case_plan_hash sha256:0da6da79, seed 0, 0 failed cases. The op-backend facts
registry gets the three new slices.

Signed-off-by: Alexandre Milesi <milesial@users.noreply.github.com>
The w4a16_nvfp4 lane runs from SM80, because Marlin FP4 supports GPUs
from SM75. Its weight preparation called vLLM's scaled_fp4_quant, which
needs an SM100+ quantization kernel (nvfp4_quant_sm_supported). So the
lane failed on A100, L40S, H100 and H200 before vLLM ran the GEMM.

Below SM100, the collector now builds the same checkpoint layout with
vLLM's PyTorch reference quantizer, ref_nvfp4_quant. The body of that
function is the same in every admitted release. The collector packs the
E2M1 values low nibble first, in the layout of vLLM's break_fp4_bytes.
On SM100+, the lane keeps scaled_fp4_quant with the same arguments as
before.

Tests use real torch tensors. They decode packed bytes with an
independent E2M1 decoder, and they check that below SM100 the reference
quantizer runs with the ModelOpt global scale and scaled_fp4_quant does
not run. The tests are skipped when torch is not installed.

Signed-off-by: Alexandre Milesi <milesial@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 29, 2026

Copy link
Copy Markdown

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.

Other collector test modules cache a MagicMock under sys.modules["torch"]
at collection time. Under xdist, the w4a16_nvfp4 weight tests could then
build their tensors with that mock and fail in CI.

The tests now check the installed torch distribution instead of
sys.modules. A fixture installs the real torch module for each test. It
reuses an already initialized torch, because torch can be initialized only
once per process.

Signed-off-by: Alexandre Milesi <milesial@users.noreply.github.com>
The application test inventory accepts module-level collection skips only
for listed modules. Without torch, the new test module skipped at
collection time and failed the contracts job.

The torch fixture now skips each test when the torch distribution is not
installed, with the reason that the other torch-dependent collector tests
use. The module is always collected.

Signed-off-by: Alexandre Milesi <milesial@users.noreply.github.com>
milesial added a commit to milesial/aisimulate-public that referenced this pull request Oct 30, 2026
… 3.5 Lightning

Bring in ai-dynamo#645 at c9465fb (and main up to its base add6fca, which adds the
vLLM 0.30.0 tables its rows extend). The collect_gemm w4a16_nvfp4 lane and
its b200_sxm/gb200 vLLM 0.30.0 and b200_sxm 0.31.0 rows serve the dense
W4A16_NVFP4 shared-expert GEMMs that ai-dynamo#617 prices per layer. Merged without
conflicts.

Signed-off-by: Alexandre Milesi <milesial@users.noreply.github.com>
Signed-off-by: milesial <milesial@users.noreply.github.com>
@carlory carlory mentioned this pull request Oct 30, 2026
12 tasks

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