Skip to content

Yanqinz/autotuner tactic - #3707

Merged
YangXu1990uiuc merged 20 commits into
mainfrom
yanqinz/autotuner-tactic
Jul 16, 2026
Merged

YangXu1990uiuc merged 20 commits into
mainfrom
yanqinz/autotuner-tactic

Conversation

@yanqinz2

@yanqinz2 yanqinz2 commented Jun 23, 2026

Copy link
Copy Markdown
Collaborator

This PR contains a series of changes to make cudnn GEMM backend stable during autotuning.

  1. Bump flashinfer dependency requirement of cudnn frontend version from v1.13.0 to v1.25.0
  2. Replace fixed plan index cached in autotuner with explicit cudnn engine/knob combination
  3. Add a fallback plan for cudnn backend when cached execution plan is not available to avoid abrupt crash

🚀 Pull Request Checklist

Thank you for contributing to FlashInfer! Before we review your pull request, please make sure the following items are complete.

✅ Pre-commit Checks

  • I have installed pre-commit by running pip install pre-commit (or used your preferred method).
  • I have installed the hooks with pre-commit install.
  • I have run the hooks manually with pre-commit run --all-files and fixed any reported issues.

If you are unsure about how to set up pre-commit, see the pre-commit documentation.

🧪 Tests

  • Tests have been added or updated as needed.
  • All tests are passing (unittest, etc.).

Reviewer Notes

Summary by CodeRabbit

  • Refactor
    • Unified cuDNN GEMM execution around a tactic → plan_index workflow, including discovery of engine/knob tactics, consistent plan selection, and plan-specific workspace sizing (covers FP4/BF16 and related paths).
  • Bug Fixes
    • Added resilience for tactic-based execution: if a tactic fails, the system warns and automatically falls back to the default tactic.
  • Tests
    • Updated autotuner tests to validate cuDNN engine/knob tactics and to verify expected warning behavior; expanded MXFP8 bmm warning assertions.
  • Dependencies
    • Increased minimum nvidia-cudnn-frontend to ≥ 1.25.0.
  • Chores
    • Broadened autotuner tactic handling to support non-integer representations; skipped legacy cuDNN integer tactic cache entries when autotuning.

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors cuDNN GEMM execution to select plans through tactic-to-plan_index mapping, updates autotuner tactic handling and cache loading, raises the cuDNN frontend minimum version, and adjusts related tests and warning-based fallback paths.

Changes

cuDNN tactic→plan_index refactor

Layer / File(s) Summary
Infrastructure and tactic helpers
requirements.txt, flashinfer/gemm/gemm_base.py
Bumps nvidia-cudnn-frontend to >=1.25.0, adds warnings imports, updates backend availability checks, and introduces shared helpers for override-shape availability, tactic enumeration, tactic-to-plan_index mapping, workspace sizing, and tactic-aware graph finalization.
BF16xFP4 graph build and execution
flashinfer/gemm/gemm_bf16_fp4_cudnn.py
Adds warning and helper imports in the BF16xFP4 cuDNN module, replaces policy-based graph planning with tactic-aware finalization for fixed-shape and override-shape builders, and updates both execution paths to resolve plan_index from tactic and size workspace per selected plan.
BF16xFP4 autotuner runner
flashinfer/gemm/gemm_bf16_fp4_cudnn.py
Updates the BF16xFP4 runner to use the shared override-shape availability checks, thread tactic through override-graph construction, normalize cache-hit tactics to 0 or -1, expose tuple-based valid tactics, and warn-fallback to fixed-shape execution with tactic=-1 on failure.
FP4 cuDNN tactic flow
flashinfer/gemm/gemm_base.py
Updates FP4 fixed-shape and override-shape graph construction and execution to use tactic-to-plan_index resolution, plan-specific workspace sizing, and tactic-aware graph finalization, and updates the FP4 runner to normalize cached override tactics, return engine/knob tactics, and fall back to tactic=-1 after warnings.
FP8 cuDNN tactic flow and assertions
flashinfer/gemm/gemm_base.py, tests/autotuner/test_autotuner_bmm_fp8.py
Updates FP8 fixed-shape and override-shape graph construction and execution to use tactic-based finalization and plan_index dispatch, updates the FP8 runner to normalize override-graph tactics and warn-fallback to tactic=-1, and revises FP8 autotuner assertions to validate cuDNN engine/knob tactics separately from cuBLASLt numeric tactics.
BF16 cuDNN tactic flow
flashinfer/gemm/gemm_base.py
Updates BF16 fixed-shape and override-shape graph construction and execution to use tactic-aware finalization, plan_index-based workspace sizing, and explicit plan dispatch, and updates the BF16 runner to normalize override tactics, enumerate engine/knob tactics, and fall back to tactic=-1 after warnings.
MXFP8 cuDNN tactic flow
flashinfer/gemm/gemm_base.py
Updates MXFP8 graph construction and execution paths to remove policy branching in favor of tactic-aware finalization and plan_index dispatch, and updates both MXFP8 runners to normalize cached override tactics, return engine/knob tactics, and warn-fallback to tactic=-1 on execution failures.
Autotuner config handling and tests
flashinfer/autotuner.py, tests/gemm/test_bmm_mxfp8.py
Widens autotuner tactic typing, skips legacy cuDNN cache entries during config loading, updates the FP8 autotuner test to validate backend-specific tactic forms, and expands the MXFP8 BMM test with additional tensor assertions and warning capture for the fallback path.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested labels

testing

