Skip to content

fix(gemm): exclude cuDNN 9.23.0 SM90 split-k bf16 plans from the autotune space - #3974

Closed
YangXu1990uiuc wants to merge 1 commit into
flashinfer-ai:mainfrom
YangXu1990uiuc:yanxu/cudnn-9230-sm90-splitk-tactic-filter
Closed

YangXu1990uiuc wants to merge 1 commit into
flashinfer-ai:mainfrom
YangXu1990uiuc:yanxu/cudnn-9230-sm90-splitk-tactic-filter

Conversation

@YangXu1990uiuc

@YangXu1990uiuc YangXu1990uiuc commented Jul 14, 2026

Copy link
Copy Markdown
Collaborator

What

CudnnBf16GemmRunner.get_valid_tactics now excludes cuDNN split-k execution plans (structured plan name containing k17=, i.e. CUDNN_KNOB_TYPE_SPLIT_K_SLC) from the autotuning space when running on SM90 with cuDNN 9.23.0 exactly (backend_version() == 92300). One file, ~15 lines.

Why

cuDNN 9.23.0 miscomputes bf16 GEMM/BMM split-k plans on SM90/Hopper — the same split-k output-layout bug that _cudnn_bf16_gemm_usable_or_skip (landed in #3539) already hard-bans for fp16 output, where even the default plan is broken. For bf16 output the default plan is correct, so a wholesale ban would kill a working path; the broken plans only execute when autotune(True) picks one — which it does, because split-k wins the timing race on tall-K shapes, and then silently returns garbage.

Found by the #3539 fuzzer's autotune-ON winner validation (mm_bf16 m63 n32 k2688: autotuned ratio 0.98 vs 0.0022 at the default plan). Full analysis with the per-plan matrix: #3539 (comment).

Per-plan enumeration on H100 + 9.23.0.39: exactly the five eng7_k17=4_* plans fail (ratio ~1.4); every other plan is correct. The same matrix on 9.23.1.3 and 9.23.2.1 is all-correct, so the version gate is exactly ==92300. Note 9.23.0.39 is a public PyPI nvidia-cudnn-cu12/cu13 release, so the silent-garbage window is reachable by real users.

Why tactic-level (not an envelope ban)

  • Default (non-autotuned) execution is correct and stays untouched.
  • Explicit backend="cudnn" users keep a working backend instead of a raise.
  • autotune(True) still tunes — just among the correct plans.

Plan names require cudnn-frontend ≥ 1.25 (get_plan_name_at_index); without the API the filter falls back to the full list, where the #3539 fuzzer's numeric-only ledger entry still catches a bad winner. This is also a concrete argument for #3707's structured plan names: eng7_k17=4 is a stable identifier where an integer plan index is version-fragile.

Validation (H100 + cuDNN 9.23.0.39)

  • Offered plans for the repro shape: 15 → 10, all k17= plans dropped (names verified).
  • 6/6 fresh-cache autotune(True) runs correct (worst ratio 0.0026; previously intermittently 1.39).
  • Fuzzer seed 1834712400 passes 3/3 deterministically (previously xfail-on-trip).
  • SM100: gate inactive, 15/15 plans offered — no behavior change on any other arch/version.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved bfloat16 GEMM tactic selection for compatible cuDNN and GPU configurations.
    • Prevented known-broken split-k execution plans from being selected, improving reliability.
    • Preserved fallback behavior when plan details are unavailable.

…tune space

cuDNN 9.23.0 (backend_version 92300) miscomputes bf16 GEMM/BMM split-k plans
on SM90/Hopper -- the same split-k output-layout bug that
_cudnn_bf16_gemm_usable_or_skip already hard-bans for fp16 output. For bf16
output the DEFAULT plan is correct, so the envelope ban would be wrong-shaped:
only the split-k plans (knob k17 = CUDNN_KNOB_TYPE_SPLIT_K_SLC) are broken,
and they only execute when autotune(True) selects one -- which it does,
because split-k wins the timing race on tall-K shapes and then silently
returns garbage (found by the flashinfer-ai#3539 fuzzer's autotune-ON winner validation:
mm_bf16 m63 n32 k2688, ratio 0.98 vs 0.0022 at the default plan).

Per-plan enumeration on H100 + 9.23.0.39: exactly the five eng7_k17=4_*
plans fail (ratio ~1.4), everything else is correct; the same matrix on
9.23.1.3 / 9.23.2.1 is all-correct, so the gate is exactly ==92300.
9.23.0.39 is a public PyPI nvidia-cudnn-cu12/cu13 release, so the window is
reachable by users.

Fix: CudnnBf16GemmRunner.get_valid_tactics drops plans whose structured name
contains 'k17=' when backend_version()==92300 on SM90 (plan names need
cudnn-frontend >= 1.25; without the API it falls back to the full list, where
the fuzzer's numeric-only ledger entry still catches a bad winner). Default
(non-autotuned) execution and every other arch/version are untouched.

Verified on H100 + cuDNN 9.23.0.39: offered plans 15 -> 10 (all k17 plans
dropped), 6/6 fresh autotune runs correct (worst ratio 0.0026, previously
intermittently 1.39), fuzzer seed 1834712400 passes 3/3 (previously xfail);
on SM100 the gate stays inactive (15/15 offered).

AI-assisted (per-plan bisect + fix by Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a3ca438b-d502-4cd1-863a-22a552ec6353

📥 Commits

Reviewing files that changed from the base of the PR and between 0472b9b and 7c06395.

📒 Files selected for processing (1)
  • flashinfer/gemm/gemm_base.py

📝 Walkthrough

Walkthrough

The cuDNN tactic search now filters k17= execution plans for cuDNN 9.23.0 on SM90 with bf16 output, while falling back to all tactics when plan-name introspection is unavailable.

Changes

cuDNN tactic filtering

Layer / File(s) Summary
Filter affected cuDNN tactics
flashinfer/gemm/gemm_base.py
get_valid_tactics excludes cuDNN plans containing k17= for the targeted configuration and retains the complete tactic list when plan-name lookup is unavailable.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: aleozlx, dhiraj113, yzh119, bkryu

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change to cuDNN autotuning for SM90 bf16 split-k plans.
Description check ✅ Passed The description covers what changed, why, fallback behavior, and validation, though related issues and checklist items are not filled in.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request modifies get_valid_tactics in flashinfer/gemm/gemm_base.py to filter out broken split-k plans (k17=) for cuDNN version 9.23.0 on SM90 architectures to prevent silent corruption during autotuning. The reviewer suggested guarding the cudnn.backend_version() check with CUDNN_AVAILABLE to avoid potential NameError or linter issues when cuDNN is not installed.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +4034 to +4038
if (
cudnn.backend_version() == 92300
and get_compute_capability(a.device)[0] == 9
and hasattr(graph, "get_plan_name_at_index")
):

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.

medium

To prevent potential NameError or static analysis/linter warnings when cudnn is not installed/available in the environment, we should guard the cudnn.backend_version() check with CUDNN_AVAILABLE, similar to how it is done in _cudnn_bf16_gemm_usable_or_skip.

            if (
                CUDNN_AVAILABLE
                and cudnn.backend_version() == 92300
                and get_compute_capability(a.device)[0] == 9
                and hasattr(graph, "get_plan_name_at_index")
            ):

@YangXu1990uiuc

Copy link
Copy Markdown
Collaborator Author

don't merge yet, will wait for 3707, then this need to be rewritten

@YangXu1990uiuc

Copy link
Copy Markdown
Collaborator Author

Closed as superseded by #3707: its _is_cudnn_override_shape_available() = backend >= 92301 gate makes the 9.23.0 split-k-under-override bug unreachable on every arch (9.23.0 falls back to static per-shape graphs; static plans verified all-correct per-plan, including the fp16-out heuristic default — full empirical matrix in #3539 (comment)). #3707 also removed the related fp16-out ban and the fuzzer's ==92300 ledger entry, so no cleanup remains. Verified on a #3707 test-merge at cuDNN 9.23.0/SM90: the previously-failing autotuned seed passes and a 200-config fuzzer sweep is green.

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants