mudler / mudler/vllm.cpp

perf(KERNEL-GEMM-NVFP4-W4A4): the tactic tuner inherits FlashInfer's single-draw timing and has no draw-quality selector

Open
#2,751 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
423
Forks
53
Avg merge
20h 26m
Merged PRs (30d)
310

Description

Row: KERNEL-GEMM-NVFP4-W4A4

Corrected 2026-09-03. The original filing described the per-candidate
aggregation as "min-of-10" and proposed swapping it for a median. That was wrong,
and the correction changes what an implementer would build. The mechanism below is
read from the tree.

What the tuner actually does

Per candidate tactic (src/vt/cuda/cuda_matmul_nvfp4_cutlass.cu:324-356):

  • 3 warmup launches (kWarmupIterations);
  • cudaStreamSynchronize, then a one-thread GPU delay kernel of
    VT_FP4_AUTOTUNE_DELAY_US (default 5,000) microseconds, so host enqueue latency cannot
    bias near-tied tactics;
  • one cudaEvent pair spanning all 10 iterations (kTimingIterations), then
    elapsed_ms / kTimingIterations.

That last line is the load-bearing correction: the result is a mean, and because a
single event pair spans the whole loop, no per-iteration timing exists at all. There is
no distribution to take a median or a minimum of. Producing one requires adding
instrumentation, not changing a reduction.

Across candidates (:758-765): strict < argmin over those means, first-registered wins a
tie. kFp4Inf marks a candidate that rejected the shape.

The part that makes this worth a row

A variance damper for exactly this problem already exists in the tree, and it is not in
the default arm (:766-775):

// W1 used a >1% threshold relative to its fixed M baseline. Preserve that
// only in the fallback arm. FlashInfer's full autotuner chooses the
// minimum valid event time directly.

In the legacy VT_FP4_FULL_TACTICS=0 arm (4 candidates), a tactic only displaces the fixed
baseline if it beats it by more than 1%; otherwise the baseline is kept. That is deliberate
stickiness against sub-1% noise.

Fp4FullTacticsEnabled() (:189-195) is value == nullptr || value[0] != '0'default
ON
. So the shipped default is the 32-candidate arm with pure argmin and no damper,
while the non-default arm has one.

The question this row owes an answer to is therefore sharper than "add a selector": does
the default arm need the stickiness the legacy arm already has?
The in-tree comment says
the answer was "no" because FlashInfer picks the minimum directly. That is a mirror
argument, not a measurement, and the measured draw spread below is the evidence that was
never brought against it.

Evidence the draw actually varies

From .agents/specs/nvfp4-persistent-plan-cache.md, re-tuning in every process:

  • paired direct/fallback runs shared only 18-33 of 64 selected tactic IDs;
  • only 9-17 keys per c16 arm and 12-15 per c2 arm held one ID across three repetitions;
  • 5 of 6 paired 128-token output hashes differed.

Wide in identity. Never measured in speed — the draws may be performance-equivalent
selections that differ only in reduction order.

What would close this

  1. Measure first. Collect N independent draws on one leased GB10, then A/B the draws
    against each other on a fixed workload. If they are performance-equivalent, this closes as
    "no change warranted" and the persistent cache alone is the right answer.
  2. Only if a real spread appears, ask the developer to ratify one of:
    • extending the legacy arm's >1% stickiness to the default arm, or
    • adding per-iteration events so a robust statistic is computable at all.
      Both diverge from the pinned FlashInfer oracle for the default arm, so both are product
      decisions. Compute authority for step 1 is recorded; ratification for step 2 is explicitly
      not granted and will be asked for with the measurement in hand.

Not in scope

Copying the "clear, boot, measure, keep the first draw above X" practice some downstream
runtimes publish. Selecting a kernel plan on the same workload it will later be scored on is
measuring around the harness, and this repository's protocol refuses it. Any selector must
pick on a workload disjoint from the gate.

Baseline to beat

Frozen-plan steady-state component on this lane: c2 1.0045x / c16 1.0050x, strict result
FAILED at 39/40 timing + 1/8 memory. Persistence is credited as a control, not a speedup. A
selector must clear a bar meaningfully above that to justify a divergence.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/vt/cuda/cuda_matmul_nvfp4_cutlass.cu:324-356 and :758-775, then read .agents/specs/nvfp4-persistent-plan-cache.md. Collect independent draws on one leased GB10 and A/B them on a fixed workload disjoint from the gate. Done means demonstrating whether a real performance spread exists; only then can a ratified selector or stickiness change be considered against the stated baseline.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
performance
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.