Skip to content

feat(jit): JitSpec ABC + disk cache for JIT-compiled CuTe-DSL kernels - #3874

Merged
bkryu merged 10 commits into
flashinfer-ai:mainfrom
bkryu:cute_dsl_cache
Jul 15, 2026
Merged

bkryu merged 10 commits into
flashinfer-ai:mainfrom
bkryu:cute_dsl_cache

Conversation

@bkryu

@bkryu bkryu commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

📌 Description

FlashInfer's nvcc-compiled kernels are cached on disk and load instantly in new processes; CuTe-DSL kernels were memoized only in-process, so every new process recompiled every kernel.

The PR adds the core disk caching functionality and applies it to nvfp4_quantize(backend='cute-dsl') by:

  • JitSpec becomes an abstract base class (ABC) with a shared build_and_load() template method (caching, locking, FLASHINFER_DISABLE_JIT); the former dataclass is now JitSpecNvcc (behavior unchanged), and the new
  • JitSpecCuteDsl persists CuTe-DSL kernels as export_to_c() object files under cached_ops/, reloaded via JITLink in 3–30 ms. Future DSLs (e.g. cutile) implement the same three methods.
  • Wires up nvfp4_quantize as the first user. Example kernel cache path:
    .../cached_ops/
    ├── fp4_quantization_100/                  # nvcc module (unchanged)
    ├── nvfp4_quantize_sm100a_cute_dsl/        # CuTe-DSL module (new)
    │   ├── meta.json                          #   one per module (invalidation)
    │   ├── swizzled_bfloat16_k4096_sf0_pdl0.o #   one .o per specialization (~28 KB)
    │   └── ...
    
  • Design doc: docs/design_docs/cute_dsl_kernel_cache.md covers the lifecycle, cache layout, invalidation keys, concurrency/crash safety, and alternatives considered (including the measured-and-rejected K-agnostic compilation). Please start there for review.

🔍 Related Issues

🚀 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

  • New Features
    • Added persistent on-disk caching for CuTe-DSL JIT kernels to reduce repeated compile time, including automatic cache invalidation based on relevant build inputs.
    • Added FLASHINFER_CUTE_DSL_DISABLE_CACHE=1 to bypass the disk cache and force recompilation.
    • Re-exported additional JIT specification classes for direct use.
  • Bug Fixes
    • Improved cache correctness with strict metadata validation and safer concurrent, crash-tolerant cache writes/updates.
  • Documentation
    • Added design docs describing the CuTe-DSL cache layout, validity rules, and locking behavior.
  • Tests
    • Updated the JIT spec construction in the C++ extension test to match the new JIT lifecycle.

@coderabbitai

coderabbitai Bot commented Jul 7, 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

Adds persistent CuTe-DSL kernel caching, refactors the JIT lifecycle into a shared base template, routes NVFP4 CuTe-DSL compilation through the cache wrapper, and updates related documentation and exports.

Changes

CuTe-DSL JIT and kernel caching

Layer / File(s) Summary
JitSpec lifecycle refactor
flashinfer/jit/core.py, flashinfer/jit/__init__.py, tests/test_jit_cpp_ext.py
Introduces the abstract JitSpec template, updates JitSpecNvcc behavior, adjusts registry/status handling, and updates exports and tests.
CuTe-DSL cache core
flashinfer/jit/cute_dsl_core.py
Adds CuTe-DSL cache disablement, module naming, metadata hashing, cache lookup, invalidation, crash-safe persistence, and the wrapper used to compile or reuse kernels.
NVFP4 CuTe-DSL integration
flashinfer/quantization/kernels/nvfp4_quantize.py
Adds source-based invalidation inputs and routes the NVFP4 CuTe-DSL compile paths through the cache wrapper.
Caching docs
CLAUDE.md, docs/design_docs/cute_dsl_kernel_cache.md
Expands the JIT/CuTe-DSL documentation and adds a design page for the cache layout and behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant NVFP4 as nvfp4_quantize compile path
  participant Cache as build_and_load_cute_dsl_kernel
  participant JitSpec as JitSpecCuteDsl
  participant Disk as cached_ops
  participant Compiler as cute.compile

  NVFP4->>Cache: request kernel(name, extra_key_files)
  Cache->>JitSpec: build_and_load()
  JitSpec->>Disk: check .o and meta.json
  alt cache hit
    Disk-->>JitSpec: load exported symbol
  else cache miss or stale
    JitSpec->>Compiler: compile_fn()
    Compiler-->>JitSpec: compiled kernel
    JitSpec->>Disk: write .o then meta.json
  end
  JitSpec-->>NVFP4: kernel handle
Loading

Possibly related PRs

Suggested reviewers: yzh119, cyx-6

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title is concise and accurately captures the main change: introducing a JitSpec ABC and CuTe-DSL disk caching.
Description check ✅ Passed The description matches the template structure and includes the required sections, with a substantive summary and checklist/reviewer notes present.
✨ 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 introduces an on-disk caching mechanism for JIT-compiled CuTe-DSL kernels, mirroring the two-level caching used for nvcc-compiled modules. It adds a core module for managing the cache and integrates it with the nvfp4 quantization kernels. The review feedback is highly constructive, focusing on making the cache architecture-aware on mixed multi-GPU systems by passing a device parameter, and adding defensive checks to prevent potential FileNotFoundError or AttributeError when hashing source files.

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/jit/cute_dsl_core.py
Comment thread flashinfer/jit/cute_dsl_core.py
Comment thread flashinfer/jit/cute_dsl_core.py Outdated
Comment thread flashinfer/quantization/kernels/nvfp4_quantize.py
Comment thread flashinfer/quantization/kernels/nvfp4_quantize.py
Comment thread flashinfer/quantization/kernels/nvfp4_quantize.py
Comment thread flashinfer/quantization/kernels/nvfp4_quantize.py
Comment thread flashinfer/jit/cute_dsl_core.py Outdated
Comment thread flashinfer/quantization/kernels/nvfp4_quantize.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.

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
docs/design_docs/cute_dsl_kernel_cache.md (1)

123-129: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Broken internal cross-reference: "see 3.2" doesn't exist.

Line 127 references section "3.2" for linking .os into .sos, but "## 3. Alternatives considered" only contains subsection "3.1" (Line 99). This should point to 3.1.

📝 Proposed fix
-- **No `JitSpecRegistry` / AOT integration**: cute-dsl kernels are invisible to `flashinfer aot` and `flashinfer-jit-cache` packaging, and do not honor `FLASHINFER_DISABLE_JIT`. AOT support would prebuild the module directories (or link them into `.so`s, see 3.2).
+- **No `JitSpecRegistry` / AOT integration**: cute-dsl kernels are invisible to `flashinfer aot` and `flashinfer-jit-cache` packaging, and do not honor `FLASHINFER_DISABLE_JIT`. AOT support would prebuild the module directories (or link them into `.so`s, see §3.1).
🤖 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 `@docs/design_docs/cute_dsl_kernel_cache.md` around lines 123 - 129, The
cross-reference in the “No `JitSpecRegistry` / AOT integration” bullet points to
a non-existent section. Update the mention of linking into `.so`s in the
`cute_dsl_kernel_cache.md` design doc so it references the existing “3.1”
subsection instead of “3.2”, keeping the wording aligned with the surrounding
discussion in the limitations section.
🤖 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 `@docs/design_docs/cute_dsl_kernel_cache.md`:
- Around line 53-63: The fenced code block in the Cute DSL kernel cache docs is
missing a language hint, which triggers markdownlint MD040. Update the fenced
block in the documented cache layout section to use a language tag such as text
so the block remains readable and lint-compliant.

