Report: NVFP4 kernel selection on SM120/SM121 (results, no code changes) - #10417
Open
danielhanchen wants to merge 1 commit into
Open
Report: NVFP4 kernel selection on SM120/SM121 (results, no code changes)#10417danielhanchen wants to merge 1 commit into
danielhanchen wants to merge 1 commit into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Member
Author
|
@codex security review |
|
Codex Review: Didn't find any major issues. Swish! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Results only, no code changes.
Update 2026-09-08: bug 1 has been fixed upstream. vllm#55170 merged 2026-09-08 as
13cf9e05cand moves the weight only kernel below all three W4A4 kernels. The workaround below is still required on every build that predates that commit, which includes every tag up to and includingv0.29.0. Bug 2 is unchanged and still live on main.On SM120/SM121 (RTX 50, RTX PRO 6000, DGX Spark), vLLM does not use the W4A4 kernels for our NVFP4 quants by default. Two upstream bugs send them down slower paths. Use these flags:
Verified effect on kernel choice:
The two bugs
882ca8d696(vllm#53014, 2026-08-31) added the kernel at position 2 of_POSSIBLE_NVFP4_KERNELS[CUDA]with anis_supported()accepting sm_12x. First match wins. The same PR gates its explicit weight only branch to sm_100/103, so the auto list entry contradicts its own intentv0.28.0,v0.29.0rc4andv0.29.0do not contain the class13cf9e05c), W4A16 kernel moved to position 5 below all three W4A4 kernels. Not in any tag yetB12XMoE backend is never auto selected, falls back to Marlin2740c817ff(vllm#52018) added the enum, kernel mapping and alias but never added B12X toAVAILABLE_BACKENDSv0.28.1rc0onward, includingv0.29.0. Not inv0.28.0, which predates the backendAVAILABLE_BACKENDSon mainUpstream state as of 2026-09-08:
Note
FLASHINFER_B12Xis excluded from auto selection deliberately and is documented as such, though only in a source comment. Do not force it, it crashes with an illegal memory access on SM121. PlainB12Xis the one that should be selected.SM120 versus SM121, and a divergence worth recording
vLLM treats sm_120 and sm_121 as one family throughout. There is no exact sm_121 test anywhere in the NVFP4 selection path; the deciding gate is
Platform.is_device_capability_family, which is literallyThat is the direct analogue of
blackwell_mma_available(cc)on the llama.cpp side, which also covered all of sm_12x. The two stacks have now diverged. In unslothai/llama.cpp#198 that gate was narrowed to an exact equality test onGGML_CUDA_CC_DGX_SPARK(1210), so it is sm_121 only. vLLM has not been narrowed and still catches every sm_12x part. A gate transplanted from one stack to the other is therefore no longer equivalent, and "SM12x" means different sets of hardware in the two codebases.One place where the family wide treatment is genuinely wrong, below vLLM: of the native W4A4 kernels,
FlashInferCutlassNvFp4LinearKernelandCutlassNvFp4LinearKernelrun on both archs, butFlashInferB12xNvFp4LinearKernelis gated onhas_device_capability(120) and has_flashinfer_b12x_gemm(), andhas_flashinfer_b12x_gemm()is a barehasattrprobe with no arch check, while FlashInfer's owndense_blockscaled_gemm_sm120.pyraises on anything that is notsm_120and its release wheels build120a/120fand never121a. So that kernel is sm_120 only in practice despite a family wide gate on the vLLM side. It is harmless today only becauseFlashInferCutlassNvFp4LinearKerneloutranks it on both archs. This is the same root cause theAVAILABLE_BACKENDScomment names, an SM121 CUTLASS guard being used to justify an exclusion applied to sm_120 as well.Bug 1 itself is real on both archs. It was measured here on GB10 (sm_121); upstream #55170 independently measured the same regression on an RTX PRO 6000 Blackwell Max-Q (sm_120), same model,
autoat TTFT 1455.2 ms / 74.81 tok/s againstflashinfer_cutlassat 926.1 ms / 92.63 tok/s. Bug 2 is arch independent:AVAILABLE_BACKENDSis a flat list with no capability condition, so B12X is missing everywhere.Why it matters
Per GEMM, 27B gate_up (N=34816 K=5120), CUDA graph replay. Time relative to W4A4, so higher is slower:
Peak observed: W4A4 80 to 148 TFLOPS, W8A8 45 to 55, BF16 30 to 33, Marlin 25 to 35, W4A16 CuTe DSL 20 to 22 and dropping to 8 to 10 at M=4096.
So the loss is in prefill and large batches, not single stream decode. Matches the upstream report of +27.5% prefill (pp2048 c1, 1423 to 1814 tok/s) on 2x DGX Spark with the kernel disabled.
SASS confirms it, all sm_121a: W4A4 runs
OMMA.SF.16864.F32.E2M1.E2M1.UE4M3.4X(native block scaled FP4), W4A16 runsF2FP.F16.E2M1.UNPACK_BthenHMMA.16816.F32(unpack, then FP16 tensor cores), and 936 Marlin instantiations contain zero FP4 instructions. GB10 does expose FP4 MMA viamma.sync, the "no FP4 tensor cores" claim refers totcgen05on sm_100. That holds on sm_120a as well as sm_121a, the two differ only in which PTX ISA version first listed them (8.7 for sm_120a, 8.8 for sm_121a).Caveat
My end to end serving runs on a battery throttled laptop were inconclusive, all configs within 10% and variance larger than the differences. The per GEMM table is the reliable measurement. The MoE model also masks the effect, since with stock flags the experts already get
FLASHINFER_CUTLASS, a real W4A4 kernel, and only the small dense projections fall back. Bug 1 matters most for dense models.Other hiccups worth recording
--limit-mm-per-prompt '{"image":0,"video":0}'or startup profiling crashes.HF_HUB_DISABLE_XET=1when pulling large quants on Spark, the Xet path thrashed the box, plain path holds about 90 MB/s..wslconfig, not the host unified pool.flashinfer_jit_cachewith sm_121a cubins.Tested on GB10 (sm_121), WSL2 Ubuntu 24.04, CUDA 13.0, vLLM 0.28.1rc1.dev345, torch 2.13.0+cu130, FlashInfer 0.6.18, with unsloth/Qwen3.8-27B-NVFP4 and unsloth/Qwen3.6-35B-A3B-NVFP4-Fast.