feat(moe): sync SM12x W4A16 fused MoE family to b12x HEAD - #4255
Conversation
Update the W4A16 (NVFP4 weights, bf16 activations) fused-MoE kernel family under flashinfer/fused_moe/cute_dsl/blackwell_sm12x/ from upstream sparkinfer cc9b476 (10 weeks of changes since the last sync): - cooperative persistent grid launches for the fused FC1/FC2 kernel - TC-decode: small-M packed decode with the top-k sum folded into the FC2 store epilogue - SMEM/occupancy-aware tile selection and launch-bounds planning - native e8m0 (MXFP4 K/32) source-format support incl. logical scale tails for non-128-aligned shards - shape-stable route packing (power-of-2 numel capacity, split count/prefix/ sort triton kernels, capture-safe fallback); W4A16 workspaces are now sized to that capacity - torch.library custom ops (flashinfer::w4a16_*) wrapping the DSL launches for torch.compile/functionalization opacity - expert_map-aware validation and route packing (groundwork for expert parallelism) New FlashInfer-local support modules: moe_w4a16_activations.py (activation metadata, narrowed to the supported silu/relu2 set) and moe_w4a16_compiler.py (in-memory compile cache providing upstream's KernelCompileSpec/compile call surface over cute.compile). Deliberately not ported: NF3 3-bit weights (rejected at the API boundary; inert const_expr branches kept so kernel code stays byte-comparable with upstream), the NVFP4+NF3 hybrid two-tier entry points, SiTU/swiglu-oai activations, and the native-ModelOpt small-M direct micro kernel (FlashInfer serves packed weights, which take the TC-decode path). Weight-prepare round-trip verified bit-identical against the previous implementation for modelopt and compressed_tensors sources (relu2 + silu, including the w13 half-swap now folded into the repack as a row rotation). AI-assisted (Claude Code). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4L2GtYW38ZZDUJzehznz2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4L2GtYW38ZZDUJzehznz2
Key the compile cache on structural compile options instead of their address-based repr, and clear it from clear_w4a16_kernel_cache. Let route packing degrade to the exact-numel capacity when any caller-provided buffer was sized for it, not only when all of them were. Drop the native-ModelOpt and native-e8m0 prepare entry points, whose small-batch consumer is not part of this port. Fix error messages that advertised removed or renamed formats, and comment the freeze-guard stub. Port the upstream route-packing tests and pin the workspace/route-pack capacity contract. AI-assisted (Claude Code). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4L2GtYW38ZZDUJzehznz2
The route-packed GEMM overtakes TC-decode before upstream's m=8 ceiling on the SM12x cards FlashInfer targets, so select TC-decode only up to the direct-topk route bound (m=6). AI-assisted (Claude Code). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4L2GtYW38ZZDUJzehznz2
The TC-decode crossover against the route-packed GEMM varies by SM12x card and sits as low as m=5 on consumer SM120. m=4 is the largest cap that wins or ties on every card measured. AI-assisted (Claude Code). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4L2GtYW38ZZDUJzehznz2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4L2GtYW38ZZDUJzehznz2
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L4L2GtYW38ZZDUJzehznz2
📝 WalkthroughWalkthroughSM12x W4A16 gains activation normalization, compilation caching, FP4/NF3 support, additional weight formats, expanded kernel execution paths, and capacity-aware route packing with CUDA-gated coverage. ChangesSM12x W4A16 implementation
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related issues
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
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. Comment |
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
Below O3 the compiler drops the L2 prefetch hints on the fused kernel's weight stream, slowing the large-batch mainloop. AI-assisted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
|
/bot run tests/moe |
There was a problem hiding this comment.
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)
flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_kernel.py (1)
6640-6659: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winPropagate the stream into route packing or forbid non-default streams.
pack_topk_routes_by_expertacceptsstream, but forwards the pack into Triton kernels that do not take any stream argument. Passing a different current stream (the API is also invoked fromrun_w4a16_moewith the launch stream) won’t order the route pack relative to the fused MoE launches. Thread the stream through the packing implementation, or explicitly reject non-default streams instead of dropping the argument.🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_kernel.py` around lines 6640 - 6659, Fix pack_topk_routes_by_expert so its stream argument is not silently discarded: thread stream through _pack_topk_routes_by_expert and the underlying Triton launches, ensuring route packing is ordered on the requested stream, or validate and reject non-default streams. Remove the unconditional del stream while preserving existing validation and output behavior.
🧹 Nitpick comments (5)
flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_host.py (1)
163-171: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
route_pack_token_capacitytakes a requiredtopkit immediately discards.It's now public (
__all__), so the signature is a contract. Either give it a default (topk: int = 1) or drop the parameter unless a future topk-dependent capacity is planned.🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_host.py` around lines 163 - 171, Update the public route_pack_token_capacity signature to make topk optional with a default of 1, preserving the current capacity calculation and compatibility with existing callers.flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_route_pack.py (2)
307-334: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCollapse the redundant re-assignments.
max_packed_routes/max_route_blocksare assigned from the capacity values and then immediately re-clamped; fold the clamp into a single assignment.♻️ Proposed cleanup
- max_packed_routes = capacity_packed_routes - max_route_blocks = capacity_route_blocks - max_packed_routes = max(max_packed_routes, 1) - max_route_blocks = max(max_route_blocks, 1) + max_packed_routes = max(capacity_packed_routes, 1) + max_route_blocks = max(capacity_route_blocks, 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 `@flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_route_pack.py` around lines 307 - 334, In the capacity setup, simplify the assignments to max_packed_routes and max_route_blocks by applying the minimum-value clamp directly when assigning from capacity_packed_routes and capacity_route_blocks. Remove the redundant intermediate assignments while preserving the existing minimum of 1 behavior.
375-475: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueConsider reusing a workspace buffer for
expert_counts.The eager large-shape path allocates a fresh
torch.zeros(num_experts)per call. It's caching-allocator cheap and the comment justifies why capture never reaches here, but the packing entry point already accepts preallocated workspaces — threading one more slice through would keep the prefill path allocation-free.🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_route_pack.py` around lines 375 - 475, Reuse a caller-provided workspace for expert_counts in the eager large-shape path instead of allocating torch.zeros inside the packing flow. Thread an appropriately sized expert-count slice through the packing entry point and use it in _w4a16_route_count_kernel, preserving the existing captured-path fallback and count initialization semantics.flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_prepare.py (1)
95-146: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDeduplicate the two padding branches in
_permute_packed_scales.The
group_size < size_kand single-scale branches differ only in the permutation vector; the padding/validation/trim logic is copied verbatim.♻️ Suggested restructure
def _permute_packed_scales( scales: torch.Tensor, *, size_k: int, size_n: int, group_size: int, output_size_n: int | None = None, ) -> torch.Tensor: scale_perm, scale_perm_single = _scale_perms() - if group_size < size_k and group_size != -1: - block = len(scale_perm) - ... - else: - block = len(scale_perm_single) - ... + perm = ( + scale_perm if (group_size < size_k and group_size != -1) else scale_perm_single + ) + block = len(perm) + if output_size_n is not None or int(size_n) % block != 0: + padded_n = ( + ((int(size_n) + block - 1) // block) * block + if output_size_n is None + else int(output_size_n) + ) + if padded_n < int(size_n) or padded_n % block != 0: + raise ValueError( + f"output_size_n must be a multiple of {block} and >= size_n" + ) + padded = scales.new_zeros((int(scales.shape[0]), padded_n)) + padded[:, : int(size_n)] = scales + rows = int(scales.shape[0]) + scales = padded.reshape((rows, -1, block))[:, :, perm].reshape((rows, padded_n)) + if output_size_n is None: + scales = scales[:, : int(size_n)] + return scales.contiguous() + scales = scales.reshape((-1, block))[:, perm] return scales.reshape((-1, size_n)).contiguous()🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_prepare.py` around lines 95 - 146, Deduplicate the repeated padding, validation, permutation, and trimming logic in _permute_packed_scales by selecting the appropriate permutation vector and block size first, then applying one shared transformation path. Preserve the existing branch condition that chooses scale_perm versus scale_perm_single and all output_size_n validation and return-shape behavior.flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_kernel.py (1)
112-121: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoffAll
nf3_2p1/e4m3_k32code is unreachable — consider dropping or gating it explicitly.
_WEIGHT_LAYOUTSis{"packed", "modelopt"}and_SCALE_FORMATSonly mapse4m3_k16/e8m0_k32, and every entry point validates against them (W4A16GemmKernel.__init__Line 568,W4A16FusedMoeKernel.__init__Line 4134,compile_w4a16_fused_moeLine 5252,run_w4a16_moeLine 6707). Soweight_layout == "nf3_2p1"can never hold, which makes a large amount of added code dead: the NF3 register table (Lines 195-197), B-stage geometry (Lines 744-754),_load_b_scale_registersNF3 arm (Lines 2522-2544),_scaled_dequant_b_fragment_nf3, the NF3 flat-span staging (Lines 3057-3086), NF3 fake buffers (Lines 5573-5584), and the prepared-tile check (Lines 7004-7016). Thenf3_2p1branch at Lines 577-583 is doubly unreachable since it demandsscale_format == "e4m3_k32", which_normalize_scale_formatrejects. Same forself.scale_k32'se4m3_k32term (Line 654) and_scale_group_size(Line 5017).This matches the PR statement that NF3 is not included, but as-is it is untestable dead weight. Either remove it from this PR or add
nf3_2p1/e4m3_k32to the allow-lists behind an explicit feature gate so the paths are reachable and covered.🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_kernel.py` around lines 112 - 121, The NF3 and e4m3_k32 implementation is unreachable because validation excludes both formats. Either remove the related NF3/e4m3_k32 code paths and supporting constants, including branches in W4A16GemmKernel, _scale_group_size, staging, and prepared-tile handling, or explicitly feature-gate and add both formats to the validation allow-lists in W4A16GemmKernel.__init__, W4A16FusedMoeKernel.__init__, compile_w4a16_fused_moe, and run_w4a16_moe so the existing paths become reachable and testable.
🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_compiler.py`:
- Around line 94-104: Update the compilation-cache flow around _COMPILE_CACHE
and clear_compile_cache() to serialize in-flight compilation per cache_key and
track cache generation across clears. Ensure concurrent misses for the same key
share one compilation, and only insert a compiled result when it belongs to the
current generation, so a pre-clear compilation cannot repopulate the cache after
invalidation.
In `@flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.py`:
- Around line 296-299: Discard the unused first return value from
pow2_ceil_ue8m0_torch in this grouped quantization path by binding it with the
project’s underscore convention, while retaining byte for
_ue8m0_output_scale_torch.
In `@tests/moe/test_b12x_w4a16_route_pack.py`:
- Around line 303-325: Add a CUDA/SM12x availability skip marker to
test_w4a16_workspace_route_buffers_cover_route_pack_capacity, matching the
module’s existing guards and using the appropriate flashinfer.utils architecture
check or backend capability marker. Keep the test body unchanged.
---
Outside diff comments:
In `@flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_kernel.py`:
- Around line 6640-6659: Fix pack_topk_routes_by_expert so its stream argument
is not silently discarded: thread stream through _pack_topk_routes_by_expert and
the underlying Triton launches, ensuring route packing is ordered on the
requested stream, or validate and reject non-default streams. Remove the
unconditional del stream while preserving existing validation and output
behavior.
---
Nitpick comments:
In `@flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_host.py`:
- Around line 163-171: Update the public route_pack_token_capacity signature to
make topk optional with a default of 1, preserving the current capacity
calculation and compatibility with existing callers.
In `@flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_kernel.py`:
- Around line 112-121: The NF3 and e4m3_k32 implementation is unreachable
because validation excludes both formats. Either remove the related NF3/e4m3_k32
code paths and supporting constants, including branches in W4A16GemmKernel,
_scale_group_size, staging, and prepared-tile handling, or explicitly
feature-gate and add both formats to the validation allow-lists in
W4A16GemmKernel.__init__, W4A16FusedMoeKernel.__init__, compile_w4a16_fused_moe,
and run_w4a16_moe so the existing paths become reachable and testable.
In `@flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_prepare.py`:
- Around line 95-146: Deduplicate the repeated padding, validation, permutation,
and trimming logic in _permute_packed_scales by selecting the appropriate
permutation vector and block size first, then applying one shared transformation
path. Preserve the existing branch condition that chooses scale_perm versus
scale_perm_single and all output_size_n validation and return-shape behavior.
In `@flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_route_pack.py`:
- Around line 307-334: In the capacity setup, simplify the assignments to
max_packed_routes and max_route_blocks by applying the minimum-value clamp
directly when assigning from capacity_packed_routes and capacity_route_blocks.
Remove the redundant intermediate assignments while preserving the existing
minimum of 1 behavior.
- Around line 375-475: Reuse a caller-provided workspace for expert_counts in
the eager large-shape path instead of allocating torch.zeros inside the packing
flow. Thread an appropriately sized expert-count slice through the packing entry
point and use it in _w4a16_route_count_kernel, preserving the existing
captured-path fallback and count initialization semantics.
🪄 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 Plus
Run ID: 8822f6f9-4333-47d2-be3e-39f99ae6e9b5
📒 Files selected for processing (10)
flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.pyflashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_activations.pyflashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_compiler.pyflashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.pyflashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_host.pyflashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_kernel.pyflashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_prepare.pyflashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_route_pack.pytests/moe/test_b12x_fused_moe.pytests/moe/test_b12x_w4a16_route_pack.py
| with _COMPILE_CACHE_LOCK: | ||
| compiled = _COMPILE_CACHE.get(cache_key) | ||
| if compiled is not None: | ||
| return compiled | ||
|
|
||
| compile_callable = cute.compile | ||
| if dsl_compile_options is not None: | ||
| compile_callable = compile_callable[dsl_compile_options] | ||
| compiled = compile_callable(func, *args, **kwargs) | ||
| with _COMPILE_CACHE_LOCK: | ||
| _COMPILE_CACHE[cache_key] = compiled |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
Make cache lookup, compilation, and invalidation atomic per key.
Line 94 releases the lock before compilation, so concurrent misses compile the same specialization repeatedly. More importantly, clear_compile_cache() can clear the cache while this work is in progress, then Line 103 repopulates it with a pre-clear result. Track in-flight compilations and a cache generation (or equivalent) so one caller compiles per key and pre-clear results cannot be inserted afterward.
🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_compiler.py` around
lines 94 - 104, Update the compilation-cache flow around _COMPILE_CACHE and
clear_compile_cache() to serialize in-flight compilation per cache_key and track
cache generation across clears. Ensure concurrent misses for the same key share
one compilation, and only insert a compiled result when it belongs to the
current generation, so a pre-clear compilation cannot repopulate the cache after
invalidation.
There was a problem hiding this comment.
Compiling the same kernel twice gives the same result, so a concurrent miss only costs a wasted compile. clear_compile_cache() is a test helper. Keeping the simple lock.
There was a problem hiding this comment.
@yichengj0, understood. Given duplicate compilation is deterministic and clear_compile_cache() is test-only, the current simple locking approach is appropriate here. Withdrawing this finding.
🐇
✏️ Learnings added
Learnt from: yichengj0
Repo: flashinfer-ai/flashinfer PR: 4255
File: flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_compiler.py:94-104
Timestamp: 2026-07-31T01:07:30.288Z
Learning: In `flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_compiler.py`, concurrent cache misses for the same SM12x W4A16 CuTe-DSL kernel specialization are intentionally allowed to compile redundantly because compilation is deterministic and the impact is only wasted compile work. `clear_compile_cache()` is a test helper, so cache-generation protection against in-flight pre-clear compilations is not required.
Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.
You are interacting with an AI system.
bkryu
left a comment
There was a problem hiding this comment.
Thanks @yichengj0 left a few comments!
Declare the launch ops' real write sets instead of mutates_args="unknown", reject route packing on a stream other than the current one instead of silently dropping the argument, and add the missing CUDA skip marker on the workspace-capacity test. AI-assisted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
/bot run tests/moe |
|
[FAILED] Pipeline #60398942 — 6/18 executed test jobs passed Compared with nightly #60101730. Unit Tests
✅ Pass · 🟡 Old failure · ❌ New failure · ⏱ Test timeout · Multi-GPU and Multi-Node Tests — 6/6 passed
Failure detailsNew relative to nightly (attribution uncertain)
Could not compare
|
|
/bot run tests/moe |
There was a problem hiding this comment.
🧹 Nitpick comments (4)
flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.py (4)
1747-1759: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAlign
has_side_effectsacross the shared-memory load helpers.
ld_shared_f32_offsetsetshas_side_effects=True, whileld_shared_u16_offset(Line 823) andld_shared_u8_offset(Line 854) setFalsefor the same class of operation. The flag controls whether LLVM may reorder or CSE the load across barriers. Pick one policy for all shared loads, and record the reason in a comment. If the intent is to keep the loads pinned relative tobarriercalls,Falseon the u8/u16 variants is the risky side.🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.py` around lines 1747 - 1759, Align the has_side_effects policy across ld_shared_f32_offset, ld_shared_u16_offset, and ld_shared_u8_offset so all shared-memory load helpers use the same value, preserving barrier ordering; add a concise comment documenting the chosen policy and its relationship to barrier calls.
5586-5589: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRecord why
silu_mul_32usesrcp_approxfor the sigmoid.The device path computes the sigmoid with
cute.arch.rcp_approx, while the Torch referencesilu_mul_quantize_grouped_mxfp8_torchuses the exactF.silu. The two therefore differ by thercp_approxerror. Add a short comment that states the accuracy tradeoff and the precise alternative, so a later reader does not treat the difference as a defect when comparing against the reference.As per coding guidelines, "For performance-critical hot paths, comment the justification for special algorithmic choices and relevant alternatives."
🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.py` around lines 5586 - 5589, Add a concise comment immediately before the sigmoid computation in silu_mul_32 explaining that rcp_approx is used for performance in this hot path, with a small accuracy tradeoff versus the exact sigmoid, and identify F.silu in silu_mul_quantize_grouped_mxfp8_torch as the precise reference alternative.Source: Coding guidelines
2075-2092: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winWrap
val_f32inFloat32and forwardloc/ip.Two deviations from the pattern used by every other helper in this file:
val_f32has no annotation and is not wrapped, so the helper fails if a caller passes a Pythonfloator a non-Float32DSL value. Comparered_add_global_f32at Line 1414.llvm.inline_asmdoes not receiveloc=loc, ip=ip, so the emitted asm loses source attribution.♻️ Proposed fix
-def scatter_add_bf16(addr: Int64, val_f32, *, loc=None, ip=None): +def scatter_add_bf16(addr: Int64, val_f32: Float32, *, loc=None, ip=None): """BF16 atomic reduction add to global memory. Converts the f32 input to bf16 inside PTX and atomically accumulates it into one bf16 output lane. This is intended for opt-in approximate reductions. """ llvm.inline_asm( None, [ Int64(addr).ir_value(loc=loc, ip=ip), - val_f32.ir_value(loc=loc, ip=ip), + Float32(val_f32).ir_value(loc=loc, ip=ip), ], "{ .reg .b16 packed; cvt.rn.satfinite.bf16.f32 packed, $1; red.relaxed.gpu.global.add.noftz.bf16 [$0], packed; }", "l,f", has_side_effects=True, is_align_stack=False, asm_dialect=llvm.AsmDialect.AD_ATT, + loc=loc, + ip=ip, )🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.py` around lines 2075 - 2092, Update scatter_add_bf16 to type and normalize val_f32 as Float32, matching red_add_global_f32 so Python floats and other compatible DSL values are accepted. Also pass loc=loc and ip=ip to llvm.inline_asm while preserving the existing atomic BF16 reduction behavior.
3394-3404: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUnify the bid/tid encoding between the two block-scaled MMA helpers.
This helper embeds
bid_a/tid_a/bid_b/tid_bas literal immediates in the asm template. The siblingmxfp8_mma_m16n8k32_f32_e4m3(Lines 3305-3325) builds fouri16MLIR constants and passes them throughhconstraints for the same operand positions. Both forms are valid, and the literal form here is simpler. Convert the e4m3 variant to the same form so one pattern covers both, or add a comment that explains why the two differ.🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.py` around lines 3394 - 3404, Unify the operand encoding in the e4m3 block-scaled MMA helper around the asm construction near `mxfp8_mma_m16n8k32_f32_e4m3`: either replace the literal `bid_a`/`tid_a`/`bid_b`/`tid_b` immediates with four `i16` MLIR constants passed through `h` constraints like the sibling helper, or document the intentional difference directly beside this template.
🤖 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/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.py`:
- Around line 1747-1759: Align the has_side_effects policy across
ld_shared_f32_offset, ld_shared_u16_offset, and ld_shared_u8_offset so all
shared-memory load helpers use the same value, preserving barrier ordering; add
a concise comment documenting the chosen policy and its relationship to barrier
calls.
- Around line 5586-5589: Add a concise comment immediately before the sigmoid
computation in silu_mul_32 explaining that rcp_approx is used for performance in
this hot path, with a small accuracy tradeoff versus the exact sigmoid, and
identify F.silu in silu_mul_quantize_grouped_mxfp8_torch as the precise
reference alternative.
- Around line 2075-2092: Update scatter_add_bf16 to type and normalize val_f32
as Float32, matching red_add_global_f32 so Python floats and other compatible
DSL values are accepted. Also pass loc=loc and ip=ip to llvm.inline_asm while
preserving the existing atomic BF16 reduction behavior.
- Around line 3394-3404: Unify the operand encoding in the e4m3 block-scaled MMA
helper around the asm construction near `mxfp8_mma_m16n8k32_f32_e4m3`: either
replace the literal `bid_a`/`tid_a`/`bid_b`/`tid_b` immediates with four `i16`
MLIR constants passed through `h` constraints like the sibling helper, or
document the intentional difference directly beside this template.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: d7110405-96db-4e2b-bda1-576a7b578c62
📒 Files selected for processing (3)
flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.pyflashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_w4a16_fp4_helpers.pytests/moe/test_b12x_fused_moe.py
🚧 Files skipped from review as they are similar to previous changes (2)
- tests/moe/test_b12x_fused_moe.py
- flashinfer/fused_moe/cute_dsl/blackwell_sm12x/moe_dispatch.py
## 📌 Description Updates the SM120/SM121 W4A16 (NVFP4 weights, bf16 activations) fused-MoE family to current [b12x](https://github.com/local-inference-lab/sparkinfer) upstream (`cc9b476`). Changes: - Cooperative persistent launches for the fused FC1/FC2 kernel, with occupancy-aware tile selection and launch bounds. - A tensor-core decode path for small batches on the packed serving weights, folding the top-k sum into the FC2 store epilogue and removing a separate launch. - Shape-stable route packing: the triton packing kernels specialize on a power-of-two capacity instead of the exact token count, so decode batch-size changes no longer recompile. The W4A16 workspaces are sized to the same capacity. - A new `fp4_e8m0_k32` (MXFP4 K/32) weight source format, including scale-tail handling for TP shards that are not 128-aligned. Public API and behavior changes: - `b12x_fused_moe`'s `source_format` parameter accepts the new `fp4_e8m0_k32` value. - W4A16 workspaces are now sized to the power-of-two route capacity, slightly larger than before. This is what lets decode batch-size changes reuse one compiled route-packing kernel instead of recompiling. - Kernel launches now dispatch through torch custom ops, registered at module import. This brings the family in line with the library-wide convention it predated. Upstream features with no FlashInfer target checkpoint are not included: - NF3 (3-bit normal-float) weights. Their kernel branches stay: they compile out and keep future sync diffs clean. - The NVFP4+NF3 hybrid entry points. - The native-ModelOpt small-batch micro kernel. - The SiTU and swiglu-oai activations. ## 📊 Performance Speedup over the previous kernels at the MoE shape of a Nemotron variant, with relu2 and silu activations, run under CUDA graphs, median of three runs. Each cell reads RTX 5080 / RTX Pro 6000 Server Edition / GB10: | Activation | m=1 | m=4 | m=16 | m=64 | m=2048 | |---|---|---|---|---|---| | relu2 | 1.07x / 1.15x / 1.11x | 1.03x / 1.07x / 1.04x | 1.00x / 1.00x / 1.02x | 0.99x / 1.00x / 1.03x | 12.91x / 18.52x / 1.00x | | silu | 0.98x / 1.23x / 1.10x | 0.99x / 1.05x / 1.02x | 0.95x / 0.98x / 1.01x | 0.95x / 0.98x / 1.00x | 11.70x / 16.88x / 1.02x | The m=2048 cells fix a stall rather than measure a general kernel win: the fused kernel uses grid-wide barriers but was not launched cooperatively, so at large m CTAs could spin waiting for peers that were never co-scheduled. GB10 kept the whole grid resident and never hit the stall, so its m=2048 cells have no win to inherit. ## 🔍 Related Issues #4223 (item 3). ## 🚀 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 - `tests/moe/test_b12x_fused_moe.py` (142 cases) passes on SM121, both on the default dispatch and with the W4A16 path forced. - Added `tests/moe/test_b12x_w4a16_route_pack.py` (64 cases): route packing against a host reference, covering `expert_map`, invalid expert ids, preallocated buffers, and the workspace capacity contract. - Weight preparation is bit-identical to the previous implementation across the supported source formats and activations. ## Reviewer Notes - Most of the diff is upstream code taken as is. FlashInfer-local changes are limited to the import remaps, the two support modules, the API-boundary checks, the workspace sizing in `moe_dispatch`, and the decode-path cap below. - One tuning deviation from upstream: the tensor-core decode path is selected up to m = 4 instead of upstream's m = 8. The crossover to the route-packed GEMM varies by card, so the cap conservatively stops where the decode path wins or ties on every card measured. - The fused kernel compiles at DSL optimizer level 3 instead of upstream's 2. Level 3 generates a faster mainloop on the DSL version pinned here, at the cost of a longer compile for this kernel. - The kernels import activation metadata and a kernel compile cache from upstream's library. The two new modules, `moe_w4a16_activations.py` and `moe_w4a16_compiler.py`, are small local implementations of those two pieces. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Expanded Blackwell W4A16 fused MoE support for NF3, NVFP4, ModelOpt, CompressedTensors, and FP4 E8M0 formats. * Added configurable activations, SwiGLU parameters, routing, calibration, and launch options. * Improved route packing for large workloads, dynamic capacities, expert remapping, and invalid route IDs. * Added optimized quantization, dequantization, conversion, and matrix-operation support. * Added compile caching to reduce repeated kernel compilation. * **Bug Fixes** * Improved workspace sizing and safe handling of zero-valued scales. * **Tests** * Added coverage for route grouping, padding, capacity fallbacks, large shapes, and expert mapping. <!-- 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> (cherry picked from commit 28ca04e)
📌 Description
Updates the SM120/SM121 W4A16 (NVFP4 weights, bf16 activations) fused-MoE family to current b12x upstream (
cc9b476).Changes:
fp4_e8m0_k32(MXFP4 K/32) weight source format, including scale-tail handling for TP shards that are not 128-aligned.Public API and behavior changes:
b12x_fused_moe'ssource_formatparameter accepts the newfp4_e8m0_k32value.Upstream features with no FlashInfer target checkpoint are not included:
📊 Performance
Speedup over the previous kernels at the MoE shape of a Nemotron variant, with relu2 and silu activations, run under CUDA graphs, median of three runs. Each cell reads RTX 5080 / RTX Pro 6000 Server Edition / GB10:
The m=2048 cells fix a stall rather than measure a general kernel win: the fused kernel uses grid-wide barriers but was not launched cooperatively, so at large m CTAs could spin waiting for peers that were never co-scheduled. GB10 kept the whole grid resident and never hit the stall, so its m=2048 cells have no win to inherit.
🔍 Related Issues
#4223 (item 3).
🚀 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
pre-commitby runningpip install pre-commit(or used your preferred method).pre-commit install.pre-commit run --all-filesand fixed any reported issues.🧪 Tests
tests/moe/test_b12x_fused_moe.py(142 cases) passes on SM121, both on the default dispatch and with the W4A16 path forced.tests/moe/test_b12x_w4a16_route_pack.py(64 cases): route packing against a host reference, coveringexpert_map, invalid expert ids, preallocated buffers, and the workspace capacity contract.Reviewer Notes
moe_dispatch, and the decode-path cap below.moe_w4a16_activations.pyandmoe_w4a16_compiler.py, are small local implementations of those two pieces.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Tests