In `@flashinfer/jit/cute_dsl_core.py`:
- Around line 66-73: The _hash_source_files helper in cute_dsl_core.py should
not be cached solely by the tuple of paths, because that makes source_sha256
stale when file contents change during the process lifetime. Remove the
`@functools.cache` on _hash_source_files or change the invalidation strategy so it
reflects file freshness/content changes, and ensure the caller path through
source_sha256 still produces a hash that updates when any extra_key_files
content changes.
- Around line 161-164: The module invalidation check in the CuTe-DSL module
write path should treat any existing module directory as stale when its metadata
is missing or does not match the expected value. Update the logic around the
meta check in the module directory handling code so that _read_meta(meta_path)
is compared against expected_meta even if meta_path does not exist, and delete
the directory whenever the metadata is absent or mismatched before writing new
metadata. Use the existing module_dir, meta_path, _read_meta, expected_meta, and
logger flow to locate and adjust the invalidation behavior.

---

Outside diff comments:
In `@docs/design_docs/cute_dsl_kernel_cache.md`:
- Around line 123-129: The cross-reference in the “No `JitSpecRegistry` / AOT
integration” bullet points to a non-existent section. Update the mention of
linking into `.so`s in the `cute_dsl_kernel_cache.md` design doc so it
references the existing “3.1” subsection instead of “3.2”, keeping the wording
aligned with the surrounding discussion in the limitations section.
🪄 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: b1a048dd-0e0c-43ca-94c2-a340472fa50a

📥 Commits

Reviewing files that changed from the base of the PR and between fe9523d and 80accc9.

📒 Files selected for processing (4)
  • CLAUDE.md
  • docs/design_docs/cute_dsl_kernel_cache.md
  • flashinfer/jit/cute_dsl_core.py
  • flashinfer/quantization/kernels/nvfp4_quantize.py

Comment thread docs/design_docs/cute_dsl_kernel_cache.md Outdated
Comment thread flashinfer/jit/cute_dsl_core.py Outdated
Comment thread flashinfer/jit/cute_dsl_core.py Outdated
@bkryu bkryu changed the title [MVP/RFC] feat(jit): add disk cache for JIT-compiled CuTe-DSL kernels feat(jit): add disk cache for JIT-compiled CuTe-DSL kernels Jul 9, 2026
@bkryu bkryu changed the title feat(jit): add disk cache for JIT-compiled CuTe-DSL kernels feat(jit): JitSpec ABC + disk cache for JIT-compiled CuTe-DSL kernels Jul 9, 2026
@waynehacking8

Copy link
Copy Markdown
Contributor

Ran a cold/warm check of this branch on SM120 (RTX PRO 6000, CUDA 13.0) with nvfp4_quantize(backend='cute-dsl'), M=640 K=7168 bf16: current main (f2f9646) pays ~0.45s first-call in every fresh process, this branch pays 0.61s once (compile + persist) then 0.11s per fresh process from the cached .o, with outputs bitwise-equal to backend='cuda' in all runs. From the consumer side this is very welcome -- the heavier DSL specializations we lean on for SM120 MoE take far longer than this quantize kernel to compile, and today they pay it on every server start.

Comment thread flashinfer/jit/core.py
def load(self) -> Any: ...

def build_and_load(self) -> Any:
cached = self.try_load()

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.

let's check if the error/exception handling is uniform and document it in the abc

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good idea. Did two things in the latest commit:

  1. Added a try-except block in try_load()
  2. Added docstrings to ABC methods: try_load, build, load to state the uniform expected behavior.

@qiching

qiching commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

some points:

  1. This MVP wires only nvfp4_quantize. The autotuning cold start vLLM hit was mm_fp4 (fp4_gemm) compiling ~20–30 cute-dsl tactics sequentially, and vllm #48268 already handles that today by skipping to the CuTe DSL FP4 GEMM Heuristic #2940 heuristic. Where feat(jit): JitSpec ABC + disk cache for JIT-compiled CuTe-DSL kernels #3874 comes in is the next step that wiring mm_fp4 lets compiled kernels survive restarts, which a) removes even the residual single-kernel compile skip_ops still pays per process, and b) lets us re-enable mm_fp4 autotuning without reintroducing cold start. Could you post the rollout order and rough timeline for wiring mm_fp4? (The trtllm MoE cost is profiling-bound / prebuilt, feat(jit): JitSpec ABC + disk cache for JIT-compiled CuTe-DSL kernels #3874 correctly does not apply there.)
  2. Selection rides entirely on the _nvfp4_kernel_name string; meta.json guards only arch / DSL-version / source-SHA, not per-kernel params. Since rollout means other authors write these names, could we add a test asserting the name is a function of every codegen arg?
  3. Could we document the full preconditions that same flashinfer version + nvcc arch-list (in the path) and same compile arch + nvidia-cutlass-dsl stack (in meta.json).
  4. cute-dsl kernels are not in flashinfer-jit-cache yet, so ship prebuilt kernels won't cover cute-dsl , first serve on a fresh image still compiles. Flagging to scope separately.

+1 on @aleozlx comment LGTM.
Please correct me if i understand wrong.

Thank @qiching

  1. Right the plan is to incrementally roll out to mm_fp4 and others cute-dsl kernels. Due to the issues you raised, mm_fp4 should be first, followed by others.
  2. Agreed and added a test_cute_dsl_cache.py file in latest commit
  3. Agreed and documented in cute_dsl_kernel_cache.md section 2.3
  4. Right and not all kernels will be shippable in the flashinfer-jit-cache due to some input parameters being compile-time constant but this is a direction to explore in the future. Especially for kernels like mm_fp4 where it takes a long time to compile and is in fact possible to put into flashinfer-jit-cache AFAIK.

@bkryu Cool! thanks! LGTM

@aleozlx aleozlx left a comment

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.

lgtm

@bkryu
bkryu merged commit 07d9b92 into flashinfer-ai:main Jul 15, 2026
37 of 40 checks passed
bkryu added a commit that referenced this pull request Jul 21, 2026
…parallel compilation (#4029)

<!-- .github/pull_request_template.md -->

## 📌 Description

<!-- What does this PR do? Briefly describe the changes and why they’re
needed. -->

Follow-up to #3948 (top-N tactic ranking) and built on #3874 (CuTe-DSL
disk cache). Autotuned `mm_fp4(backend='cute-dsl')` still:
* Recompiles every tactic in-process
* Compiles serially, taking 2-3 seconds per kernel

This PR:

1. Persists mm_fp4 kernels to the on-disk CuTe-DSL cache. Loading
`JITLink` cached artifacts from disk is measured to be done in ~10 ms
per kernel.
2. Parallelizes first-time compilation. The autotuner checks for
not-yet-cached tactics with a subprocess pool (default 4, RAM-capped)
that persists directly into the shared disk cache.
* default pool size of 4 was set to be conservative. Empirically
measured RAM (RSS) size per compilation was ~1GB per compilation.

### Autotune time improvements
Evaluated with
`python3 benchmarks/flashinfer_benchmark.py --routine mm_fp4 --m 256 --n
1024 --k 7168 --out_dtype bfloat16 --backends cute-dsl
--use_128x4_sf_layout --use_nvfp4 --refcheck --autotune`

End-to-end process wall time on B200, 3 runs each:

| Scenario | Runs | Median | Speedup |
|---|---|---:|---:|                             
| Current main branch main (in-process serial compilation) | 94.9 / 93.5
/ 93.2 s | **93.5 s** | 1.0× |
| this PR, first run; cold disk cache (parallel compile + persist) |
45.8 / 44.6 / 46.0 s | **45.8 s** | **~2×** |
| this PR, every subsequent process; hot disk cache (load from disk) |
4.38 / 4.35 / 4.35 s | **4.35 s** | **~20×** |

Autotuned results are unchanged: the same tactics are profiled with
bitwise identical kernel and output; just changes in the compilation
infra.

## 🔍 Related Issues

<!-- Link any related issues here -->

## 🚀 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](https://pre-commit.com/).

## 🧪 Tests

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

## Reviewer Notes

<!-- Optional: anything you'd like reviewers to focus on, concerns, etc.
-->


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Performance**
  * Improved FP4 GEMM startup by precompiling eligible CuTe-DSL tactics.
* Added parallel CuTe-DSL kernel compilation with persistent on-disk
caching to reduce repeated build times.
  * Strengthened device-scoped caching to speed up subsequent launches.
* **Reliability**
* Disk persistence failures now fall back gracefully, allowing kernels
to compile on demand.
* Updated FP4/MXF dtype handling and improved deterministic,
collision-resistant kernel naming.
* **Tests**
* Added coverage ensuring FP4 CuTe-DSL kernel names change with all
codegen arguments and are filename-safe.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@bkryu
bkryu deleted the cute_dsl_cache branch July 27, 2026 18:10
aleozlx added a commit to YangXu1990uiuc/flashinfer that referenced this pull request Aug 4, 2026
Consolidates the v2 design into docs/design_docs/, following the
structure of cute_dsl_kernel_cache.md: motivation, store layout,
environment identity, concurrency/crash safety, MeasurementPolicy,
runner contract, distributed story, alternatives, limitations.
Specifics are checked against flashinfer/autotune_cache.py on this
branch (manifest = _collect_metadata() + cache_schema + policy fields,
sha256[:16] env hash / [:24] op hash, {key, runner, tactic} entries).

Two sections go beyond restating RFC flashinfer-ai#3920:

- Relationship to the CuTe-DSL kernel cache (flashinfer-ai#3874): why the two
  caches cannot share a payload format -- opposite locking contracts
  (single-flight vs last-valid-write-wins, the latter required because
  ranks tune inside collectives), reproducible artifacts vs
  measurements -- and which mechanics should be shared anyway:
  env-record naming (meta.json vs manifest.json), one atomic-write /
  invalid-is-a-miss helper, one cache-clearing story.

- Graduation plan: autotune_v2 is a transitional name. At graduation
  autotune() becomes the v2 implementation, autotune_v2 becomes a
  deprecated alias, and the v1 spellings are retained as forwarding
  shims with cache=<path> honored as placement only. Names the four
  gates hidden behind "deprecate v1 afterwards" (framework release,
  validate_tactic adoption, execution_mode default, regret <= v1 on
  >=2 arches) and the major-bump constraint on removal, so the version
  number does not become permanent public API surface.

Also records why a separate entry point is needed: not the on-disk
format (autotune caches are already per-version disposable --
flashinfer_version is stamped by _collect_metadata() and hard-rejected
on mismatch, so no v2 process can encounter a live v1 file) but the
call-site signature (cache=<file> vs a placement-only root directory)
and the context-scoped vs process-attach lifetime change.

Flags that docs/autotuning.rst still documents v1 only and must be
updated in the change that swaps the implementation.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
aleozlx added a commit to YangXu1990uiuc/flashinfer that referenced this pull request Aug 4, 2026
§3 compared the autotune store to the CuTe-DSL kernel disk cache (flashinfer-ai#3874):
why the two cannot share a payload format, and which mechanics they
should share. It answered a question that came up in review, but in the
doc it reads as a digression into a different subsystem -- a reader
arriving at "Autotuner v2" has no reason to care about JitSpec's locking
contract, and the section invited more confusion than it resolved.

Deleted, keeping the one part that actually explains an autotuner design
decision: §2.4's "no locks" bullet now says why single-flight is right
for the kernel cache and wrong here -- compiling twice wastes CPU,
whereas ranks tune inside collectives, so a cross-rank lock would
serialize warmup or deadlock it. That is the sentence a reader needs at
the point they wonder why publishes are unsynchronised.

The cross-cutting cleanup §3 proposed (one atomic-write /
invalid-is-a-miss helper, one name for the environment record, one
cache-clearing story) is real but belongs in an issue against the JIT
layer, not in this doc.

Sections 4-7 renumbered to 3-6; cross-references updated. Code comments
cite §2.1/§2.4/§2.5 only, so they are unaffected.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bkryu added a commit that referenced this pull request Aug 8, 2026
## 📌 Description

Rolls the on-disk CuTe-DSL kernel cache out to the three sm12x NVFP4
fused-MoE kernels (micro,
static, dynamic), addressing #4317. The infrastructure landed in #3874 /
#4029; @bkryu noted in
#4317 that it still had to reach individual kernels and that the core
team lacked bandwidth, so this
applies it to b12x MoE.

Follows the rollout note in `docs/design_docs/cute_dsl_kernel_cache.md`:
each `cute.compile` becomes
a closure passed to `build_and_load_cute_dsl_kernel`. The three
cache-key tuples become named
functions so both cache levels derive from one source of truth, and the
artifact name appends a
digest of that tuple — the keys hold floats and `None` (`swiglu_*`), and
`1.5` / `-1.5` both sanitize
to `1_5`, so a formatted name would not be injective.

As in the existing adopters, the kernels now compile against
`make_fake_stream(use_tvm_ffi_env_stream=True)`, so TVM-FFI supplies the
caller's current stream and
the two launch sites no longer pass one (compiled signatures: 24 / 24 /
32 parameters).

**Not covered:** the direct-micro kernel
(`compile_direct_micro_kernel`), which this module started
dispatching to recently. It compiles without `--enable-tvm-ffi` and
launches through the DSL rather
than a TVM-FFI callable, so caching it is a separate change — #4317 is
only partly closed by this PR.

### Measured — GB10 (sm_121), DSL 4.6.0, five kernel shapes per process

| process | total compile + load |
|---|---:|
| before, two runs | 17.92 s / 17.95 s |
| after, cold first run | 18.91 s |
| after, two later runs | **0.130 s / 0.134 s** (~135×) |

Warm, per kernel: dynamic 7.7 s → 1 ms; static 3.2 s → 1 ms; micro
2.3–3.2 s → 1 ms (the first warm
kernel pays 0.13 s of one-off module setup). The cold run costs a few
percent for the export.
Artifacts are 161–262 KB each.

## 🔍 Related Issues

#4317 (partly — see the scope note above). Builds on the cache
infrastructure from #3874 and #4029.

## 🚀 Pull Request Checklist

### ✅ Pre-commit Checks

- [x] `pre-commit` installed.
- [x] Hooks installed.
- [x] `pre-commit run` on the two changed files: all hooks pass, no
files modified. (I ran it
file-scoped rather than `--all-files`, since the remaining hooks are
repo-wide.)

## 🧪 Tests

- [x] Tests added.
- [x] The new tests pass; see the caveat below for what does not run on
my hardware.

- New `tests/moe/test_b12x_moe_kernel_cache.py`: 61 naming-contract
tests — signature coverage,
per-argument perturbation, symbol safety, cross-family collision —
replicating
`tests/jit/test_cute_dsl_cache.py` as the design doc asks of new
adopters (happy to fold them into
  that file instead if you would rather keep all adopters together).
- Also checked on the same host: a corrupt artifact and a read-only
cache directory each fall back to
compiling with a warning, and editing a key source invalidates the
module and recompiles it once.

**Not verified — please check on sm120 hardware.**
`tests/moe/test_b12x_fused_moe.py`'s numerical
tests do not run on my GB10: the nvcc reference ops fail to build (`CUDA
compiler and CUDA toolkit
headers are incompatible`). The suite gives an identical `142 failed, 15
passed` — the same 142 test
ids — on unmodified `main` and on this branch, so I have no accuracy
signal either way. All timings
above are compile/load time; steady-state kernel performance should be
unchanged, since it is the
same binary.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added shared on-disk caching for static, micro, and dynamic MoE
kernels.
  * Improved cache invalidation when source dependencies change.
* Kernel launches now automatically use the caller’s active CUDA stream.

* **Tests**
* Added comprehensive coverage for cache-key completeness, naming
stability, symbol safety, and uniqueness across kernel types.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: Han-Yin Chang <nick20350@gmail.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Brian K. Ryu <bryu@nvidia.com>
bkryu added a commit that referenced this pull request Aug 13, 2026
…r mm_bf16_fp4 (#4038)

## 📌 Description

#3597 added `mm_bf16_fp4` (bf16 activations x nvfp4 weights) so vLLM can
replace Marlin, its default W4A16 backend, with a FlashInfer kernel; DGX
Spark is the lead target. On decode shapes the kernel trailed Marlin for
two reasons: small-n grids underfill the GPU, and at single-token
batches (m=1) too few resident warps per SM hide DRAM latency. This PR
addresses both: m=1 decode beats or matches Marlin on every part we
measured, and end-to-end serving of Qwen3.6-27B-NVFP4 flips from behind
to ahead at batch 1 with no regression elsewhere.

**What it does**

- Adds split-K tactics (2/4/8 splits) to the autotuner space, offered
only when splitting shortens the grid's last wave by at least 25%.
Splits write fp32 partials, and a PDL-chained reduce kernel sums them in
fixed order, so results are deterministic run to run.
- Adds occupancy tactics: 2 or 3 co-resident CTAs per SM trade pipeline
depth for latency hiding on weight-bound grids, plus occupancy 2
combined with split-K for narrow-n shapes.
- Adds a streaming GEMV kernel (`gemv_bf16_fp4_sm12x.py`) for the
bandwidth-bound m=1 case: no shared memory, no tensor cores, weights
stream from global memory to registers with latency hidden by warp
count. It reads the same packed operands as the MMA kernel, so the
autotuner picks between the two per shape.
- Sizes GEMV split-K from the device: alongside the power-of-2 splits,
the menu carries a split targeting ~20 warps/SM (the measured saturation
point). Tactic indices become device-scoped, so the autotuner cache key
now carries the SM count.
- Routes the no-autotune m=1 fallback onto the GEMV. Serving stacks do
not tune every shape (vLLM's warmup never captures the logits GEMM), so
the lm_head always takes this path.
- Fixes kernel launch to pass no cluster dimensions: the boilerplate
`cluster=[1,1,1]` routed launches through the cluster work distributor,
whose co-residency cap silently defeated the occupancy tactics on SM12x.

No public API changes. The one observable behavior change: untuned m=1
calls on SM12x now run the GEMV, whose output is bitwise different from
the MMA heuristic's but equally accurate and still deterministic.

### Performance

#### Split-K on the #3597 decode shapes (RTX PRO 6000 / DGX Spark / RTX
5080)

Single-token decode GEMMs (m=1). The first table covers serving-class
shapes (Llama-8B projection layers plus the #3597 example shape); the
second covers #3597's own benchmark grid. Median GPU time over
CUDA-graph replays with a cold L2 cache, as in serving. Baseline is
vLLM's Marlin on the same GPU; FlashInfer runs with autotuning. Speedup
= Marlin time / FlashInfer time. Each cell is a RTX PRO 6000 / DGX Spark
/ RTX 5080 triple.

| n x k | vs Marlin, before this PR | vs Marlin, with this PR |
|--:|:--:|:--:|
| 2048x7168 | 0.57 / 1.00 / 0.47 | **1.36** / **1.02** / **0.78** |
| 4096x4096 | 0.75 / 0.91 / 0.90 | **1.06** / **1.00** / 0.90 |
| 4096x14336 | 0.60 / 0.91 / 0.78 | **0.98** / **1.00** / 0.78 |
| 14336x4096 | 0.97 / 1.00 / 0.86 | 0.97 / 1.00 / 0.86 |
| 10304x2688 | 0.78 / 0.98 / 0.96 | 0.78 / 0.98 / 0.96 |

The same comparison over #3597's benchmark grid (4096x4096 appears in
the table above):

| n x k | vs Marlin, before this PR | vs Marlin, with this PR |
|--:|:--:|:--:|
| 512x2048 | 2.09 / 0.95 / 0.95 | **4.48** / **1.43** / **2.30** |
| 512x4096 | 1.19 / 0.70 / 0.56 | **3.61** / **1.26** / **1.88** |
| 1024x2048 | 1.26 / 0.98 / 0.70 | **2.39** / **1.03** / **1.32** |
| 1024x4096 | 0.99 / 0.86 / 0.49 | **2.72** / **1.04** / **1.21** |
| 2048x512 | 1.86 / 1.25 / 1.19 | 1.86 / 1.25 / 1.19 |
| 2048x1024 | 1.36 / 1.16 / 0.90 | **1.40** / 1.16 / **1.02** |
| 2048x2048 | 1.17 / 1.11 / 0.70 | **1.72** / **1.02**\* (1.11) /
**0.97** |
| 2048x4096 | 0.73 / 1.08 / 0.54 | **1.48** / **1.04**\* (1.08) /
**0.84** |
| 4096x512 | 1.40 / 1.01 / 1.38 | 1.40 / **0.95**\* (1.01) / 1.38 |
| 4096x1024 | 1.41 / 0.93 / 1.18 | 1.41 / **0.95** / 1.18 |
| 4096x2048 | 0.95 / 0.95 / 1.06 | 0.95 / **1.00** / 1.06 |
| 131072x2048 | 0.98 / 0.97 / 0.90 | 0.98 / 0.97 / 0.90 |
| 248320x2048 | 0.98 / 1.00 / 0.93 | 0.98 / 1.00 / 0.93 |

- Bold marks the cells this PR changes (the tuner picks a new split-K
tactic); unbolded picks perform as before.
- \* These three cells are tuner mis-picks, not kernel regressions: an
accurate pick would keep #3597's pre-existing non-split config, and the
value in parentheses is what that config achieves. The Reviewer Notes
explain the cause.
- On the serving shapes, the RTX 5080 column stays below 1.0 even where
this PR helps. Profiling of the larger losses points to activation
re-reads through L2, a separate problem from grid fill and out of scope
here.

#### Qwen3.6-27B-NVFP4 decode GEMMs at m=1 (RTX 5080, DGX Spark)

Same methodology as above; speedup = Marlin time / FlashInfer time.

| GEMM (n x k) | RTX 5080 | DGX Spark |
|--|:--:|:--:|
| gate_up 34816x5120 | **1.03** | 1.00 |
| down 5120x17408 | **1.03** | 1.00 |
| lm_head 248320x5120 | **1.04**\* | **1.04** |

\* Marlin's lm_head repack does not fit on the 16 GB RTX 5080, so this
cell compares against the best in-tree MMA tactic. Spark ties at its
bandwidth floor on the first two shapes.

#### End-to-end vLLM serving of Qwen3.6-27B-NVFP4 (RTX PRO 6000)

Full serving A/B, FlashInfer leg vs Marlin leg under identical settings,
aiperf output-token throughput, ratio = FlashInfer / Marlin: batch-1
decode 1.011x, low-concurrency speculative decode 1.08 to 1.11x ahead,
and every other cell at parity within run-to-run noise (0.978 to 1.014x)
with no regression beyond it.

## 🔍 Related Issues

Follow-up to #3597.

## 🚀 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.
- [x] All tests are passing (`unittest`, etc.).

New tests:

- Every enumerated tactic (MMA and GEMV) is checked against a reference
and for bit-exact run-to-run determinism.
- Unit tests pin the fallback selectors' picks; one test drives
tactic=-1 through the GEMV fallback end to end.
- Full test file passes on RTX 5080, RTX PRO 6000, and GB10.

## Reviewer Notes

- Most gains require autotuning, which serving frameworks run at
startup. The no-autotune fallback picks match the tuner's choices on
every part we measured.
- The autotuner times candidates with a warm L2 while decode serving
runs cold, so it can over-rank split tactics; the 25% last-wave guard
compensates but does not fully close it (the three Spark cells in the
grid table). This measurement gap is general and deserves its own issue.
- The fallback picks add JIT-compiled kernel variants per decode shape
class, cached in-process only; that cost amortizes to once per machine
when this module adopts the #3874 CuTe-DSL disk cache, as #4029 did for
the sibling `mm_fp4` path. The GEMV's device-derived splits widen this
surface, so the follow-up is worth prioritizing.
- Other FlashInfer cute-dsl kernels also pass `cluster=[1,1,1]` at
launch and inherit the same co-residency cap; they are worth a separate
audit.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added split-K support for bf16 × fp4 matrix multiplication to improve
performance across varying workloads.
- Added a dedicated SM12x GEMV path for efficient single-row operations.
- Added automatic tuning for split counts, occupancy, and
device-specific execution strategies.
- Added support for FP16 GEMV outputs and deterministic partial-result
reduction.

- **Bug Fixes**
- Improved handling of GEMV and split-K fallback selection across
supported shapes and GPU configurations.

- **Tests**
- Added coverage for accuracy, determinism, GEMV correctness, and
split-K selection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Brian K. Ryu <bryu@nvidia.com>
jefby pushed a commit to jefby/flashinfer that referenced this pull request Aug 19, 2026
…r mm_bf16_fp4 (flashinfer-ai#4038)

## 📌 Description

flashinfer-ai#3597 added `mm_bf16_fp4` (bf16 activations x nvfp4 weights) so vLLM can
replace Marlin, its default W4A16 backend, with a FlashInfer kernel; DGX
Spark is the lead target. On decode shapes the kernel trailed Marlin for
two reasons: small-n grids underfill the GPU, and at single-token
batches (m=1) too few resident warps per SM hide DRAM latency. This PR
addresses both: m=1 decode beats or matches Marlin on every part we
measured, and end-to-end serving of Qwen3.6-27B-NVFP4 flips from behind
to ahead at batch 1 with no regression elsewhere.

**What it does**

- Adds split-K tactics (2/4/8 splits) to the autotuner space, offered
only when splitting shortens the grid's last wave by at least 25%.
Splits write fp32 partials, and a PDL-chained reduce kernel sums them in
fixed order, so results are deterministic run to run.
- Adds occupancy tactics: 2 or 3 co-resident CTAs per SM trade pipeline
depth for latency hiding on weight-bound grids, plus occupancy 2
combined with split-K for narrow-n shapes.
- Adds a streaming GEMV kernel (`gemv_bf16_fp4_sm12x.py`) for the
bandwidth-bound m=1 case: no shared memory, no tensor cores, weights
stream from global memory to registers with latency hidden by warp
count. It reads the same packed operands as the MMA kernel, so the
autotuner picks between the two per shape.
- Sizes GEMV split-K from the device: alongside the power-of-2 splits,
the menu carries a split targeting ~20 warps/SM (the measured saturation
point). Tactic indices become device-scoped, so the autotuner cache key
now carries the SM count.
- Routes the no-autotune m=1 fallback onto the GEMV. Serving stacks do
not tune every shape (vLLM's warmup never captures the logits GEMM), so
the lm_head always takes this path.
- Fixes kernel launch to pass no cluster dimensions: the boilerplate
`cluster=[1,1,1]` routed launches through the cluster work distributor,
whose co-residency cap silently defeated the occupancy tactics on SM12x.

No public API changes. The one observable behavior change: untuned m=1
calls on SM12x now run the GEMV, whose output is bitwise different from
the MMA heuristic's but equally accurate and still deterministic.

### Performance

#### Split-K on the flashinfer-ai#3597 decode shapes (RTX PRO 6000 / DGX Spark / RTX
5080)

Single-token decode GEMMs (m=1). The first table covers serving-class
shapes (Llama-8B projection layers plus the flashinfer-ai#3597 example shape); the
second covers flashinfer-ai#3597's own benchmark grid. Median GPU time over
CUDA-graph replays with a cold L2 cache, as in serving. Baseline is
vLLM's Marlin on the same GPU; FlashInfer runs with autotuning. Speedup
= Marlin time / FlashInfer time. Each cell is a RTX PRO 6000 / DGX Spark
/ RTX 5080 triple.

| n x k | vs Marlin, before this PR | vs Marlin, with this PR |
|--:|:--:|:--:|
| 2048x7168 | 0.57 / 1.00 / 0.47 | **1.36** / **1.02** / **0.78** |
| 4096x4096 | 0.75 / 0.91 / 0.90 | **1.06** / **1.00** / 0.90 |
| 4096x14336 | 0.60 / 0.91 / 0.78 | **0.98** / **1.00** / 0.78 |
| 14336x4096 | 0.97 / 1.00 / 0.86 | 0.97 / 1.00 / 0.86 |
| 10304x2688 | 0.78 / 0.98 / 0.96 | 0.78 / 0.98 / 0.96 |

The same comparison over flashinfer-ai#3597's benchmark grid (4096x4096 appears in
the table above):

| n x k | vs Marlin, before this PR | vs Marlin, with this PR |
|--:|:--:|:--:|
| 512x2048 | 2.09 / 0.95 / 0.95 | **4.48** / **1.43** / **2.30** |
| 512x4096 | 1.19 / 0.70 / 0.56 | **3.61** / **1.26** / **1.88** |
| 1024x2048 | 1.26 / 0.98 / 0.70 | **2.39** / **1.03** / **1.32** |
| 1024x4096 | 0.99 / 0.86 / 0.49 | **2.72** / **1.04** / **1.21** |
| 2048x512 | 1.86 / 1.25 / 1.19 | 1.86 / 1.25 / 1.19 |
| 2048x1024 | 1.36 / 1.16 / 0.90 | **1.40** / 1.16 / **1.02** |
| 2048x2048 | 1.17 / 1.11 / 0.70 | **1.72** / **1.02**\* (1.11) /
**0.97** |
| 2048x4096 | 0.73 / 1.08 / 0.54 | **1.48** / **1.04**\* (1.08) /
**0.84** |
| 4096x512 | 1.40 / 1.01 / 1.38 | 1.40 / **0.95**\* (1.01) / 1.38 |
| 4096x1024 | 1.41 / 0.93 / 1.18 | 1.41 / **0.95** / 1.18 |
| 4096x2048 | 0.95 / 0.95 / 1.06 | 0.95 / **1.00** / 1.06 |
| 131072x2048 | 0.98 / 0.97 / 0.90 | 0.98 / 0.97 / 0.90 |
| 248320x2048 | 0.98 / 1.00 / 0.93 | 0.98 / 1.00 / 0.93 |

- Bold marks the cells this PR changes (the tuner picks a new split-K
tactic); unbolded picks perform as before.
- \* These three cells are tuner mis-picks, not kernel regressions: an
accurate pick would keep flashinfer-ai#3597's pre-existing non-split config, and the
value in parentheses is what that config achieves. The Reviewer Notes
explain the cause.
- On the serving shapes, the RTX 5080 column stays below 1.0 even where
this PR helps. Profiling of the larger losses points to activation
re-reads through L2, a separate problem from grid fill and out of scope
here.

#### Qwen3.6-27B-NVFP4 decode GEMMs at m=1 (RTX 5080, DGX Spark)

Same methodology as above; speedup = Marlin time / FlashInfer time.

| GEMM (n x k) | RTX 5080 | DGX Spark |
|--|:--:|:--:|
| gate_up 34816x5120 | **1.03** | 1.00 |
| down 5120x17408 | **1.03** | 1.00 |
| lm_head 248320x5120 | **1.04**\* | **1.04** |

\* Marlin's lm_head repack does not fit on the 16 GB RTX 5080, so this
cell compares against the best in-tree MMA tactic. Spark ties at its
bandwidth floor on the first two shapes.

#### End-to-end vLLM serving of Qwen3.6-27B-NVFP4 (RTX PRO 6000)

Full serving A/B, FlashInfer leg vs Marlin leg under identical settings,
aiperf output-token throughput, ratio = FlashInfer / Marlin: batch-1
decode 1.011x, low-concurrency speculative decode 1.08 to 1.11x ahead,
and every other cell at parity within run-to-run noise (0.978 to 1.014x)
with no regression beyond it.

## 🔍 Related Issues

Follow-up to flashinfer-ai#3597.

## 🚀 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.
- [x] All tests are passing (`unittest`, etc.).

New tests:

- Every enumerated tactic (MMA and GEMV) is checked against a reference
and for bit-exact run-to-run determinism.
- Unit tests pin the fallback selectors' picks; one test drives
tactic=-1 through the GEMV fallback end to end.
- Full test file passes on RTX 5080, RTX PRO 6000, and GB10.

## Reviewer Notes

- Most gains require autotuning, which serving frameworks run at
startup. The no-autotune fallback picks match the tuner's choices on
every part we measured.
- The autotuner times candidates with a warm L2 while decode serving
runs cold, so it can over-rank split tactics; the 25% last-wave guard
compensates but does not fully close it (the three Spark cells in the
grid table). This measurement gap is general and deserves its own issue.
- The fallback picks add JIT-compiled kernel variants per decode shape
class, cached in-process only; that cost amortizes to once per machine
when this module adopts the flashinfer-ai#3874 CuTe-DSL disk cache, as flashinfer-ai#4029 did for
the sibling `mm_fp4` path. The GEMV's device-derived splits widen this
surface, so the follow-up is worth prioritizing.
- Other FlashInfer cute-dsl kernels also pass `cluster=[1,1,1]` at
launch and inherit the same co-residency cap; they are worth a separate
audit.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
- Added split-K support for bf16 × fp4 matrix multiplication to improve
performance across varying workloads.
- Added a dedicated SM12x GEMV path for efficient single-row operations.
- Added automatic tuning for split counts, occupancy, and
device-specific execution strategies.
- Added support for FP16 GEMV outputs and deterministic partial-result
reduction.

- **Bug Fixes**
- Improved handling of GEMV and split-K fallback selection across
supported shapes and GPU configurations.

- **Tests**
- Added coverage for accuracy, determinism, GEMV correctness, and
split-K selection.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Brian K. Ryu <bryu@nvidia.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Aug 20, 2026
Consolidates the v2 design into docs/design_docs/, following the
structure of cute_dsl_kernel_cache.md: motivation, store layout,
environment identity, concurrency/crash safety, MeasurementPolicy,
runner contract, distributed story, alternatives, limitations.
Specifics are checked against flashinfer/autotune_cache.py on this
branch (manifest = _collect_metadata() + cache_schema + policy fields,
sha256[:16] env hash / [:24] op hash, {key, runner, tactic} entries).

Two sections go beyond restating RFC flashinfer-ai#3920:

- Relationship to the CuTe-DSL kernel cache (flashinfer-ai#3874): why the two
  caches cannot share a payload format -- opposite locking contracts
  (single-flight vs last-valid-write-wins, the latter required because
  ranks tune inside collectives), reproducible artifacts vs
  measurements -- and which mechanics should be shared anyway:
  env-record naming (meta.json vs manifest.json), one atomic-write /
  invalid-is-a-miss helper, one cache-clearing story.

- Graduation plan: autotune_v2 is a transitional name. At graduation
  autotune() becomes the v2 implementation, autotune_v2 becomes a
  deprecated alias, and the v1 spellings are retained as forwarding
  shims with cache=<path> honored as placement only. Names the four
  gates hidden behind "deprecate v1 afterwards" (framework release,
  validate_tactic adoption, execution_mode default, regret <= v1 on
  >=2 arches) and the major-bump constraint on removal, so the version
  number does not become permanent public API surface.

Also records why a separate entry point is needed: not the on-disk
format (autotune caches are already per-version disposable --
flashinfer_version is stamped by _collect_metadata() and hard-rejected
on mismatch, so no v2 process can encounter a live v1 file) but the
call-site signature (cache=<file> vs a placement-only root directory)
and the context-scoped vs process-attach lifetime change.

Flags that docs/autotuning.rst still documents v1 only and must be
updated in the change that swaps the implementation.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Aug 20, 2026
§3 compared the autotune store to the CuTe-DSL kernel disk cache (flashinfer-ai#3874):
why the two cannot share a payload format, and which mechanics they
should share. It answered a question that came up in review, but in the
doc it reads as a digression into a different subsystem -- a reader
arriving at "Autotuner v2" has no reason to care about JitSpec's locking
contract, and the section invited more confusion than it resolved.

Deleted, keeping the one part that actually explains an autotuner design
decision: §2.4's "no locks" bullet now says why single-flight is right
for the kernel cache and wrong here -- compiling twice wastes CPU,
whereas ranks tune inside collectives, so a cross-rank lock would
serialize warmup or deadlock it. That is the sentence a reader needs at
the point they wonder why publishes are unsynchronised.

The cross-cutting cleanup §3 proposed (one atomic-write /
invalid-is-a-miss helper, one name for the environment record, one
cache-clearing story) is real but belongs in an issue against the JIT
layer, not in this doc.

Sections 4-7 renumbered to 3-6; cross-references updated. Code comments
cite §2.1/§2.4/§2.5 only, so they are unaffected.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Aug 28, 2026
Consolidates the v2 design into docs/design_docs/, following the
structure of cute_dsl_kernel_cache.md: motivation, store layout,
environment identity, concurrency/crash safety, MeasurementPolicy,
runner contract, distributed story, alternatives, limitations.
Specifics are checked against flashinfer/autotune_cache.py on this
branch (manifest = _collect_metadata() + cache_schema + policy fields,
sha256[:16] env hash / [:24] op hash, {key, runner, tactic} entries).

Two sections go beyond restating RFC flashinfer-ai#3920:

- Relationship to the CuTe-DSL kernel cache (flashinfer-ai#3874): why the two
  caches cannot share a payload format -- opposite locking contracts
  (single-flight vs last-valid-write-wins, the latter required because
  ranks tune inside collectives), reproducible artifacts vs
  measurements -- and which mechanics should be shared anyway:
  env-record naming (meta.json vs manifest.json), one atomic-write /
  invalid-is-a-miss helper, one cache-clearing story.

- Graduation plan: autotune_v2 is a transitional name. At graduation
  autotune() becomes the v2 implementation, autotune_v2 becomes a
  deprecated alias, and the v1 spellings are retained as forwarding
  shims with cache=<path> honored as placement only. Names the four
  gates hidden behind "deprecate v1 afterwards" (framework release,
  validate_tactic adoption, execution_mode default, regret <= v1 on
  >=2 arches) and the major-bump constraint on removal, so the version
  number does not become permanent public API surface.

Also records why a separate entry point is needed: not the on-disk
format (autotune caches are already per-version disposable --
flashinfer_version is stamped by _collect_metadata() and hard-rejected
on mismatch, so no v2 process can encounter a live v1 file) but the
call-site signature (cache=<file> vs a placement-only root directory)
and the context-scoped vs process-attach lifetime change.

Flags that docs/autotuning.rst still documents v1 only and must be
updated in the change that swaps the implementation.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Aug 28, 2026
§3 compared the autotune store to the CuTe-DSL kernel disk cache (flashinfer-ai#3874):
why the two cannot share a payload format, and which mechanics they
should share. It answered a question that came up in review, but in the
doc it reads as a digression into a different subsystem -- a reader
arriving at "Autotuner v2" has no reason to care about JitSpec's locking
contract, and the section invited more confusion than it resolved.

Deleted, keeping the one part that actually explains an autotuner design
decision: §2.4's "no locks" bullet now says why single-flight is right
for the kernel cache and wrong here -- compiling twice wastes CPU,
whereas ranks tune inside collectives, so a cross-rank lock would
serialize warmup or deadlock it. That is the sentence a reader needs at
the point they wonder why publishes are unsynchronised.

The cross-cutting cleanup §3 proposed (one atomic-write /
invalid-is-a-miss helper, one name for the environment record, one
cache-clearing story) is real but belongs in an issue against the JIT
layer, not in this doc.

Sections 4-7 renumbered to 3-6; cross-references updated. Code comments
cite §2.1/§2.4/§2.5 only, so they are unaffected.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Sep 1, 2026
Consolidates the v2 design into docs/design_docs/, following the
structure of cute_dsl_kernel_cache.md: motivation, store layout,
environment identity, concurrency/crash safety, MeasurementPolicy,
runner contract, distributed story, alternatives, limitations.
Specifics are checked against flashinfer/autotune_cache.py on this
branch (manifest = _collect_metadata() + cache_schema + policy fields,
sha256[:16] env hash / [:24] op hash, {key, runner, tactic} entries).

Two sections go beyond restating RFC flashinfer-ai#3920:

- Relationship to the CuTe-DSL kernel cache (flashinfer-ai#3874): why the two
  caches cannot share a payload format -- opposite locking contracts
  (single-flight vs last-valid-write-wins, the latter required because
  ranks tune inside collectives), reproducible artifacts vs
  measurements -- and which mechanics should be shared anyway:
  env-record naming (meta.json vs manifest.json), one atomic-write /
  invalid-is-a-miss helper, one cache-clearing story.

- Graduation plan: autotune_v2 is a transitional name. At graduation
  autotune() becomes the v2 implementation, autotune_v2 becomes a
  deprecated alias, and the v1 spellings are retained as forwarding
  shims with cache=<path> honored as placement only. Names the four
  gates hidden behind "deprecate v1 afterwards" (framework release,
  validate_tactic adoption, execution_mode default, regret <= v1 on
  >=2 arches) and the major-bump constraint on removal, so the version
  number does not become permanent public API surface.

Also records why a separate entry point is needed: not the on-disk
format (autotune caches are already per-version disposable --
flashinfer_version is stamped by _collect_metadata() and hard-rejected
on mismatch, so no v2 process can encounter a live v1 file) but the
call-site signature (cache=<file> vs a placement-only root directory)
and the context-scoped vs process-attach lifetime change.

Flags that docs/autotuning.rst still documents v1 only and must be
updated in the change that swaps the implementation.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Sep 1, 2026
§3 compared the autotune store to the CuTe-DSL kernel disk cache (flashinfer-ai#3874):
why the two cannot share a payload format, and which mechanics they
should share. It answered a question that came up in review, but in the
doc it reads as a digression into a different subsystem -- a reader
arriving at "Autotuner v2" has no reason to care about JitSpec's locking
contract, and the section invited more confusion than it resolved.

Deleted, keeping the one part that actually explains an autotuner design
decision: §2.4's "no locks" bullet now says why single-flight is right
for the kernel cache and wrong here -- compiling twice wastes CPU,
whereas ranks tune inside collectives, so a cross-rank lock would
serialize warmup or deadlock it. That is the sentence a reader needs at
the point they wonder why publishes are unsynchronised.

The cross-cutting cleanup §3 proposed (one atomic-write /
invalid-is-a-miss helper, one name for the environment record, one
cache-clearing story) is real but belongs in an issue against the JIT
layer, not in this doc.

Sections 4-7 renumbered to 3-6; cross-references updated. Code comments
cite §2.1/§2.4/§2.5 only, so they are unaffected.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Sep 3, 2026
Consolidates the v2 design into docs/design_docs/, following the
structure of cute_dsl_kernel_cache.md: motivation, store layout,
environment identity, concurrency/crash safety, MeasurementPolicy,
runner contract, distributed story, alternatives, limitations.
Specifics are checked against flashinfer/autotune_cache.py on this
branch (manifest = _collect_metadata() + cache_schema + policy fields,
sha256[:16] env hash / [:24] op hash, {key, runner, tactic} entries).

Two sections go beyond restating RFC flashinfer-ai#3920:

- Relationship to the CuTe-DSL kernel cache (flashinfer-ai#3874): why the two
  caches cannot share a payload format -- opposite locking contracts
  (single-flight vs last-valid-write-wins, the latter required because
  ranks tune inside collectives), reproducible artifacts vs
  measurements -- and which mechanics should be shared anyway:
  env-record naming (meta.json vs manifest.json), one atomic-write /
  invalid-is-a-miss helper, one cache-clearing story.

- Graduation plan: autotune_v2 is a transitional name. At graduation
  autotune() becomes the v2 implementation, autotune_v2 becomes a
  deprecated alias, and the v1 spellings are retained as forwarding
  shims with cache=<path> honored as placement only. Names the four
  gates hidden behind "deprecate v1 afterwards" (framework release,
  validate_tactic adoption, execution_mode default, regret <= v1 on
  >=2 arches) and the major-bump constraint on removal, so the version
  number does not become permanent public API surface.

Also records why a separate entry point is needed: not the on-disk
format (autotune caches are already per-version disposable --
flashinfer_version is stamped by _collect_metadata() and hard-rejected
on mismatch, so no v2 process can encounter a live v1 file) but the
call-site signature (cache=<file> vs a placement-only root directory)
and the context-scoped vs process-attach lifetime change.

Flags that docs/autotuning.rst still documents v1 only and must be
updated in the change that swaps the implementation.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Sep 3, 2026
§3 compared the autotune store to the CuTe-DSL kernel disk cache (flashinfer-ai#3874):
why the two cannot share a payload format, and which mechanics they
should share. It answered a question that came up in review, but in the
doc it reads as a digression into a different subsystem -- a reader
arriving at "Autotuner v2" has no reason to care about JitSpec's locking
contract, and the section invited more confusion than it resolved.

Deleted, keeping the one part that actually explains an autotuner design
decision: §2.4's "no locks" bullet now says why single-flight is right
for the kernel cache and wrong here -- compiling twice wastes CPU,
whereas ranks tune inside collectives, so a cross-rank lock would
serialize warmup or deadlock it. That is the sentence a reader needs at
the point they wonder why publishes are unsynchronised.

The cross-cutting cleanup §3 proposed (one atomic-write /
invalid-is-a-miss helper, one name for the environment record, one
cache-clearing story) is real but belongs in an issue against the JIT
layer, not in this doc.

Sections 4-7 renumbered to 3-6; cross-references updated. Code comments
cite §2.1/§2.4/§2.5 only, so they are unaffected.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Sep 4, 2026
Consolidates the v2 design into docs/design_docs/, following the
structure of cute_dsl_kernel_cache.md: motivation, store layout,
environment identity, concurrency/crash safety, MeasurementPolicy,
runner contract, distributed story, alternatives, limitations.
Specifics are checked against flashinfer/autotune_cache.py on this
branch (manifest = _collect_metadata() + cache_schema + policy fields,
sha256[:16] env hash / [:24] op hash, {key, runner, tactic} entries).

Two sections go beyond restating RFC flashinfer-ai#3920:

- Relationship to the CuTe-DSL kernel cache (flashinfer-ai#3874): why the two
  caches cannot share a payload format -- opposite locking contracts
  (single-flight vs last-valid-write-wins, the latter required because
  ranks tune inside collectives), reproducible artifacts vs
  measurements -- and which mechanics should be shared anyway:
  env-record naming (meta.json vs manifest.json), one atomic-write /
  invalid-is-a-miss helper, one cache-clearing story.

- Graduation plan: autotune_v2 is a transitional name. At graduation
  autotune() becomes the v2 implementation, autotune_v2 becomes a
  deprecated alias, and the v1 spellings are retained as forwarding
  shims with cache=<path> honored as placement only. Names the four
  gates hidden behind "deprecate v1 afterwards" (framework release,
  validate_tactic adoption, execution_mode default, regret <= v1 on
  >=2 arches) and the major-bump constraint on removal, so the version
  number does not become permanent public API surface.

Also records why a separate entry point is needed: not the on-disk
format (autotune caches are already per-version disposable --
flashinfer_version is stamped by _collect_metadata() and hard-rejected
on mismatch, so no v2 process can encounter a live v1 file) but the
call-site signature (cache=<file> vs a placement-only root directory)
and the context-scoped vs process-attach lifetime change.

Flags that docs/autotuning.rst still documents v1 only and must be
updated in the change that swaps the implementation.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
YangXu1990uiuc pushed a commit to YangXu1990uiuc/flashinfer that referenced this pull request Sep 4, 2026
§3 compared the autotune store to the CuTe-DSL kernel disk cache (flashinfer-ai#3874):
why the two cannot share a payload format, and which mechanics they
should share. It answered a question that came up in review, but in the
doc it reads as a digression into a different subsystem -- a reader
arriving at "Autotuner v2" has no reason to care about JitSpec's locking
contract, and the section invited more confusion than it resolved.

Deleted, keeping the one part that actually explains an autotuner design
decision: §2.4's "no locks" bullet now says why single-flight is right
for the kernel cache and wrong here -- compiling twice wastes CPU,
whereas ranks tune inside collectives, so a cross-rank lock would
serialize warmup or deadlock it. That is the sentence a reader needs at
the point they wonder why publishes are unsynchronised.

The cross-cutting cleanup §3 proposed (one atomic-write /
invalid-is-a-miss helper, one name for the environment record, one
cache-clearing story) is real but belongs in an issue against the JIT
layer, not in this doc.

Sections 4-7 renumbered to 3-6; cross-references updated. Code comments
cite §2.1/§2.4/§2.5 only, so they are unaffected.

AI-assisted: drafted with Claude Code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants