Skip to content

dense_nvfp4_gemm.h caches the Marlin repack in a process-static map keyed on the weight's ADDRESS, the stale-pointer hazard ResidentSlot exists to fix #984

Description

@localai-bot

Claim

include/vllm/model_executor/models/dense_nvfp4_gemm.h:379-384 caches the dense Marlin repack in a process-static map keyed on the weight object's address.

An address is only a valid identity while the object lives. Across two engine builds in one process — the second engine's weights allocated where the first's used to be — a hit on that map returns the previous engine's repacked device buffer. The result is numerically plausible and silently wrong: right shapes, right dtypes, wrong values.

Why this is a known class, not a new theory

#237 exists for precisely this, and ResidentSlot is the fix it produced. qwen3_5.cpp:2429-2431 already uses ResidentSlot, and Nvfp4Weight carries an unused resident_marlin slot — so the correct mechanism is present in the type and simply not used by the shared header.

I could not find this instance filed anywhere; #237 tracks the class and the qwen3_5.cpp call site, not dense_nvfp4_gemm.h.

Why it matters more now than when it was written

The header was, until recently, effectively single-consumer. Two things changed:

  1. dense_nvfp4_gemm.h is a shared seamResidentNvfp4, MatmulNvfp4MarlinD, MatmulNvfp4W4A16D plus a policy layer at compressed_tensors/schemes/nvfp4.h. It is the seam every NVFP4 consumer is told to route through rather than hand-rolling a parallel path.
  2. MODEL-NEMOTRON-H (Run Nemotron-3.5-Lightning-30B-A3B-NVFP4 end to end: NemotronH hybrid (23 Mamba2 / 6 GQA / 23 non-gated relu² MoE) with a MIXED_PRECISION checkpoint #517, The paged runner's RECURRENT allocation is Qwen3.5-shaped: hybrid models are refused by the wrong model's name, and the only correct behaviour reads as a user config error #810) is about to route lm_head and 5935 expert projections through it. That is a second engine class in the same process space, which is exactly the condition the address key cannot survive.

So the hazard moves from latent to reachable as the seam gains its second consumer.

What done looks like

  1. Key the cache on ResidentSlot rather than the weight address, mirroring qwen3_5.cpp:2429-2431, and use the resident_marlin slot Nvfp4Weight already carries.
  2. A red-before test: build two engines in one process such that the second's weight lands at a recycled address, and show the current code returns the first engine's buffer. That test is the whole value of the fix — without it this is a refactor with no evidence, and the defect is invisible to any single-engine gate.
  3. Consider whether a structural checker belongs here, in the shape of scripts/check-fp4-resident-consistency.py, which already enforces a neighbouring obligation on ResidentNvfp4 — an address-keyed static in a shared header is a pattern worth refusing by construction rather than by review.

Found while scoping A2-Q of #810. Not fixed there: it is a behaviour change with its own gate, and folding it into a model row would land it without the two-engine test that gives it meaning.

Related: #237 (the class and ResidentSlot), #940 (the FP8 seam extraction, which deliberately left an adjacent latent defect to #974 for the same reason), #974.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions