Skip to content

Studio: NVFP4 flashinfer backend kernel items (device guard, persistent barrier, bias path, cached dispatch) - #10731

Open
danielhanchen wants to merge 56 commits into
studio-nvfp4-imagefrom
studio-nvfp4-kernels
Open

danielhanchen wants to merge 56 commits into
studio-nvfp4-imagefrom
studio-nvfp4-kernels

Conversation

@danielhanchen

@danielhanchen danielhanchen commented Sep 10, 2026 •

Copy link
Copy Markdown
Member

Summary

Kernel-side items on the flashinfer NVFP4 backend introduced by the per-layer image policy PR #10730. Stacked on studio-nvfp4-image; retarget to main once that one merges. Four items, each behind its own switch, none changing what a render looks like:

  1. Device guard audit (diffusion_nvfp4_ops.py, diffusion_nvfp4_linear.py). Every flashinfer entry point sits inside with torch.cuda.device(t.device): the quantiser body, the GEMM body spanning barrier and GEMM, the plan builder including AutoTuner.choose_one and the cutlass module and cache-buffer getters, the preflight, and the warm-up entry. Skipped inside the traced layer forward under is_compiling() because inductor's wrapper already opens one and a live context manager there is a graph break. A source test bans torch.cuda.set_stream in the subtree (it silently sets the current device; that is how three cards were lost on this host). A CUDA test loads the layer on card 1 with card 0 current.

  2. Persistent PDL barrier. flashinfer launches the CUTLASS FP4 GEMM with programmatic dependent launch while the kernel's griddepcontrol wait is compiled out, so the GEMM can start before the quantiser has finished writing (torch.empty output: 50 of 50 non-finite in the harness; a one-element fill: 0 of 50). The image PR paid a torch.zeros(1) allocation per GEMM. This PR keeps one process-global one-element buffer per device and zeroes it inside the mm op body before the GEMM; never allocated during capture (falls back to uncached zeros), reset with the graph pool on unload, UNSLOTH_NVFP4_ZERO_BUFFER=1 keeps the full memset for comparison. Not an op argument: a mutable input would go through auto_functionalized and clone. A comment above the buffer names what to delete when flashinfer ships the griddepcontrol fix.

  3. Eager bias fast path (diffusion_nvfp4_bias.py). mm_fp4 has no bias argument. The fused CUTLASS epilogue from the investigation is dropped: not bit-identical to the unfused path (max abs 2 to 4), sm_100a only, a 104 s nvcc build at runtime, and inductor already fuses the bias into the compiled path where a custom op would block that fusion. What ships is a Triton in-place add on the eager path only, bit-identical to Tensor.add_ on seven verified shapes, declined under is_compiling(), on non-contiguous input, on a dtype mismatch, without Triton, below 12 M output elements and above 2**31. The floor is measured: the kernel's launch is 20 to 28 us against 5 to 10 us for add_, so it runs 0.26x to 0.37x below 4 M elements and 0.84x at 1024x10240, then 1.84x at 4096x3840 and 3.4x to 3.8x from 4096x10240 up; flux and qwen-image route only M = 1 GEMMs to nvfp4, where it was pure launch cost. UNSLOTH_NVFP4_FAST_BIAS=0 disables it.

  4. Cached flashinfer dispatch state (diffusion_nvfp4_dispatch.py). flashinfer's public mm_fp4 rebuilds its plan and re-transposes the weight on every call: 58 us of host per GEMM. All private-internal touching lives in one file behind an exact version allowlist ("0.6.6") plus a one-shot runtime bit-identity self-check (verify(), run inside the preflight, off the request path). gemm_plan returns None when capturing with a cold key so no profiling launch is baked into a graph; the transpose cache is keyed on (data_ptr, shape), weights only, bounded at 4096, reset on unload. UNSLOTH_NVFP4_FAST_DISPATCH=auto|0|1 (1 skips only the version check, never verify). Any other flashinfer release silently uses the public path.

Measurements

B200, torch 2.12.1+cu130, flashinfer 0.6.6. The PR 2 arm is the PR 2 worktree, not an env setting: the barrier has no switch back to the per-call allocation, so arms are whole trees and a "knobs off" arm on this tree isolates the barrier.

Per-kernel (commits 2 to 4): persistent barrier saves 1.7 us host per GEMM; cached dispatch 58 to 19 us host per GEMM, torch.equal on every checked shape; Triton bias 1.6x to 3.7x over add_, bit-identical.

End to end, z-image, 9 steps, 3 warm-ups + 20 timed renders, median of 3 processes per arm:

arm graphs 512 1024
fp8 on 0.262 s 0.933 s
nvfp4 PR 2 tree on 0.265 s 0.936 s
nvfp4 this PR, knobs off on 0.261 s 0.931 s
nvfp4 this PR, default on 0.261 s 0.926 s
fp8 off, compiled 0.286 s 0.939 s
nvfp4 PR 2 tree off 0.384 s 0.937 s
nvfp4 this PR, knobs off off 0.393 s 0.935 s
nvfp4 this PR, default off 0.340 s 0.945 s

This PR over the PR 2 tree: graphed 1.016x and 1.011x, un-graphed 1.128x at 512 and 0.992x at 1024. The un-graphed 512 gain is entirely the cached dispatch (the barrier arm shows no end-to-end effect and the compiled path declines the Triton bias). The 1.45x to 1.55x the investigation prototype measured does not reproduce and should not: that prototype had every admitted linear (239) on the flashinfer path, the shipped z-image policy puts 34 there. Wan2.2-TI2V-5B at 1280x704x121, 50 steps, 304 flashinfer linears: 48.15 s to 47.72 s (1.009x), the same with graphs requested or forced off since Studio does not capture a graph for that family.

Bit identity against the PR 2 tree: with deterministic algorithms pinned, every z-image pair is exactly 0 in all four cells. As shipped, the un-graphed compiled path is not reproducible against itself (two runs of the same tree and seed differ by up to 136 levels on 72 percent of pixels when one compiled cold and the other loaded a compile-cache bundle), every cross-tree pair lands inside that spread and several are exactly 0; Wan pairs differ by at most 2 levels on under 0.5 percent of pixels, within the same-arm range. No knob in this PR breaks identity. The compile-cache drift is pre-existing and independent of NVFP4, tracked separately.

Capture inertness: barrier and dispatch workspace pointers unchanged across a GraphedForward capture and 20 replays, one barrier per device, no plan built during a cold capture, replays bit-identical to eager. verify() costs 0.9 ms cold inside a 110 to 130 ms preflight inside a 20 to 35 s load.

Tests

test_diffusion_nvfp4_speed.py, test_diffusion_nvfp4_dispatch.py: barrier allocated once per device, not cached during capture, zero_ precedes mm_fp4 in the stub call log, ZERO_BUFFER env; every flashinfer call sees the tensor's device with tensors on device 1 and device 0 current; no set_stream in the subtree; dispatch refuses an unknown version, a missing symbol, env off; no plan during capture; transpose cache bounded; fused bias falls back on each condition; reset() clears state. CUDA-gated: barrier bit identity over 50 iterations, fast vs public dispatch torch.equal at five shapes, fused bias identity on seven shapes, multi-GPU guard, capture and replay identity with a stable barrier pointer.

Full studio/backend/tests on this branch (test_blender_managed.py excluded), 828 files in 10 batches: 41593 passed, 124 failed, 262 skipped, 15 errors. Every failing id reproduces on the merge base (95feb6979) in the same 19 files none of which this PR touches (see the image PR for the list). hub/tests: 683 passed.

Not measured

FLUX.1 and Qwen-Image end to end (the eager path where the Triton bias is live is not a shipped configuration for either); other flashinfer releases (the allowlist makes them the public path by construction); the 1.6 percent graphed z-image gap, three runs per arm do not resolve it and no claim is made for it.

UI evidence

No before/after pair is attached, because nothing a user sees changes, and a pair of identical screens is not evidence of that. What was checked against this PR's own base (studio-nvfp4-image):

  • The diff under studio/frontend is empty, studio/backend/main.py and studio/backend/models/ are untouched, and no response payload gains or loses a field.
  • The three new reset_nvfp4_state() calls are teardown on paths that already tore down. The rest is kernel internals behind torch.ops.unsloth_nvfp4, plus four environment switches listed in diffusion_speed.py's module docstring, all unset by default.
  • The one field this stack renders, transformer_quant_backend on /api/inference/images/status from Studio: per-layer NVFP4 image policies, flashinfer FP4 backend and a gated auto row #10730, cannot move here. nvfp4_preflight sets ok from the GEMM probe BEFORE the new fast-dispatch check runs, and that check sits inside the same try whose except rewrites only reason. A dispatch.verify that fails or raises changes which dispatch path runs, never which backend is reported, so the loaded-models row reads the same on both sides by construction.

The claim this PR does make is speed at bit identity, and that is the table above: a picture cannot show that two renders are equal to the last level.

NVFP4 kill switch

NVFP4 for image and video generation is off by default until the hosted unsloth/*-NVFP4 repos are public. UNSLOTH_NVFP4_DIFFUSION=1 (also true, yes, on) restores the full NVFP4 behaviour of this stack, including private-repo access through the user's HF token; anything else, or unset, disables it. The default lives in one constant, NVFP4_DIFFUSION_DEFAULT in studio/backend/core/inference/diffusion_nvfp4_flag.py, so turning it on for everyone later is a one-line change. The switch is read at call time through nvfp4_diffusion_enabled().

This PR adds no new entry point. The fast dispatch is only reached through nvfp4_preflight and the NVFP4 layers, both behind the switch. A test here checks that neither the dispatch probe nor its verify pass runs with the switch off. The gates themselves are on #10729 and #10730.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-25T15:03:16.277949Z 921b270 Manual request
🔒 Security Review ✅ Completed 2026-09-10T14:35:56.440012Z ade62d2 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7e87ca6da5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +6511 to +6512
from .diffusion_nvfp4_linear import reset_nvfp4_state
reset_nvfp4_state()

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Do not reset another backend's live CUDA-graph state

This unconditional process-global reset is unsafe when the sibling image backend still has an NVFP4 CUDA graph resident—for example, a GPU image model can coexist with a CPU video pipeline because the GPU arbiter only excludes competing GPU owners. Unloading that CPU video state clears the shared barrier tensor and then calls clear_gpu_cache(), while the image graph still contains a recorded zero_ kernel targeting the barrier's address; a later replay can therefore access freed or reused memory. Scope these caches to the owning model/backend, or reset them only after every graph that references them has been uninstalled.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Not reachable. Ownership in the GPU arbiter is exclusive and a video load evicts the image backend; both media routes gate on the host-wide device target, so on any host where a flashinfer NVFP4 graph can exist the video target is never cpu, and a cpu target means no image CUDA graph exists. Within each backend uninstall_all runs before reset_barriers, and the video reset sits inside the committed-state branch.

danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Sep 10, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: ade62d2045

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
chatgpt-codex-connector[bot]

This comment was marked as resolved.

@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 10, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Sep 10, 2026
@danielhanchen

Copy link
Copy Markdown
Member Author

Checked the device guard coverage in studio/backend/core/inference/diffusion_nvfp4_ops.py and the persistent barrier and cached dispatch paths, and the bit-identity numbers line up with the switches described. Will review once #10730 lands and this retargets to main.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep it up!

Reviewed commit: dece8c1a37

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Nice work!

Reviewed commit: 5ab4436132

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: 006cc7173b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 25, 2026
@unslothai unslothai deleted a comment from chatgpt-codex-connector Bot Sep 25, 2026
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

1 similar comment
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Keep them coming!

Reviewed commit: ba858d6955

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

danielhanchen added a commit to danielhanchen/unsloth-staging-2 that referenced this pull request Sep 25, 2026
@danielhanchen danielhanchen self-assigned this Sep 25, 2026
@shimmyshimmer

Copy link
Copy Markdown
Member

@codex review

1 similar comment
@shimmyshimmer

Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 921b270691

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

This branch has not been deployed

No deployments
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.

2 participants