Suggested reviewers

  • dhiraj113
  • aleozlx
  • yongwww
  • yzh119
  • jiahanc
  • qsang-nv
  • nv-yunzheq
  • kahyunnam

Poem

🐇 Hop, hop, the graphs align,
Tactics now pick plans by sign.
If a path says “nope” tonight,
A warning guides the fallback right.
Cache and tests both learn the tune —
cuDNN hops on a steadier dune.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too vague to clearly describe the main change and reads like a placeholder. Use a concise, specific title such as "Switch cuDNN autotuning to engine/knob tactics".
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description matches the template structure and includes the required sections and checklist items.
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
  • Commit unit tests in branch yanqinz/autotuner-tactic

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 refactors the cuDNN GEMM backend to support fine-grained tuning tactics (engine/knob configurations) during graph compilation and execution, updates the minimum cuDNN backend version requirement for override-shape GEMM to 9.23.1, and bumps the nvidia-cudnn-frontend dependency to >=1.25.0. The review feedback suggests caching the resolved plan index in _get_cudnn_plan_index_for_tactic to eliminate runtime lookup overhead, and wrapping _is_cudnn_override_shape_available in a try...except block to prevent unhandled exceptions if cuDNN queries fail.

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 thread flashinfer/gemm/gemm_base.py
Comment thread flashinfer/gemm/gemm_base.py Outdated

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@flashinfer/gemm/gemm_base.py`:
- Around line 2275-2280: The code currently allows any non-negative integer
tactic to be used as a concrete plan index, which permits legacy autotune caches
with unstable fixed-plan-index semantics to silently continue operating. Add
validation to reject non-negative integer tactics as legacy/invalid by checking
if tactic is an integer type and if so (and not equal to -1), set plan_index to
-1 instead of using the tactic value directly. This ensures old cached integer
plan indices are not silently accepted and forces the use of stable semantics.
- Around line 2696-2698: The override-shape FP4 graph builder is not applying
the same `eng0` engine deselection guard that exists in the fixed-shape builder.
Apply the same logic that excludes `eng0` when alpha or global scaling is
present and cuBLAS FP4-in-cuDNN is unavailable to the override-shape builder's
finalization step. Ensure that before calling `_finalize_cudnn_graph_for_tactic`
in the override-shape path, you check the same conditions as the fixed-shape
builder and apply the necessary engine deselection to prevent autotuning from
selecting unsupported engines.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c083a14e-d6f9-4f16-8c0c-db0bc9c10a1c

📥 Commits

Reviewing files that changed from the base of the PR and between 0b727e6 and f5ca057.

📒 Files selected for processing (3)
  • flashinfer/gemm/gemm_base.py
  • flashinfer/gemm/gemm_bf16_fp4_cudnn.py
  • requirements.txt

Comment thread flashinfer/gemm/gemm_base.py
Comment thread flashinfer/gemm/gemm_base.py

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
flashinfer/autotuner.py (1)

425-425: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align remaining tactic contracts with the new Any model

After widening forward/choose_one to Any, there are still integer-specific remnants (TunableRunner.forward doc text at Line 433 and search_cache return annotation at Line 947) that imply tactic is always int. Please update those to an opaque tactic type consistently to avoid type-checking drift and misleading API docs.

Also applies to: 1102-1102, 1113-1115

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flashinfer/autotuner.py` at line 425, Update the tactic-related documentation
and type annotations throughout the file to consistently use the `Any` type
model. Specifically, update the docstring for the TunableRunner.forward method
to remove language implying tactic is always an integer, change the return type
annotation of the search_cache method from int to Any to match the new tactic
contract, and similarly update any other type annotations or documentation at
the additional locations (around lines 1102 and 1113-1115) that still reference
tactic as an integer type to instead reflect the opaque `Any` tactic model.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@flashinfer/autotuner.py`:
- Line 425: Update the tactic-related documentation and type annotations
throughout the file to consistently use the `Any` type model. Specifically,
update the docstring for the TunableRunner.forward method to remove language
implying tactic is always an integer, change the return type annotation of the
search_cache method from int to Any to match the new tactic contract, and
similarly update any other type annotations or documentation at the additional
locations (around lines 1102 and 1113-1115) that still reference tactic as an
integer type to instead reflect the opaque `Any` tactic model.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6788c460-c4dc-4acf-9309-9e679bd5390a

📥 Commits

Reviewing files that changed from the base of the PR and between f5ca057 and f359f89.

📒 Files selected for processing (2)
  • flashinfer/autotuner.py
  • flashinfer/gemm/gemm_base.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • flashinfer/gemm/gemm_base.py

@yanqinz2
yanqinz2 force-pushed the yanqinz/autotuner-tactic branch from f359f89 to 8a37e5b Compare June 25, 2026 18:53

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
flashinfer/gemm/gemm_bf16_fp4_cudnn.py (1)

510-527: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Do not retry default fixed-shape failures as fallback.

If _use_override_shape is false and tactic == -1, this catch block retries the same fixed-shape default path after warning, which can duplicate OOM/validation failures and obscure the real error. Re-raise in that case; keep the fallback for non-default tactics or override-shape recovery.

Suggested guard
 except Exception as exc:
+    if tactic == -1 and not self._use_override_shape:
+        raise
     warnings.warn(
         "cuDNN bf16-fp4 GEMM tactic failed; falling back to default "
         f"tactic=-1. ({exc})",
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flashinfer/gemm/gemm_bf16_fp4_cudnn.py` around lines 510 - 527, The fallback
in the cuDNN bf16-fp4 GEMM path is retrying the same fixed-shape default tactic
when `_use_override_shape` is false and `tactic == -1`, which can repeat the
original failure instead of surfacing it. Update the `except Exception as exc`
block in `build_cudnn_bf16_fp4_graph` / the surrounding tactic-selection logic
to re-raise immediately for that default fixed-shape case, and only keep the
warning-plus-retry behavior for non-default tactics or when recovering via
`_use_override_shape`.

Source: Linters/SAST tools

flashinfer/gemm/gemm_base.py (1)

3478-3493: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Avoid retrying the same default fixed-shape failure.

When _use_override_shape is false and tactic == -1, these handlers catch a default execution failure, warn that they are “falling back” to the same default path, then retry it. Re-raise in that case, and reserve fallback for cached/non-default tactics or override-shape-to-fixed-shape recovery.

Suggested guard
 except Exception as exc:
+    if tactic == -1 and not self._use_override_shape:
+        raise
     warnings.warn(
         "... falling back to default tactic=-1. ...",
         stacklevel=2,
     )

Also applies to: 3992-3998, 4970-4984, 5441-5458, 8788-8802

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flashinfer/gemm/gemm_base.py` around lines 3478 - 3493, In the GEMM fallback
handlers in _cudnn_gemm_fp8 and the related retry paths, avoid re-invoking the
same default fixed-shape execution when _use_override_shape is false and tactic
== -1. Add a guard before the fallback call so these blocks re-raise the
original exception in that case, and only retry for non-default/cached tactics
or when recovering from override-shape to fixed-shape. Apply the same logic
consistently across the matching exception handlers in the other GEMM entry
points.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@flashinfer/gemm/gemm_base.py`:
- Around line 3478-3493: In the GEMM fallback handlers in _cudnn_gemm_fp8 and
the related retry paths, avoid re-invoking the same default fixed-shape
execution when _use_override_shape is false and tactic == -1. Add a guard before
the fallback call so these blocks re-raise the original exception in that case,
and only retry for non-default/cached tactics or when recovering from
override-shape to fixed-shape. Apply the same logic consistently across the
matching exception handlers in the other GEMM entry points.

In `@flashinfer/gemm/gemm_bf16_fp4_cudnn.py`:
- Around line 510-527: The fallback in the cuDNN bf16-fp4 GEMM path is retrying
the same fixed-shape default tactic when `_use_override_shape` is false and
`tactic == -1`, which can repeat the original failure instead of surfacing it.
Update the `except Exception as exc` block in `build_cudnn_bf16_fp4_graph` / the
surrounding tactic-selection logic to re-raise immediately for that default
fixed-shape case, and only keep the warning-plus-retry behavior for non-default
tactics or when recovering via `_use_override_shape`.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ba169850-bd5f-431d-925f-a2f2a1635afc

📥 Commits

Reviewing files that changed from the base of the PR and between f359f89 and 8a37e5b.

📒 Files selected for processing (5)
  • flashinfer/autotuner.py
  • flashinfer/gemm/gemm_base.py
  • flashinfer/gemm/gemm_bf16_fp4_cudnn.py
  • requirements.txt
  • tests/autotuner/test_autotuner_bmm_fp8.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • requirements.txt
  • flashinfer/autotuner.py

@YangXu1990uiuc

YangXu1990uiuc commented Jun 30, 2026

Copy link
Copy Markdown
Collaborator

Overall, the engine/knob tactic representation and the cuDNN version gating look reasonable.

I have the following requested changes/hardening suggestions:

1. Fix the MXFP8 test so it actually exercises the override-shape path

test_bmm_mxfp8.py currently constructs B as:

mat2 = torch.randn([b, n, k]).transpose(-2, -1).contiguous()
mat2_q, mat2_scale = mxfp8_quantize(mat2, ...)

This produces a row-major logical [B, K, N] tensor and quantizes along N. However, the API contract requires B to be column-major, with quantization performed along the contraction K dimension.

It should instead be constructed as:

weight = torch.randn([b, n, k], device="cuda", dtype=input_dtype)
weight_q, weight_scale = mxfp8_quantize(weight, is_sf_swizzled_layout)

B = weight_q.transpose(-2, -1)
reference = torch.bmm(input_mat, weight.transpose(-2, -1))

Please also assert the expected stride, and ideally ensure that no fallback warning is emitted. The current test passes numerically only because the runner silently falls back to the fixed-shape path, so it does not cover the new override-shape execution path.

This test bug predates this PR, but the new fallback behavior makes it especially easy for the test to pass without exercising the intended code.

2. Do not treat a failed tactic's fallback execution as a successful tactic during profiling

Fallback is desirable for inference robustness, but during autotuning, a failed tactic should be reported back to the autotuner as failed rather than being timed as tactic=-1.

Otherwise, the autotuner may persist a tactic that never executed and actually measured the fallback implementation. A preferable policy is:

  • During profiling: propagate the tactic failure so the autotuner marks it unsupported.
  • After all candidate tactics fail: use tactic=-1 as the final fallback.
  • Outside profiling/in normal inference: retain the current warning-and-fallback behavior.

This preserves serving reliability while ensuring that tuning results represent the requested tactics.

3. Include BF16 matrix layout in the persistent cache key

The in-memory runner identity distinguishes the A/B layout flags, but the persisted file-cache key currently does not distinguish:

is_a_k_major
is_b_k_major

As a result, different NT/TN/layout configurations can reuse a tactic tuned for another layout after serialization. Please include these flags in get_cache_key_extras() or otherwise encode them in the persistent key.

This may be uncommon, but cache-key bugs are extremely difficult to diagnose because they produce stable, configuration-dependent performance regressions.

4. Version the persisted cuDNN tactic representation

This PR changes cuDNN tactics from integer plan indices to engine/knob descriptions. Please add a tactic-schema/cache-format version and invalidate or explicitly reject legacy non-negative integer cuDNN tactics.

FlashInfer version metadata helps for normal releases, but it does not fully protect development builds, same-version branch changes, manually copied configs, or wildcard metadata. A persisted plan index must never be interpreted as a tactic under the new representation.

@yanqinz2
yanqinz2 force-pushed the yanqinz/autotuner-tactic branch from 8a37e5b to 1e781d0 Compare June 30, 2026 00:53

@coderabbitai coderabbitai 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
flashinfer/gemm/gemm_base.py (1)

3478-3493: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Don’t fallback during autotuner profiling.

These except Exception blocks make a failed candidate tactic return successfully after executing tactic=-1, so autotuning can cache an unsupported tactic as successful. Gate this fallback to normal inference only; during profiling/tuning, re-raise so the autotuner marks the candidate failed and only uses fallback after all candidates fail.

Also applies to: 3997-4003, 4975-4989, 5446-5464, 8798-8813

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flashinfer/gemm/gemm_base.py` around lines 3478 - 3493, The cuDNN fp8 GEMM
exception handlers currently swallow candidate failures by retrying with
tactic=-1, which can make autotuner profiling treat unsupported tactics as
successful. Update the relevant fallback paths in the affected GEMM helpers
(including the code around _cudnn_gemm_fp8 and the other listed except Exception
blocks) to distinguish normal inference from autotuner profiling/tuning, and
only apply the tactic=-1 fallback during normal inference. During profiling,
re-raise the exception so the autotuner records the candidate as failed and only
relies on fallback after all candidates fail.

Source: Linters/SAST tools

flashinfer/gemm/gemm_bf16_fp4_cudnn.py (1)

510-538: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Don’t fallback during autotuner profiling.

This catches candidate tactic failures and then succeeds via fixed-shape tactic=-1, which can cause autotuning to record the failed candidate as supported. Re-raise in tuning/profiling mode and keep the warning fallback only for normal inference.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flashinfer/gemm/gemm_bf16_fp4_cudnn.py` around lines 510 - 538, The fallback
inside the cuDNN bf16-fp4 GEMM path should not run during autotuner profiling,
because the fixed-shape tactic=-1 execution can make a failed candidate look
supported. Update the exception handling around the cuDNN graph execution in the
bf16_fp4 GEMM flow to re-raise when tuning/profiling is active, and keep the
warning plus tactic=-1 fallback only for normal inference. Use the existing
cuDNN GEMM helpers and control flow in gemm_bf16_fp4_cudnn.py, especially the
block that calls build_cudnn_bf16_fp4_graph and execute_cudnn_bf16_fp4_graph.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@flashinfer/gemm/gemm_base.py`:
- Around line 3478-3493: The cuDNN fp8 GEMM exception handlers currently swallow
candidate failures by retrying with tactic=-1, which can make autotuner
profiling treat unsupported tactics as successful. Update the relevant fallback
paths in the affected GEMM helpers (including the code around _cudnn_gemm_fp8
and the other listed except Exception blocks) to distinguish normal inference
from autotuner profiling/tuning, and only apply the tactic=-1 fallback during
normal inference. During profiling, re-raise the exception so the autotuner
records the candidate as failed and only relies on fallback after all candidates
fail.

In `@flashinfer/gemm/gemm_bf16_fp4_cudnn.py`:
- Around line 510-538: The fallback inside the cuDNN bf16-fp4 GEMM path should
not run during autotuner profiling, because the fixed-shape tactic=-1 execution
can make a failed candidate look supported. Update the exception handling around
the cuDNN graph execution in the bf16_fp4 GEMM flow to re-raise when
tuning/profiling is active, and keep the warning plus tactic=-1 fallback only
for normal inference. Use the existing cuDNN GEMM helpers and control flow in
gemm_bf16_fp4_cudnn.py, especially the block that calls
build_cudnn_bf16_fp4_graph and execute_cudnn_bf16_fp4_graph.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c4dc52c9-7e34-4cd5-ae24-fb25ded3fa81

📥 Commits

Reviewing files that changed from the base of the PR and between 8a37e5b and 1e781d0.

📒 Files selected for processing (6)
  • flashinfer/autotuner.py
  • flashinfer/gemm/gemm_base.py
  • flashinfer/gemm/gemm_bf16_fp4_cudnn.py
  • requirements.txt
  • tests/autotuner/test_autotuner_bmm_fp8.py
  • tests/gemm/test_bmm_mxfp8.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • requirements.txt
  • flashinfer/autotuner.py

@yanqinz2

Copy link
Copy Markdown
Collaborator Author

Regarding above comment from @YangXu1990uiuc

  1. Fixed.
  2. For now, I’d prefer not to change this behavior. Propagating tactic failures differently during profiling would require a relatively broad autotuner change, and I don’t want to take that risk in this PR. In practice, this fallback path should be rare, so I think the current behavior is acceptable for now.
  3. Fixed.
  4. For the cache format/versioning concern, I think the cleaner path after this refactor is to ask users to regenerate their autotune cache rather than continuing to support legacy cuDNN cache entries. The old integer plan-index representation is no longer compatible with the new engine/knob tactic format, so re-autotuning is the safest and simplest migration path.

@coderabbitai coderabbitai 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.

🧹 Nitpick comments (1)
flashinfer/autotuner.py (1)

385-385: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Finish widening the tactic contract beyond List/integer wording.

get_valid_tactics() now supports arbitrary tactic values, but List[Any] is still too concrete for tuple-returning runners, and forward() still documents tactic as an integer ID. Prefer Sequence[Any] and runner-defined wording so cuDNN engine/knob tuple tactics match the public contract.

Suggested contract cleanup
-    ) -> List[Any]:
+    ) -> Sequence[Any]:
@@
-            tactic: Integer ID specifying which implementation tactic to use.
+            tactic: Runner-defined selector specifying which implementation tactic to use.

If Sequence is not already imported:

-from typing import Any, ...
+from typing import Any, Sequence, ...

Also applies to: 425-435

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@flashinfer/autotuner.py` at line 385, The tactic contract is still too narrow
in both the return type and the `forward()` docs: update `get_valid_tactics()`
to use `Sequence[Any]` instead of `List[Any]`, and revise `forward()`’s `tactic`
wording so it describes runner-defined tactic values rather than an integer ID.
Make sure the `autotuner` public API consistently allows tuple-style cuDNN
engine/knob tactics, and import `Sequence` if needed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@flashinfer/autotuner.py`:
- Line 385: The tactic contract is still too narrow in both the return type and
the `forward()` docs: update `get_valid_tactics()` to use `Sequence[Any]`
instead of `List[Any]`, and revise `forward()`’s `tactic` wording so it
describes runner-defined tactic values rather than an integer ID. Make sure the
`autotuner` public API consistently allows tuple-style cuDNN engine/knob
tactics, and import `Sequence` if needed.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d105482b-9e4b-40ed-a514-49913ac915ed

📥 Commits

Reviewing files that changed from the base of the PR and between 1e781d0 and 4c23f77.

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

@yanqinz2
yanqinz2 force-pushed the yanqinz/autotuner-tactic branch from 1990cae to 382c1b6 Compare July 15, 2026 16:55
YangXu1990uiuc added a commit that referenced this pull request Jul 16, 2026
Quantize the contiguous [b,n,k] weight and pass the TRANSPOSE VIEW as B
(column-major [b,k,n], K contiguous), instead of .contiguous()-ing the
transpose before quantization. The old layout is rejected by the cuDNN
override-shape path's stride check; CI never saw it only because its older
cudnn-frontend (<1.24) keeps the override path disabled -- the moment the
frontend floor rises (e.g. #3707 pins >=1.25.0) every cudnn case here would
fail. Matches the unified fuzzer's bmm_mxfp8 adapter recipe.

Verified on SM100 + cuDNN 9.24.0.43 (torch main's current pin): 3 cudnn cases
pass (previously ValueError), 2 cutlass cases skip (SM12x-only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@YangXu1990uiuc
YangXu1990uiuc merged commit b35396c into main Jul 16, 2026
45 of 63 checks passed
@YangXu1990uiuc
YangXu1990uiuc deleted the yanqinz/autotuner-tactic branch July 16, 2026 03:02
@YangXu1990uiuc

Copy link
Copy Markdown
Collaborator

Following up on review point #2 from this PR ("do not treat a failed tactic's fallback execution as a successful tactic during profiling"), which was deferred here as potentially needing a broad autotuner change: it turned out to be a local change — is_in_profile_measurement() already exists and choose_one already disqualifies a raised tactic — so each cuDNN fallback site just re-raises during profiling and keeps warn+fallback for serving. Done in #4132 (GPU-tested on SM100). Thanks for flagging it originally.

Saddss added a commit to Saddss/flashinfer that referenced this pull request Jul 27, 2026
…navailable

On SM12x (RTX 5090) with override_shape unavailable (cuDNN backend < 9.23.1),
cuDNN's per-shape policy=ALL execution-graph build is unbounded host-side work
at serving time (RFC flashinfer-ai#3920, runner-contract rule 6), and the cuDNN FP8 serving
path raises an async cudaErrorMisalignedAddress that flashinfer-ai#3707's synchronous
forward() try/except cannot catch. Gate cuDNN out of the SM12x auto candidate
list in that case; SM100/103/107/110 and SM12x-with-override are unchanged
(cuDNN stays a candidate where M-bucketing amortizes the build).

Verified on RTX 5090 (SM120, vLLM nightly, online-replay load test), same
container, only the FlashInfer wheel differs:
- backend 92000 (override unavailable) + cuDNN in auto: ~5 rounds crash
  (misaligned), p50 ~17s
- backend 92000 + this gate (cuDNN removed): 12/12 PASS, p50 10.31s, 0 crashes
- backend 92400 (override available, nvidia-cudnn-cu13 9.24) + cuDNN in auto,
  NO gate: 12/12 PASS, p50 10.69s, 0 crashes -- cuDNN is safe when override_shape
  is available, validating the conditional gate's else branch.

The gate removes only cuDNN; cutlass_sm12x/cublas kernels and within-backend
winner selection are unchanged, so eliminating the 92000 crash by removing
cuDNN identifies cuDNN as the faulting path. (An earlier nvidia-cudnn-cu12 9.24
attempt was a CUDA 12/13 library mismatch, not a cuDNN signal.)

Signed-off-by: Saddss <2872669061@qq.com>
bkryu added a commit that referenced this pull request Aug 6, 2026
…navailable (#4165)

## 📌 Description

Gate the cuDNN FP8 GEMM backend out of the `bmm_fp8(backend="auto")`
candidate
list on SM12x when cuDNN `override_shape` is unavailable (cuDNN backend
<
9.23.1). On this narrow configuration the cuDNN path is both unsafe and
unnecessarily slow; cublas/cutlass already cover the workload.

## Root cause

Two independent failure modes on SM12x (RTX 5090), both removed by the
gate:

1. **Unbounded host-side compilation (perf).** Without `override_shape`,
cuDNN
builds a fresh `policy=ALL` execution graph per distinct serving
`(M,K,N)`
   shape (~250 ms host-side; measured 236 ms/shape). Under load this is
   unbounded — RFC #3920, runner-contract rule 6.
2. **Async CUDA fault (crash).** The cuDNN FP8 serving path raises
`cudaErrorMisalignedAddress` asynchronously at the next sync, which
#3707's
`forward()` try/except cannot catch (it only handles synchronous
exceptions).

## 📊 Evidence chain (RTX 5090, SM120, vLLM nightly, online-replay load
test)

Same container; only the FlashInfer wheel (and cuDNN package) differs.

**Reproducibility (input/output lengths only)**
- Model checkpoint (open-sourced):
<https://huggingface.co/wangqia0309/gemma-4-26B-A4B-it_nvfp4_experts_fp8_dense_fp8_attn-kv_fp8>
(NVFP4 mixed-precision: FP8 dense + attention, FP8 KV cache)
- Input: `max-model-len 8192`, `max-num-batched-tokens 8192`,
`max-num-seqs 64`
- Output: `max-tokens 200`
- MTP: `num_speculative_tokens=3`
- vLLM dtype/quant config: `--quantization modelopt --kv-cache-dtype fp8
--moe-backend cutlass -O3 --gpu-memory-utilization 0.95`

| FI build | cuDNN in SM12x auto | override_shape | result |
|---|---|---|---|
| 0.6.15.post1 (BAD baseline) | yes | unavailable (backend 92000) | 6
rounds then crash; p50 96/78/71/78/74/40 s |
| main + #3707 | yes | unavailable (backend 92000) | ~5 rounds crash
(`misaligned`); p50 26/17.6/17/17.5 s |
| **main + #3707 + this gate** | no (gated) | unavailable (backend
92000) | **12/12 PASS, steady p50 10.31 s, 0 crashes** |
| main + #3707 (NO gate) | yes | **available** (backend 92400,
`nvidia-cudnn-cu13` 9.24) | **12/12 PASS, steady p50 10.69 s, 0
crashes** |

The last row is the **clean** override-available test: with the cu13
cuDNN 9.24
stack (override_shape available), cuDNN-in-auto does *not* crash and
matches the
gated run's p50 — validating the conditional gate's `else` branch (keep
cuDNN
where override_shape amortizes the build).

**Why the gate proves the faulting path is cuDNN (elimination).** The
gate
removes *only* cuDNN from the candidate list; the `cutlass_sm12x` and
`cublas`
kernels and the autotuner's within-backend winner selection are
unchanged. At
serving time only the winning runner executes, and on
92000-without-override
the cuDNN runner is selected (per-tactic warm median ≈ 0.05–0.07 ms,
winning
20/20 in the §7.3 micro-bench). Removing cuDNN eliminates the crash; the
remaining cutlass/cublas kernels do not fault (12/12). Therefore the
faulting
path is cuDNN — no `compute-sanitizer` pinning needed.

**Independent reproduction:** handoff §6 — on post2, removing cuDNN from
the
SM120 candidate list → 12/12, steady p50 ≈ 12 s.

## Scope and limitation

- **Scoped fix.** The gate is `is_sm120_supported and not
_is_cudnn_override_shape_available()` — SM100/103/107/110 and
SM12x-with-
override are unchanged (cuDNN stays a candidate where M-bucketing
amortizes
  the build).
- **"Upgrade cuDNN" is tested clean here.** With the matching CUDA-13
cuDNN
(`nvidia-cudnn-cu13==9.24.0.43`, backend 92400, override_shape
available) +
cuDNN still in `auto`, the run is **12/12 PASS, p50 10.69 s, 0 crashes**
— cuDNN
is safe when `override_shape` is available, so the conditional gate
keeps it.
  (An earlier `nvidia-cudnn-cu12==9.24` attempt was a CUDA 12/13 library
  mismatch that broke cuBLASLt symbol loading — not a cuDNN-FP8 signal.)
- **No upstream improvement yet:** vLLM v0.26.0 stable pins
  `flashinfer-python==0.6.14` (BAD per the §4 bisect); vLLM nightly pins
0.6.15.post1 (BAD); FlashInfer main + #3707 still crashes on SM12x
without
  this gate.

## 🔍 Related Issues

- RFC #3920 (Autotuner v2, runner-contract rule 6) — unbounded
serving-time compilation
- #3707 (engine/knob tactics + sync fallback) — fixes stale plan-index
crash, not the async fault
- #3437 (multi-tactic / `policy=ALL` for cuDNN) — introduced the
per-shape ALL build cost
- #2914 (cuBLASLt + FP8 multi-tactic autotuning) — first BAD boundary
- #3566, #3673, #3255 — SM12x cuDNN/plan-index field reports

## 🚀 Pull Request Checklist

Thank you for contributing to FlashInfer! Before we review your pull
request, please make sure the following items are complete.

### ✅ Pre-commit Checks

- [x] I have installed `pre-commit` by running `pip install pre-commit`
(or used your preferred method).
- [x] I have installed the hooks with `pre-commit install`.
- [x] I have run the hooks manually with `pre-commit run --all-files`
and fixed any reported issues.

> If you are unsure about how to set up `pre-commit`, see [the
pre-commit documentation](https://pre-commit.com/).

## 🧪 Tests

- [x] Tests have been added or updated as needed.
- [x] All tests are passing (`unittest`, etc.).

Added
`test_bmm_fp8_heuristic_gates_cudnn_on_sm12x_without_override_shape` —
monkeypatches SM version + cuDNN availability so it runs on any host
without
SM12x hardware or a cuDNN install; asserts cuDNN is excluded when
`override_shape` is unavailable and retained when available.

## Reviewer Notes

- Scope is deliberately minimal: one heuristic branch + one test. No
change to
  cuDNN code, the override_shape path, or non-SM12x architectures.
- The one-shot warning uses a module-level `set` (mutation, no `global`)
so it
does not spam per-call (the heuristic runs on every
`bmm_fp8(backend="auto")`).
- DCO: `Signed-off-by: Saddss <2872669061@qq.com>`.

Signed-off-by: Saddss <2872669061@qq.com>
Co-authored-by: Brian K. Ryu <bryu@nvidia.com>
aleozlx added a commit that referenced this pull request Sep 5, 2026
…ployment-matched measurement, runner contract (#3861)

## 📌 Description

**Autotuner v2** — an evolution of the autotuner exposed through a
standalone `autotune_v2()`, additive over `autotune()` (which stays
byte-identical). It started as a managed persistent cache and has grown,
across review, into five composing pillars:

1. **Managed persistence** — FlashInfer owns cache keys, schema,
environment isolation, and invalidation; frameworks stop hashing
filenames, parsing the JSON schema, broadcasting raw cache files, and
managing staleness. Per-entry atomic store, safe for concurrent
all-ranks tuning.
2. **Deployment-matched measurement** (`MeasurementPolicy`) — tune the
way you serve (eager vs CUDA-graph host-cost semantics); part of the
store's environment identity.
3. **Runner contract** — self-describing tactics + runtime revalidation
(`validate_tactic`, covering in-memory and on-disk hits) so a
stale/invalid tactic is a loud fallback, never a silent wrong kernel or
a dead server.
4. **Distributed consistency** — a shared store plus
`autotune_v2_reload()` finalize step so homogeneous ranks converge on
byte-identical tactics (composes with #3187).
5. **Selection quality** — a default-candidate coverage guard (a tuned
result cannot lose to not-tuning at the probe inputs) and an accuracy
harness that quantifies regret against a deployment-faithful oracle.

> **RFC** (design + evidence, for public/customer review): #3920.
Detailed rationale, the review-round fixes, framework-migration drafts,
and the production-B200 validation are in the thread below.

## Summary

FlashInfer owns the persistence and compatibility rules for autotuning
results, so frameworks stop constructing cache filenames, parsing
FlashInfer's JSON schema, broadcasting raw cache files, and managing
invalidation themselves — and, via `MeasurementPolicy`, tuning measures
the way the deployment actually runs.

Exposed as a **standalone `autotune_v2()` context manager**,
deliberately disjoint from `autotune(cache=<json path>)` (v1) so the
design can iterate without inheriting v1 semantics —
`flashinfer/autotuner/` changes are purely additive hooks and
`autotune()` itself is untouched.

## API

```python
with flashinfer.autotune_v2(measure=MeasurementPolicy(execution_mode="cuda_graph")):
    model(dummy_inputs)          # startup warmup: tune + publish atomically
model(inputs)                    # serving: reuses entries, no context needed

# fresh process, same environment: hydrate + serve
with flashinfer.autotune_v2(mode="replay", measure=policy):
    pass
```

| `mode` | `persistent_cache` | meaning |
|---|---|---|
| `"tune"` | True *(default)* | tune misses, publish to disk |
| `"tune"` | False | tune in-memory only (no disk I/O) |
| `"replay"` | True | serve from the on-disk cache (no profiling) |
| `"replay"` | False | no-op unless already hydrated |

**Attach semantics**: `persistent_cache=True` attaches the store for the
remainder of the process (like v1's `load_configs`) — a survey of
vLLM/sglang showed both serve **outside** any context, so a
context-scoped store would silently regress serving to heuristics after
warmup exits. The context scopes only *when profiling cost may be paid*.
`cache_root` (or `FLASHINFER_AUTOTUNE_CACHE_DIR`) is a placement-only
**directory**; the schema/environment namespaces live below it.

## MeasurementPolicy — tune the way you deploy

The decisive question is whether per-call **host** cost counts: under
CUDA-graph serving it is paid once at capture (excluding it is correct);
under eager serving it is paid every call. Measured on SM100 (`bmm_fp8`
M=8, kernel ~8µs): the same cuDNN candidate reads **8.0µs host-excluded
vs ~330µs host-included** — the eager ranking flips to cublas < cutlass
≪ cuDNN, matching real framework behavior.

```python
MeasurementPolicy(execution_mode="cuda_graph")  # profile under real capture+replay
MeasurementPolicy(execution_mode="eager")       # host cost included (no delay kernel)
# default "auto" = today's legacy behavior; cold_l2 composes. The timer is derived
# from execution_mode and is always CUDA events: "auto"/"cuda_graph" use the
# events+delay-kernel window, "eager" uses events without the delay kernel.
# CUPTI activity-span timing is NOT selected by any public mode; it exists only
# as a private diagnostic override (_timer="cupti") used by the accuracy harness.
```

The policy is part of the store's **environment identity** (manifest →
env hash): entries tuned under different policies never overwrite each
other. A CUPTI activity-span timer is implemented as a
**diagnostic-only** route (per-iteration GPU spans, median; no delay
kernel; `cuptiIsTracingSessionRunning` pre-check + graceful events
fallback around the legacy single-subscriber limitation) and is
reachable only via the private `_timer="cupti"` override, not through
`execution_mode`. It is not used for any shipped policy; see the
validation report's CUPTI appendix for why (it cannot rank
eagerly-launched multi-kernel ops such as `moe_cute_dsl`).

## Store design

```
<root>/v2/<environment_hash>/          # sha256 of canonical manifest (flashinfer/CUDA/
  manifest.json                        #   cuBLAS/cuDNN backend+frontend/GPU [+ policy])
  entries/<operation_hash>.json        # one atomic file per tuned operation
```

- One entry per tuned op, published via tempfile + atomic `os.replace`
as soon as it is tuned — no exit-time read/merge/write, no locks;
concurrent all-ranks tuning is safe (redundant work, last valid write
wins), so per-rank cache files become unnecessary.
- Missing/malformed/key-mismatched entries are cache misses, never
errors; each entry embeds its canonical key.
- Hit/miss memoized (decoded once); the serving hot path re-reads
neither the filesystem nor JSON.
- Incompatible environments hash to different directories: nothing to
invalidate manually, downgrade-safe.
- v1 and v2 never share in-memory state — a v1 `save_configs`
structurally cannot observe v2 entries (regression-tested).

## Also included

- **Runner contract**: `validate_tactic(inputs, tactic)` revalidation on
in-memory and on-disk hits (a rejected tactic is a loud fallback, not a
blind replay); default-candidate coverage guard.
- **Distributed**: `autotune_v2_reload()` finalize step for rank
convergence.
- `benchmarks/bench_autotuner_accuracy.py`: accuracy harness (oracle
sweep → regret / top-1 / winner-flip-rate under each policy, scored
against both oracles), with an interleaved drift-protected oracle and
per-round SM-clock recording. **Validated on a production B200** across
bmm_fp8 / mm_fp4 / cutlass MoE (thread).
- Observability: loud INFO at store attach, once-per-op hit provenance
logs.

## Explicitly out of scope (this PR)

- opaque export/install artifact for multi-node distribution (only
needed for leader-tunes-then-broadcast without a shared FS; under
discussion)
- flipping `execution_mode="auto"` to `"cuda_graph"` (pending
forced-capture validation across the op suite)
- per-runner adoption of `validate_tactic` (cuDNN first, via its #3707
structured tactics — the hook is here; runners opt in separately)
- representative-probe construction for MoE (EP-deflated shapes /
skewed-routing dummies — the #3622/#3537 op-level track; sibling PRs)
- serialized backend artifacts (e.g. cuDNN plan sidecar, after #3707);
GC/pruning; cross-job single-flight; comm-op tuning; any change to v1
behavior

## Relation to existing work

Orthogonal to #3707 (structured `(engine_id, knobs)` cuDNN tactics):
entries store whatever tactic representation runners produce —
compound-tuple round-trip is covered by tests; the environment hash
contains index-tactic drift meanwhile.

## Testing

238 GPU-free tests pass in `tests/autotuner/` (the v2 file
`test_autotune_cache_v2.py` covering: both real consumer patterns —
serve-after-context-exit, hydrate-only; corruption tolerance; env-hash
and measurement-policy isolation; bare-serving-uses-ambient-identity and
cache_root-switch repopulation; atomicity; v1 coexistence;
runner-reorder / extras-collision key completeness; guard
win-persist-replay; reload convergence; same-process revalidation
fallback — plus the upstream suites incl. the #3187 AST guards).
Pre-commit clean.

On-GPU validation on SM100 and a **production B200**: tune → atomic
publish → fresh-process serve; a 4-process concurrent tuning storm into
one shared store (SGLang all-ranks pattern) with store integrity intact;
and the accuracy harness across three ops (bmm_fp8 / mm_fp4 / cutlass
MoE) — details, matrices, and framework-migration drafts in the thread.
Branch went through several external review rounds plus a four-lens
cleanup pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)


## 🔍 Related Issues

RFC #3920 (design) · #3707 (structured tactics, composed) · #3187
(cross-rank sync, composed) · addresses classes from #3186, #3537,
#3566, #3622, #3648, #3719 (see the RFC's issue-bucket table)

## 🚀 Pull Request Checklist

Thank you for contributing to FlashInfer! Before we review your pull
request, please make sure the following items are complete.

### ✅ Pre-commit Checks

- [x] I have installed `pre-commit` by running `pip install pre-commit`
(or used your preferred method).
- [x] I have installed the hooks with `pre-commit install`.
- [x] I have run the hooks manually with `pre-commit run --all-files`
and fixed any reported issues.

## 🧪 Tests

- [x] Tests have been added or updated as needed
(`tests/autotuner/test_autotune_cache_v2.py`, 28 GPU-free tests; full
`tests/autotuner/` suite green).
- [x] All tests are passing.

## Reviewer Notes

Opt-in and additive: `autotune()` and the v1 cache path are
byte-identical; v2 state lives behind `autotune_v2()`. Suggested review
order: `flashinfer/autotune_cache.py` (store + API), then the four hook
sites in `flashinfer/autotuner/autotuner.py` (search_cache 2.5, publish,
measurement routing, store stack), then the tests.




<!-- note to self: claude::28440023-66ee-410c-a0aa-234997e886d7 —
"Autotuner v2 report review"
cwd /home/scratch.yanxu_libs/flashinfer · workspace
/tmp/claude-25653/autotune-v2-rebase -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Alex Yang <aleyang@nvidia.com>
Co-authored-by: Vincent Tombari <34876120+Vinnie6167@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants