Skip to content

[Bugfix] Pin FlashInfer bmm_fp8 to cuBLAS on sm_12x to avoid cuDNN hot-path stalls - #48210

Open
Majid-Taheri wants to merge 1 commit into
vllm-project:mainfrom
Majid-Taheri:fix-flashinfer-fp8-cudnn-stall
Open

Majid-Taheri wants to merge 1 commit into
vllm-project:mainfrom
Majid-Taheri:fix-flashinfer-fp8-cudnn-stall

Conversation

@Majid-Taheri

@Majid-Taheri Majid-Taheri commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Purpose

FlashInferFP8ScaledMMLinearKernel runs FP8 linears through bmm_fp8(backend="auto"). On sm_12x GPUs (RTX PRO 6000, RTX 50, GB10), "auto" picks cuDNN, which builds a GEMM graph for every new shape inside the serving loop. This stalls the engine 12-17 s per new prompt length, GPU at 0%.

"cublas" is FlashInfer's own default for bmm_fp8. The "auto" comes from vLLM. This PR keeps the kernel on and pins "cublas" on sm_12x only. Other GPUs keep "auto".

(Reworked from the first version that gated the kernel off sm_12x. Per review, the backend pin keeps the kernel in use.)

Change (3 files, +99/-4)

  • flashinfer.py: new bmm_fp8_backend() helper returns "cublas" on sm_12x, else "auto". Uses @torch.compiler.assume_constant_result (functools.cache crashes torch.compile here).
  • collective_fusion.py: the two AsyncTP patterns use the same helper, so they still match bmm_fp8.
  • new test: is_supported over cc {90,100,103,120,121}, plus the backend pin.

Test Result (RTX PRO 6000, sm_120, Nemotron-3-Super-120B-NVFP4, ShareGPT, c=8)

Metric before (cuDNN) this PR (cublas)
Output throughput 47.8 tok/s 210.3 tok/s
Median TTFT 38.6 s 5.8 s
Median TPOT 46.8 ms 18.7 ms
Novel-shape latency 12-17 s 0.5-1.3 s
Failed requests 0 0

Kernel stays FlashInfer (no CUTLASS fallback). Tests 12/12 pass.

Relation to flashinfer#3566

The crash half is already fixed in flashinfer 0.6.13. The stall is structural on flashinfer main. This pin fixes the stall on the vLLM side and can be relaxed once flashinfer builds cuDNN plans off the hot path.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@mergify mergify Bot added nvidia bug Something isn't working labels Jul 10, 2026
# build_cudnn_gemm_fp8_graph call inside the serving hot path,
# stalling the engine 12-17s per new prompt length. Fall back to
# CUTLASS on sm_12x until FlashInfer builds cuDNN plans off the
# hot path.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turning off Flashinfer capability all backends for bmm_fp8 on SM120+ because of cuDNN backend alone doesn't seem to be the right approach. Can we wait for newer flashinfer version to fix this like the issue suggests?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the review. I agree, turning off the whole kernel is too much. I test another way today: keep the FlashInfer kernel but use backend="cublas" instead of "auto". Then there is no stall on sm_120. New shapes run in under 1 ms. cuDNN is only in the path because vLLM asks for "auto"; flashinfer's default is "cublas". I can update the PR this way. Is that ok? Waiting for a flashinfer fix leaves sm_12x at 48 tok/s instead of 210.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Datapoint on the wait-for-newer-flashinfer option: the crash half is already fixed in the 0.6.13 that vLLM pins (flashinfer#3437's tactic clamp; A/B'd on GB10 in flashinfer-ai/flashinfer#3566), but the stall half is structural and unchanged on flashinfer main -- build_cudnn_gemm_fp8_graph is an lru_cache keyed on the exact gemm shape (gemm_base.py:3098 on main, :2915 in v0.6.13) and cudnn still joins the auto list on sm12x, so new prompt lengths keep paying the graph build inside the engine loop. FWIW backend="cublas" is also flashinfer's own default for bmm_fp8 (v0.6.13 gemm_base.py:6415); the "auto" comes from the vLLM side in flashinfer_scaled_fp8_mm.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pavanimajety I redo the fix the smaller way you asked (force-pushed). The kernel stays on for sm_12x. vLLM now uses backend="cublas" there, not "auto", so cuDNN is not picked. "auto" stays for all other GPUs. I also update the AsyncTP fusion patterns to use the same backend helper, so they still match. On sm_120 the new-shape delay drops from 12-17s to under 1.3s. Thanks @waynehacking8 for confirming the cause.

…d=auto

flashinfer_scaled_fp8_mm passes backend="auto" to FlashInfer's bmm_fp8.
With the cudnn python module importable (a transitive dependency via
nvidia-cudnn-frontend in requirements/cuda.txt), auto selection includes
the cuDNN runner, and FlashInfer's fp8_gemm_sm100 builds a cuDNN GEMM
graph lazily per exact (batch, seqlen) shape on the host, inside the
serving hot path.

On the sm_12x (consumer/workstation Blackwell) family this causes:
1. 12-17s engine stalls (GPU at 0%) on every novel prompt length. On a
   ShareGPT serving benchmark (Nemotron-3-Super-120B NVFP4, RTX PRO 6000
   Blackwell) this cut output throughput from ~210 tok/s to ~48 tok/s.
2. Execute-time plan rejection: "Plan index N is invalid"
   (flashinfer-ai/flashinfer#3566).

Instead of gating FlashInferFP8ScaledMMLinearKernel off sm_12x, keep the
kernel enabled and pin the backend to cuBLAS (FlashInfer's own default
for bmm_fp8) on sm_12x via a new bmm_fp8_backend() helper. "auto" is
preserved on other archs (e.g. sm_10x datacenter Blackwell), where the
cuDNN runner is autotuned at warmup and beneficial.

The AsyncTP fusion patterns (FlashInferBMMFP8ReduceScatterPattern,
FlashInferAllGatherBMMFP8Pattern) match torch.ops.vllm.bmm_fp8 on the
exact backend string literal, so they now derive it from the same
helper — otherwise the fusion would silently stop matching on sm_12x.

Tests: parametrized is_supported over cc in {90,100,103,120,121}
(sm_12x stays supported) and bmm_fp8_backend over the platform family
(cublas on sm_12x, auto elsewhere).

Signed-off-by: Majid Taheri Andani <tahemaji@amazon.com>
@Majid-Taheri
Majid-Taheri force-pushed the fix-flashinfer-fp8-cudnn-stall branch from 7783012 to 971b2aa Compare July 12, 2026 00:35
@Majid-Taheri Majid-Taheri changed the title [Bugfix] Gate FlashInfer per-tensor FP8 linear kernel off sm_12x [Bugfix] Pin FlashInfer bmm_fp8 to cuBLAS on sm_12x to avoid cuDNN hot-path stalls Jul 12, 2026

@waynehacking8 waynehacking8 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reread the full diff at 971b2aa and ran the two new tests on this branch locally - 7/7 pass, and on my RTX PRO 6000 bmm_fp8_backend() resolves to cublas as intended. The pin matches the stall mechanism I traced earlier: the per-exact-shape cuDNN graph build is still on the serving hot path in current flashinfer main, so keeping the kernel on and pinning cublas on sm_12x only is the right shape for this fix. Deriving the AsyncTP pattern literal from the same helper instead of hardcoding "auto" also avoids the silent pattern-match break. LGTM.

@Majid-Taheri

Copy link
Copy Markdown
Contributor Author

Hi @pavanimajety, thanks for the earlier look. A reviewer approved this and the two new tests pass. Can you approve and add the ready label so CI runs? @ProExpertProg, could you check the small AsyncTP change in collective_fusion.py? Happy to fix anything.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working nvidia torch.compile

Projects

Status: No status
Status: To triage

Development

Successfully merging this pull request may close these issues.

3 participants