Skip to content

feat(moe_ep): restructure MegaMoE kernel sources into kernel_src + latest CuTeDSL MegaMoE kernels with improved performance - #3980

Merged
Anerudhan merged 28 commits into
flashinfer-ai:mainfrom
mhoqueanik:new_cutedsl_kernels
Jul 16, 2026
Merged

Anerudhan merged 28 commits into
flashinfer-ai:mainfrom
mhoqueanik:new_cutedsl_kernels

Conversation

@mhoqueanik

@mhoqueanik mhoqueanik commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Follow-up to #3852: brings the CuTeDSL MegaMoE kernels to their tuned state. Restructures kernel sources into a canonical kernel_src/cutedsl_megamoe layout (verbatim src/ drop zone behind an enforced shim/ boundary), drops the updated kernel, and adds new kernel knobs (in_kernel_fc2_reduce, combine_dtype), per-size default profiles, and session-aware collective online autotuning. See TUNING.md for detailed changes.

Perf

Measured 2026-07-15 on one GB200 node (4x GPUs, EP=4), DeepSeek-V3-like geometry (256 experts, top-8, hidden 7168, intermediate 2048), number below checked in-line against an fp32 dense-MoE reference (see Accuracy). Steady-state full-forward p50 _s (e2e_pipelined; bare-kernel timing tracks within a few _s), speedup vs deep_gemm_mega in parens:

tok/rank dg nvfp4 bf16 +ikr +combine_nvfp4 +combine_mxfp8
1024 473.1 428.5 (1.10x) 431.1 (1.10x) 375.8 (1.26x) 386.0 (1.23x)
2048 844.3 625.6 (1.35x) 619.8 (1.36x) 549.9 (1.54x) 582.6 (1.45x)
4096 1490.4 1018.4 (1.46x) 998.7 (1.49x) 901.6 (1.65x) 936.5 (1.59x)
8192 3105.2 1923.5 (1.61x) 1914.9 (1.62x) 1644.0 (1.89x) 1783.3 (1.74x)

[ikr = in_kernel_fc2_reduce; +combine_nvfp4 / +combine_mxfp8 = the fc2 epilogue quantizes each partial IN REGISTERS to fp4 (e2m1 + bf16 scale per 16) or fp8 (e4m3 + e8m0 scale per 32) just for the wire]

combine_dtype="nvfp4" is the throughput winner across this range (1.89x vs dg and 19.9 Mtok/s at 8192); bigger wins expected multi-node where combine bytes dominate. Below ~1024 tok/rank nvfp4 and dg are at parity (e.g. 217.9 vs 211.0 _s at 8 tok/rank _ small batch is weight-load bound and fp4-vs-fp4 is a wash), with the plain bf16 wire the best small-batch
config.


[Known issue] CuTe-DSL runtime sensitivity: nvidia-cutlass-dsl[cu13]==4.5.2 (2026-07-15)

[Fix underway] Same recipe, geometry and runbook as the table above, with the only change being the CuTe-DSL runtime pinned to 4.5.2 instead of >= 4.6.1 (CSVs moe_ep_benchmark/results/sweep_20260715_1634*..1641*_fi_mega.csv):

tok/rank dg nvfp4 bf16 +ikr +combine_nvfp4 +combine_mxfp8
1024 468.8 583.7 (0.80x) 580.9 (0.81x) 564.2 (0.83x) 566.3 (0.83x)
2048 813.1 878.6 (0.93x) 869.2 (0.94x) 834.6 (0.97x) 842.8 (0.96x)
4096 1538.1 1453.1 (1.06x) 1430.2 (1.08x) 1385.5 (1.11x) 1394.7 (1.10x)
8192 3060.7 2579.5 (1.19x) 2529.0 (1.21x) 2473.0 (1.24x) 2492.4 (1.23x)

Takeaways:

  • 4.5.2 compiles and runs this kernel drop (4.5.0 fails outright at cute.compile), and every variant/point completed without error.
  • But the 4.5.2-generated code is 34-54% slower than 4.6.1 across every nvfp4 variant and token count (e.g. bf16 wire 583.7 vs 428.5 µs @1024, 2579.5 vs 1923.5 @8192; +combine_nvfp4 ~+50% everywhere). The dg baseline reproduces within noise (468.8-3060.7 vs 473.1-3105.2), so the delta is the DSL runtime, not the run.
  • The perf shape regresses qualitatively too: the dg crossover moves from ~512-1024 tok/rank out past 2048 (nvfp4 loses at 1024, 0.80-0.83x), and the 8192 win shrinks from 1.61-1.89x to 1.19-1.24x.

Conclusion: treat 4.6.1 as a performance floor, not just a compile-compatibility floor at 4.5.2 is functional but leaves ~1.3-1.5x of nvfp4 kernel performance on the table.


Accuracy

Reported per run by the benchmark's new acc_loss_pct pass: one un-timed forward through the exact timed configuration vs an fp32 dense-MoE ground truth over all 256 experts (global rel-L2, random data _ an upper bound; calibrated real-model weights typically lose less). Constant across token counts:

variant acc loss
mxfp8_cutedsl 6.4%
deep_gemm_mega 20.6%
nvfp4 (bf16 wire) 23.2%
nvfp4 +ikr 23.2%
nvfp4 +combine_mxfp8 23.3%
nvfp4 +combine_nvfp4 25.0%

The fp4 combine wire buys its large-token speedup for ~1.8 pt of extra quantization loss. Every compute path is additionally pinned to a single-GPU pure-torch oracle in tests/moe_ep/ (run_tests.sh oracle); the nvfp4 mega kernel is bit-exact vs the kernel-team CuTeDSL reference on identical operands.

What's included

  • Kernel source restructure: cutedsl_backend_kernels relocated to kernel_src/cutedsl_megamoesrc/ is a verbatim kernel drop zone, backends import only through the shim/ package boundary (enforced by test).
  • New kernel drop + shim port to the new kernel API.
  • New kernel knobs: in_kernel_fc2_reduce and quantized combine wire (combine_dtype = bf16 / nvfp4 / mxfp8).
  • Tuning: measured per-size default profiles (incl. a fix for the NVFP4 mid-range dip), default token-count heuristic, and knobs="auto" session-aware collective online autotune (candidates include the ikr axis).
  • Launch-path overhead fixes: validated-once fast launch + async fused-launch wrappers; robust peer-offset resolution on nvshmem heap reuse.
  • Docs: TUNING.md (tuning surface, measured sweeps, benchmark methodology + pitfalls), ACKNOWLEDGEMENT.md for the kernel authors.
  • Tests: ikr + quantized-combine multirank coverage, large-token (≥2048) tuner-profile coverage.

Directories affected

  • flashinfer/moe_ep/kernel_src/cutedsl_megamoe/ — kernel sources (src/), shim boundary (shim/), docs (new home)
  • flashinfer/moe_ep/backends/mega/ — nvfp4/mxfp8 cutedsl backends: knob plumbing, autotune, launch path (old kernel dir removed)
  • flashinfer/moe_ep/core/runtime/ — bootstrap import path update
  • tests/moe_ep/ — multirank + tuner coverage
  • docs/design_docs/ — moe_ep architecture + runbook updates

Runbook

Summary by CodeRabbit

  • New Features

    • Added a unified CuTeDSL MegaMoE API boundary for NVFP4 and MXFP8.
    • Added one-time "auto" autotuning on first compute, with runtime tuning-knob overrides.
    • Added configurable support for in-kernel FC2 reduction and selectable combine formats (bf16, MXFP8, NVFP4).
  • Documentation

    • Updated architecture/runbook guidance and added detailed CuTeDSL tuning + drop-workflow docs.
  • Breaking Changes

    • Removed legacy CuTeDSL mega-kernel frontend/export entrypoints; use the unified shim API.
  • Tests

    • Expanded multi-rank/mega-layer regression coverage (large tokens, quantized combines, in-kernel FC2 reduce), including repeated-forward checks.

Md Saidul Hoque Anik and others added 18 commits July 14, 2026 22:50
… into canonical kernel_src layout

Moves CuTeDSL kernel sources from the ad-hoc
`backends/mega/kernel/cutedsl_backend_kernels/` tree into the new
`flashinfer/moe_ep/kernel_src/cutedsl_megamoe/` hierarchy so all kernel
drops from the kernel team land in one predictable location. Backend
shims (mxfp8_cutedsl, nvfp4_cutedsl) and bootstrap updated to import
from the new path.

Also drops dead code from the vendored frontend: the unused
`megamoe_frontend/api.py` re-export shim and the deprecated
`MegaMoE{Config,Frontend,Inputs}` aliases (all live callers use the
dtype-specific `MegaMoENvfp4*` / `MegaMoEMxfp8*` names).

Mega tests (nvfp4 + mxfp8 multirank, mxfp8 preprocess vs reference)
verified passing end-to-end on Blackwell via SLURM.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nit__

Collapses the confusing two-layer split (parent nvfp4.py/mxfp8.py wrappers
over megamoe_frontend/api_nvfp4.py/api_mxfp8.py frontends) into a cleaner,
more compact layout that future kernel drops can follow:

  __init__.py    public API for moe_ep; talks only to shim/, re-exports the
                 curated symm-buffer + fused-launch surface (the sole boundary)
  shim/comm.py   dist bootstrap, sym heap, compile state, resolve_gate_up_clamp
                 (was megamoe_frontend/common.py + _util.py)
  shim/nvfp4.py  NVFP4 frontend + symm-buffer/launch wrappers, self-contained
  shim/mxfp8.py  MXFP8 frontend + symm-buffer/launch wrappers, self-contained
  shim/correctness.py  standalone NVFP4 smoke runner

Each dtype is now one self-contained module (frontend + wrapper merged), so
there is no more api_mxfp8.py-vs-mxfp8.py duplication. Path bootstrap moved
into shim/__init__ so the shim is import-safe. The unused ``python -m`` entry
wrappers (__main__.py) were dropped; the smoke runners remain invokable via
``python -m ...cutedsl_megamoe.shim.{nvfp4,mxfp8,correctness}``. SKILL.md
updated for the new layout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…zone

Prepares the cutedsl_megamoe kernel_src for repeatable kernel-team drops by
making shim/ the single adaptation layer, so a new drop is a pure replace of
src/ with the only work confined to shim/.

Shim-boundary isolation (nothing outside shim/ imports src/ packages):
  - New shim/kernel_helpers.py: the single re-export point for raw-kernel
    helpers/constants/reference the FI backend + tests need. Light helpers are
    eager; the cutlass-pulling mega_runner/mega_reference helpers are exposed
    lazily (module __getattr__ here + package-level __getattr__) so importing
    the package stays CPU-safe.
  - backends/mega/kernel/{nvfp4,mxfp8}_cutedsl/{backend,staging,weights}.py now
    import every helper/constant through the package public API instead of
    reaching into common/moe_nvfp4_swapab/moe_mxfp8_glu directly; dropped the
    now-redundant per-file _require_cutedsl_paths() shims.
  - tests/moe_ep cutedsl tests verify only through the package boundary
    (the MXFP8 torch reference is re-exported by kernel_helpers.py); added
    clarifying comments to all three.

Verbatim src/ + bootstrap relocation:
  - Moved path bootstrap out of src/ into shim/_paths.py (bootstrap_paths now
    computes the sibling src/ dir from the shim location) and re-exported it
    from shim/ and the package __init__. src/ no longer contains any injected
    files (deleted src/__init__.py and src/_bootstrap_paths.py) and is now an
    exact copy of the kernel-team drop's four packages.
  - core/runtime/bootstrap.py reaches bootstrap_paths through the package
    boundary rather than importing src._bootstrap_paths.

Docs/SKILL.md updated for the new shim layout, the verbatim-src principle, and
the kernel_helpers src-symbol audit table used when resyncing a drop.

Verified on Blackwell (4-GPU srun): mega multirank (nvfp4 + mxfp8, staged and
prestaged) and mxfp8 preprocess-vs-reference all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…kernel API

Replaces the vendored src/ with the latest kernel-team drop (verbatim: the four
packages common/, moe_nvfp4_swapab/, moe_mxfp8_glu/, src/ copied as-is, no
injected files) and adapts the shim to the kernel's substantially changed
construct + launch + output model. All adaptation is confined to shim/; src/
is an exact copy of the drop.

Kernel API migration (mirrors the training driver
moe_ep_training/megamoe/forward_nvfp4.py / forward.py):
  - Constructor: NVFP4 dropped local_rank (derived from the peer mapper now) and
    takes a CombineFormat object instead of a combine_dtype string; MXFP8 keeps
    local_rank but renames token_back_by_dispatch(bool) -> token_back_mode(str).
  - Launch/output: the 4 combine tensors (form-A combine_output + reduced +
    quantized combine staging) collapse to a single 2D (T, hidden)
    output_activation -- the kernel reduces the top-k combine internally. NVFP4
    passes local/shared_workspace as raw uint8 gmem pointers (make_ptr); MXFP8
    keeps cute-tensor workspaces and adds fc1_c=None. Launch the full padded
    buffer and slice [:n] (matches the reference driver).
  - _stack_byte_reinterpretable_tensors moved to runner_common (light) -> made
    an eager kernel_helpers re-export. Removed the obsolete standalone
    topk-reduce path (the free fns became the TopkReduce class; unused by
    moe_ep) and the dead _CompiledTopk scaffolding.

tuner.py (new): exposes the kernel tuning knobs -- correctness knobs
(in_kernel_fc2_reduce, token_back_mode, non_ubulk_fc2_store, load_balance_mode,
mma_tiler_mnk, cluster_shape_mnk) and perf knobs (group_hint, flag_batch,
epi_flag_batch), mirroring tester/solvers/inference_solver. Provides is_valid()
(mirrors filter_invalid), iter_candidates(), and a dtype-aware with_knobs();
get_symm_buffer_for_*(knobs=...) applies them before compile.

correctness.py smoke runner stays stubbed (NotImplementedError) pending a port
to the drop's MegaMoETester. Preprocess test updated: the form-A per-topk output
is gone, and the reference now uses apply_topk_in_fc1=True + plain-sum (the
kernel folds the topk weight pre-quant, so post-hoc weighting no longer matches).
SKILL.md + architecture doc updated for the new layout and the construct/launch
drop-audit surface.

Verified on Blackwell (4-GPU srun): mega multirank (nvfp4 + mxfp8, staged and
prestaged), deep_gemm control, and mxfp8 preprocess-vs-reference all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…el call

Wires a compile-time token-count heuristic into get_symm_buffer_for_{mega,mxfp8}
_moe as the default tactic (an explicit knobs= dict still overrides it). Keyed on
the buffer's num_max_tokens (the kernel compiles once for that size):

  num_tokens < 2048  -> mma_tiler (256,128,256), flag_batch 4, token_back epi_warps
  num_tokens >= 2048 -> mma_tiler (256,256,256), flag_batch 8, token_back
                        reuse_dispatch_warps

both with cluster (2,1,1), group_hint 512, epi_flag_batch (2,4), load_balance
atomic_counter.  in_kernel_fc2_reduce and combine_format stay owned by the config
/ caller (not set by the heuristic).

MXFP8's mma_tiler is kernel-fixed at (256,256) -- the NVFP4-tuned (256,128,256)
is rejected by the kernel ctor -- so tuner.default_knobs(..., include_tile=False)
drops the tile for MXFP8, which keeps its validated (256,256,128) and takes only
the non-tile perf knobs (schedule / token-back / load balance).

Verified on Blackwell (max_tokens=64 -> small profile): mega multirank (nvfp4 +
mxfp8, staged + prestaged), deep_gemm control, and mxfp8 preprocess-vs-reference
all pass. NOTE: the large profile (>=2048 tokens) and MXFP8 large-token
reuse_dispatch_warps are not covered by the 64-token suite -- verify against a
large-token workload before relying on them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rofile

The 64-token mega suite only exercised the tuner's SMALL profile. Add
num_tokens=max_tokens=2048 variants of the nvfp4 and mxfp8 mega-layer tests so
the LARGE profile is actually run:

  - nvfp4: throughput tile mma_tiler (256,256,256) + token_back reuse_dispatch_warps
  - mxfp8: kernel-fixed tile (256,256) + flag_batch 8 + token_back_by_dispatch=True

The mxfp8 case is the regression guard for large-token dispatch-warp token-back
(mxfp8 has no non_ubulk_fc2_store knob); it compiles + runs + stays bit-exact
with the direct-kernel reference, so the large profile's reuse_dispatch_warps is
confirmed valid for MXFP8 -- no need to pin it back to epi_warps.

Parametrized _mega_problem / _run_mega_layer with num_tokens/max_tokens
(defaults unchanged at 64, so the existing small-profile tests are untouched).

Verified on Blackwell (4-GPU srun): all mega sections pass, including the two
new large-token tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…overhead fixes

Closes the measured gap between the FI moe_ep cutedsl backends and the
kernel-repo tester, in three parts:

Knob plumbing + online autotuning:
- Nvfp4/Mxfp8CutedslMegaMoeConfig gain `knobs`: an explicit tuner knob dict
  (e.g. a kernel-repo tester sweep winner) overrides the token-count
  heuristic; "auto" runs a COLLECTIVE online autotune at the first forward
  (new shim/autotune.py): every rank compiles+times a curated candidate set
  in lockstep (barriers around compile and timing), per-candidate medians
  are all-reduced MAX (slowest rank = collective latency), and the argmin
  winner is applied identically on every rank. Candidates mirror the tester
  sweep restriction minus in_kernel_fc2_reduce (changes output placement /
  determinism; stays config-owned). Frontends gain apply_knobs().
- tuner.default_knobs(dtype="mxfp8"): MXFP8 gets its own measured profile
  (flag_batch=4 + epi_warps at all sizes; the NVFP4-large fb8+dispatch-warp
  schedule measured ~5% slower for MXFP8 at 2048 tokens on 4x GB200). The
  mxfp8 large-token test pins the old dispatch-warp combo via knobs= so
  that regression coverage survives the heuristic change.

Launch-path overhead (was misread as kernel slowness):
- frontend.run() rebuilt all 12 cute tensor views (from_dlpack) plus a
  SymBufferHost on EVERY launch; now cached on _CompiledMega keyed by input
  data_ptrs + shape + stream (dropped automatically on recompile).
- make_launch_thunk() + {nvfp4,mxfp8}_mega_launch_thunk(): prebuilt bare
  launch closures matching the kernel tester's perf_run timed region, for
  benchmark loops and tuners.
- run() defaults reset_counters=False: workspaces are allocated zeroed and
  the kernel tail-cleans its counters/flags after every launch (the
  kernel-team drivers/tester never host-reset). The multirank layer tests
  now run a REPEATED forward on the same session and assert both outputs
  bit-exact against the direct-kernel reference (tail-cleanup contract).

Measured (4x GB200, 7168/2048/256/top-8, p50 us, kernel-parity | e2e):
  @8 tok    dg 215|243   nvfp4 132|269 (e2e was 465)   mxfp8 381|519 (was 691)
  @2048 tok dg 844|872   nvfp4 527|671 (e2e was 1121)  mxfp8 1214|1325 (was 1658)
nvfp4_cutedsl is ~1.6x FASTER than deep_gemm at the kernel level at both
batch sizes and beats it e2e at 2048 tokens; the earlier 2x-slower reading
was launch-path overhead plus a geometry mismatch vs the tester problems
(nvfp4_perf.jsonl is top-6 with different inter/experts).

Verified on Blackwell (4-GPU srun): cutedsl multirank suite (nvfp4 + mxfp8 +
preprocess-vs-reference, incl. the new repeated-forward guards) 14 passed;
autotune (12 nvfp4 + 4 mxfp8 candidates x 2 token sizes) compiles and
selects winners collectively with zero failed candidates.

AI-assisted (Claude Code).

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

Trims the remaining per-forward host overhead in the cutedsl e2e path:

- frontend.run() fast path: when the launch cache hits (same session
  buffers, same resolved token count, same stream), the call goes from a
  cheap pointer-tuple key compare straight to the kernel launch -- input
  validation, _prepare_launch_inputs, and the compile-key rebuild run only
  on a cache miss.  Safe because any config change (apply_knobs /
  set_gate_up_clamp) nulls self._mega, so a live entry always matches the
  compiled config, and the cached entry was fully validated when built.
  The key uses the RAW input pointers + resolved token count (slices start
  at row 0, so sliced views keep the same data_ptrs).
- nvfp4_mega_moe / mxfp8_mega_moe gain sync (default False): the kernel and
  the y output copy are enqueued on the current stream and the call returns
  without a host sync, matching deep_gemm and normal stream semantics.
  sync=True blocks until y is host-readable.  The online autotuner passes
  sync=True explicitly (it times launches with perf_counter); the multirank
  tests already synchronize after the wrapper (audited).

Measured (4x GB200, 7168/2048/256/top-8, p50 us, e2e barrier-cold):
  @8 tok    nvfp4 269 -> 221 (kernel floor 132; dg e2e 244)
            mxfp8 519 -> 473
  @2048 tok nvfp4 671 -> 629 (kernel floor 534; dg e2e 866)
            mxfp8 1325 -> 1289
nvfp4_cutedsl now beats deep_gemm end-to-end at BOTH batch sizes; its
residual e2e-vs-kernel delta (~90 us) is the output copy + the benchmark's
barrier-cold per-iter launch, not per-call Python.

Verified on Blackwell (4-GPU srun): cutedsl multirank suite (incl. the
repeated-forward bit-exact guards) 14 passed on every rank.

AI-assisted (Claude Code).

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

The full token sweep (1..8192, e2e_pipelined) showed nvfp4 beating deep_gemm
1.3-1.7x everywhere EXCEPT a dip at 512 (1.20x) and 1024 (1.00x) -- right
below the SMALL->LARGE threshold at 2048.  Online autotune at those points
found the cause is token_back_mode alone: every dispatch-warp candidate beat
every epi_warps candidate (epi_warps +18% at 512, +35% at 1024); tile and
flag_batch are second-order.

default_knobs now has four NVFP4 profiles (SMALL and LARGE unchanged):
  <512       SMALL      (256,128,256) fb4 epi_warps            [winner @8]
  512..1023  MID        (256,128,256) fb4 reuse_dispatch_warps [winner @512]
  1024..2047 MID-LARGE  (256,256,256) fb4 standalone_warps     [winner @1024]
  >=2048     LARGE      (256,256,256) fb8 reuse_dispatch_warps [validated @2048]

Measured with the new defaults (4x GB200, 7168/2048/256/top-8, p50 us,
e2e_pipelined): 512: 287.7 -> 234.5 (1.48x vs dg); 1024: 474.1 -> 332.3
(1.43x vs dg); 2048 unchanged (537.6).  The nvfp4 advantage over deep_gemm
is now uniform 1.3-1.7x across the whole curve.

New parametrized multirank test covers both mid profiles (512 + 1024)
bit-exact against the direct-kernel reference; full cutedsl suite passed
16/16 on every rank.

AI-assisted (Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…h methodology

Companion doc to SKILL.md capturing what the 2026-07-14 tuning round changed
and learned: the knob system (per-size profiles + provenance, online
collective autotune, kernel-repo tester handoff), the launch-path work that
brought the FI forward within 2-13us of the bare kernel (kwargs cache,
no-reset tail-cleanup contract, async wrappers, launch thunks), and the
benchmarking rules that prevent the 2x-slower misread from recurring (match
geometry AND timed region; MEGA_TIMING kernel/e2e_pipelined/e2e semantics;
from-idle collective start skew).  Headline: nvfp4_cutedsl 1.3-1.7x faster
than deep_gemm at every token count 1..8192, uniform after the mid-range
profile fix.

AI-assisted (Claude Code).

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

nvshmem4py's get_peer_tensor resolves the Buffer from a tracker keyed by
raw address and re-views it with the tensor's exact shape; when the
symmetric heap reuses an address for a SMALLER allocation (first hit when
in_kernel_fc2_reduce shrinks shared_workspace from ~1 MiB to ~8 KiB), a
stale larger peer entry survives and the view throws. _compute_peer_offsets
only needs peer BASE ADDRESSES (the nvshmem_ptr mapping is deterministic),
so read them off the peer Buffer without the shaped view. Also skip the
self-peer lookup entirely: nvshmem4py resolves it to the parent tracker
entry and bumps its refcount, deferring the real nvshmem free from
free_tensor() to GC (the 'memory was not freed explicitly' finalize
warnings).

AI-assisted (Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mport)

Import both idea families from the TRT-LLM MegaMoE integration
(PR #16190; plan in moe_ep/todo_trtllm_import.md):

- in_kernel_fc2_reduce: in-flight top-k combine via cross-rank REDG
  atomic-add. get_symm_buffer_for_mega_moe exposes the param;
  output_activation is now ALWAYS sym-heap allocated (ikr hard-requires
  it, explicit-reduce is indifferent, and the knob can then flip
  per-compile without reallocation); frontend.run() and the launch thunks
  enqueue the accumulate-from-zero output zero_() before every launch.
  Fixes the MXFP8 twin's latent bug: it accepted the param but allocated
  the REDG target rank-locally.
- combine_dtype: quantized cross-rank combine wire ('mxfp8' =
  32e4m3xe8m0, 2x less combine traffic; 'nvfp4' = 16e2m1xbf16, 4x less)
  on the NVFP4 backend. Default knobs auto-adjust to dispatch-warp
  token-back (the only wired mode for quantized wires); tuner.is_valid
  gains the matching rule. Both fields surface on
  Nvfp4CutedslMegaMoeConfig.

Measured (4x GB200, 7168/2048/256/top-8): combine_dtype='nvfp4' is the
large-token winner (-16% @2048, -19% @8192 vs bf16 wire; 2.08x vs
deep_gemm); ikr's win is the multi-GB combine-staging removal, not
latency at this geometry. Details in cutedsl_megamoe/TUNING.md.

AI-assisted (Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nvfp4_candidates gains the in_kernel_fc2_reduce axis (24 candidates for
the bf16 combine wire) plus combine_format / allow_in_kernel_fc2_reduce
pruning; autotune_nvfp4_mega_moe derives the default list from the live
session config (combine wire, apply_topk_in_fc1). The always-sym output
(previous commit) is what makes the ikr knob flippable per-compile.
mxfp8_candidates prunes ikr-incompatible dispatch-warp token-back combos
instead of letting them fail at compile. An ikr winner makes the output
accumulation order nondeterministic; callers needing bit-reproducibility
pin in_kernel_fc2_reduce=False via explicit knobs.

AI-assisted (Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
New 4-rank variants (both suites pass 15/6 on 4x GB200):
- in_kernel_fc2_reduce (nvfp4 + mxfp8): tolerance verdict vs the
  explicit-reduce reference via _assert_ikr_close -- a row-scaled bf16
  K-term accumulation band (K x 2^-8 x safety 8 x row max). A flat
  atol/rtol misfires here: the REDG path accumulates the K per-topk terms
  in bf16, so where large terms nearly cancel the achievable agreement is
  bounded by the ULP of the largest TERM, not the final value (the kernel
  repo validates ikr with a K!-ordering bitwise check for the same
  reason; measured band need <= 0.67x, a missing per-launch output zero
  overshoots ~64x so the repeated-forward regression guard stays sharp).
- combine_dtype nvfp4/mxfp8: bit-exact vs a same-wire direct-shim
  reference (deterministic path) + rel-L2 sanity band vs the exact bf16
  wire.
- CPU-safe config/tuner/candidate validation tests.
Also fixes the mxfp8 large-token test: pinned knobs= now reaches the
layer config and the reference (it previously only reached the throwaway
runtime-requirements kernel, so the pinned profile was never exercised).

AI-assisted (Claude Code).

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

- TRT-LLM-import knobs section: what ikr / combine_dtype mean (combine
  leg only; output always bf16; exact-vs-lossy terms x fp32-vs-bf16 sum),
  the measured 4-variant sweep table with speedups vs deep_gemm
  (combine_nvfp4 up to 2.08x @8192), methodology + exact environment
  versions, and a reproduce runbook pointing at
  https://github.com/mhoqueanik/moe_ep_benchmark (MEGA_IKR /
  MEGA_COMBINE_DTYPE land in bench commit a15fb01).
- Runtime knob-resolution flow + mermaid sequence diagrams (supersedes
  the unmerged docs/cutedsl-tuning-flow commit 32175498, updated for the
  ikr/combine changes: knobs bind once per session keyed on
  num_max_tokens, single-slot compile cache, no persistence; setup +
  steady-state forward and the knobs='auto' collective sweep).
- Stale-info cleanup: headline speedup range, ikr expectation replaced
  with the measured verdict, e2e_pipelined staging-exclusion caveat.

AI-assisted (Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Credit the NVIDIA CuTeDSL MegaMoE kernel team for the vendored src/
kernels, validation harness, and tuning groundwork the moe_ep
integration builds on.

AI-assisted (Claude Code).

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

Mermaid sequence diagrams broke on GitHub: a ';' inside a Note is a
mermaid statement separator (parse error at 'output_activation always
sym-heap'), and a bare '<' in note text can open a stray HTML tag.
Replace both; also drop quotes from an else-label for parser safety.
Rewrite the abrupt opening paragraph into a proper overview (what the
doc covers, shared measurement setup, pointer to the reproduce recipe).

AI-assisted (Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mhoqueanik and others added 4 commits July 15, 2026 13:46
…EMENT.md

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ght layout

Full per-bucket retune (online collective autotuner, 4x GB200, default
geometry, kernel-mode p50 vs defaults):

- NVFP4: all four non-ikr profiles CONFIRMED optimal (64/512/1024 tokens
  within <=1.2% run noise; @64 the tuner reproduced the SMALL profile
  exactly). The only wins were ikr candidates at >=2048 (-4.8% @2048,
  -4.2% @8192); ikr stays opt-in/autotune because its nondeterministic
  accumulation order would break the bit-exact multirank tests and
  bit-reproducibility. No NVFP4 profile change.
- MXFP8: ONE real change — a new >=2048 profile with dispatch-warp
  token-back (fb4 + reuse_dispatch_warps): 1006.6 vs 1209.6 us @2048
  (-17%), 1742.8 vs 2208.2 @4096 (-21%), 3122.2 vs 4844.0 @8192 (-35%,
  reaching deep_gemm parity at a 3x better accuracy point). Supersedes
  the 07-14 'dispatch-warp is ~5% slower for MXFP8' reading, which
  conflated the token-back mode with fb8. Deterministic (no ikr in the
  mxfp8 candidate space); acc_loss_pct unchanged at 6.358%; mega test
  section re-verified green with the new default.

TUNING.md updated: sweep-table mxfp8 rows re-measured with the new
default, profile provenance notes refreshed. AI-assisted.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ant breakdown

One table now carries dg, nvfp4 bf16/ikr/combine-wire variants, and the
mxfp8 backend (new dispatch-warp default at >=2048) per token count,
replacing the compact nvfp4 column and the duplicate variants table in
'Measured results'.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d summary

First table: nvfp4 combine-leg variants vs dg (unchanged columns).
Second table: backend summary — dg vs best nvfp4 config (+combine_nvfp4)
vs the mxfp8 backend on its new dispatch-warp default.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Anerudhan Anerudhan added run-ci and removed run-ci labels Jul 15, 2026
@Anerudhan
Anerudhan enabled auto-merge (squash) July 15, 2026 21:35
@Anerudhan
Anerudhan disabled auto-merge July 15, 2026 21:37
Formatting only (ruff format via pre-commit); no behavior change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Anerudhan Anerudhan removed the run-ci label Jul 15, 2026
@flashinfer-bot

Copy link
Copy Markdown
Collaborator

[FAILED] Pipeline #58079185: 13/20 passed

@Anerudhan
Anerudhan enabled auto-merge (squash) July 16, 2026 06:19
@Anerudhan
Anerudhan merged commit fa527b2 into flashinfer-ai:main Jul 16, 2026
27 of 40 checks passed
Anerudhan pushed a commit that referenced this pull request Jul 28, 2026
…, fused quant+stage launch, persistent knob cache, and prequantized weight packs (#4079)

<!-- .github/pull_request_template.md -->
## 📌 Description

### Summary

Follow-up to #3980. This PR makes the MegaMoE (`moe_ep`) path consumable
by serving engines (vLLM-style integration): full CUDA-graph
capture/replay support, a fused single-launch quant+stage hot path,
engine-friendly weight lifecycle (prequantized packs, post-preprocess
source release), pooled symmetric-buffer workspaces shared across
layers, and a persistent knob cache so production sessions resolve tuned
knobs with a pure lookup. No in-engine autotuning.

### Performance Highlights

All numbers measured on a **single node, 4x GB200 (EP=4)**. The built-in
knob heuristic currently supports **GB200 only** (offline-tuned knob
caches expected to work on any supported device).

**Microbenchmark** — DeepSeek-V3 geometry (256 experts, top-8, hidden
7168, intermediate 2048), `e2e_pipelined` p50 µs, heuristic knobs, best
variant (`nvfp4 + combine_nvfp4`) vs `deep_gemm_mega` baseline:

| tok/rank | deep_gemm_mega | nvfp4 + combine_nvfp4 | Speedup |
|---|---:|---:|---:|
| 512      | 340.0  | 321.1  | 1.06x |
| 1024     | 468.0  | 363.5  | 1.29x |
| 2048     | 817.2  | 529.4  | 1.54x |
| 4096     | 1473.5 | 862.7  | 1.71x |
| 8192     | 2993.7 | 1677.3 | 1.78x |

The sweep also covers real-model MoE geometries (DeepSeek V3 / V4-Flash
/ V4-Pro, Kimi K2.6, Qwen3.5-397B, gpt-oss-120b — the last enabled by
the %64 alignment relaxation); the pattern holds everywhere:
deep_gemm-parity at small token counts, 1.6–1.9x for fp4 combine-wire at
large tokens on 7168-hidden shapes. Full sweep tables (all variants),
accuracy, and benchmark methodology:
[`kernel_src/cutedsl_megamoe/TUNING.md`](https://github.com/mhoqueanik/flashinfer-moe_ep/blob/fi-moe_ep-framework-integration/flashinfer/moe_ep/kernel_src/cutedsl_megamoe/TUNING.md#real-model-geometry-sweep-2026-07-21).

**End-to-end (vLLM)** — early e2e benchmarks with this backend
integrated into vLLM 0.25.1 (DeepSeek-V4-Flash, 4x GB200 TP4/EP4, CUDA
graphs capturing all recurring step shapes including prefill chunks,
per-role offline knob caches)
show **~18% prefill and ~7% decode throughput gain over native vLLM**
(i.e.: vLLMs built-in deep_gemm mega path):

| Workload | native vLLM | fi MegaMoE (nvfp4) | Speedup |
|---|---:|---:|---:|
| Prefill, 8k-token chunks (tok/s) | 45,701 | 53,962 | 1.18x |
| Decode, 1024-seq concurrency (output tok/s) | 21,049 | 22,614 | 1.07x
|
| GSM8K (200q, greedy) | 0.965 | 0.975 | — |

Numbers reproduce within 1% at this branch tip; details and caveats (the
decode number requires graph capture to cover prefill chunk shapes) are
in TUNING.md's e2e section. The vLLM integration itself will land as a
separate PR.

**Accuracy** — per-variant microbench rel-L2 (`acc_loss_pct` vs fp32
dense-MoE reference) is unchanged from #3980; see the accuracy table in
TUNING.md. On real-model distributions the nvfp4 path is at
parity-or-better (GSM8K above), consuming the single-quant nvfp4
checkpoint directly via the prequantized weight-pack path.

### Key Changes

**CUDA Graph Support (cutedsl mega path)**
- Warmup contract + capture guards for graph-safe forward passes
- Tail-mask memo made graph-capture-aware; records the actual staged
count (fixes a view-slicing regression on capture-touched buffers)
- 2-rank lockstep CUDA-graph replay test for the nvfp4 mega layer

**Launch-Path Optimizations**
- Fused single-launch quant+stage for the cutedsl mega path; fused
staging for `deep_gemm` mega with a 16B-alignment fallback
(bit-identical to the multi-kernel path, toggle:
`FLASHINFER_MEGA_FUSED_STAGE`)
- Zero-copy output + memoized tail mask + caller-owned ikr buffers to
cut per-call launch count
- Cached stream-aware launch thunk in the nvfp4 backend hot path

**Engine Integration: Weights & Workspaces**
- `MoEWeightPack` split into `Unquantized | Prequantized` variants
- Source weight pack released after preprocess (fixes OOM at model load)
- Pooled symmetric-buffer workspaces shared across mega layers

**Tuning Infrastructure**
- Persistent knob cache (`FLASHINFER_MOE_EP_KNOB_CACHE`): offline-tuned
winners resolved by pure lookup before the built-in heuristic; populated
via the new offline tuner CLI `python -m flashinfer.moe_ep.tune`.
Per-role cache files (prefill-tuned / decode-tuned) are the validated
deployment pattern.
- Skew-aware tuning (`--skew`), schedule-axis sweep (`--sweep
schedule`), `--live-tokens`

**Shape Coverage & Guardrails**
- cutedsl mega alignment relaxed to %64 (dg keeps %128) — enables
gpt-oss-120b-class geometries - Warn-once import check for the CuTe-DSL
4.6.1 performance floor

**Determinism Investigation (docs)**
- Reported `fi_dg` cross-run nondeterminism traced to vLLM
batch-formation timing (schedule diff, 1-tok vs 8-tok batch) —
engine-level and backend-independent; closed as a FlashInfer issue with
probe tests retained

### Directories Affected
- `flashinfer/moe_ep/backends/mega/` — CUDA graph support, fused
staging, launch caching
- `flashinfer/moe_ep/kernel_src/cutedsl_megamoe/` — shim knob cache,
TUNING.md
- `flashinfer/moe_ep/core/`, `flashinfer/moe_ep/modes/` — workspace
pooling, weight-pack lifecycle
- `flashinfer/moe_ep/tune.py` — offline tuner CLI
- `tests/moe_ep/` — CUDA graph (single- and multi-rank), fused stage,
knob cache, weight-pack, workspace-pool, determinism-probe coverage

### Next Steps (WIP)
- Generalized tuning: extend the knob heuristic / offline-tuned profiles
beyond GB200
- Performance support for CuTe-DSL 4.5.2 (currently 34–54% slower than
4.6.1, which is treated as the perf floor with a warn-once import check)

## 🔍 Related Issues

Follow-up to #3980 (MegaMoE kernel restructure).

### ✅ Pre-commit Checks

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

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

## 🧪 Tests

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

New tests: `test_mega_cuda_graph.py`,
`test_mega_cuda_graph_multirank.py`,
`test_fused_quant_stage.py`, `test_knob_cache.py`,
`test_weight_pack_union.py`,
`test_workspace_pool.py`, `test_moe_ep_deep_gemm_skew_determinism.py`.
Multi-rank tests run under `tests/moe_ep/run_tests.sh`.

## Reviewer Notes

- The fused quant+stage path is bit-identical to the original
multi-kernel path (`tests/moe_ep/test_fused_quant_stage.py`);
`FLASHINFER_MEGA_FUSED_STAGE=0` reverts it for bisection.
- The knob cache keeps autotuning strictly offline: `knobs=None`
sessions do a pure cache lookup, falling back to the heuristic on miss —
no measurement runs inside an engine process.

<!-- 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

* **New Features**
* Added fused input staging for supported MoE quantization workflows,
with an environment-variable toggle.
* Added persistent autotuning cache support and an offline tuning
command.
* Added workspace sharing to reduce memory usage across compatible
layers.
* Added optional zero-copy output handling and CUDA Graph warmup
support.
  * Added explicit pre-quantized and unquantized weight types.
* **Bug Fixes**
* Improved CUDA Graph capture safety, token tracking, alignment support,
and workspace cleanup.
* **Documentation**
* Updated configuration and tuning guidance, including cache settings
and performance information.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
mhoqueanik added a commit that referenced this pull request Jul 30, 2026
…le test (#4221)

test_deep_gemm_mega_kernel_matches_torch_reference (added in #3980)
fails under plain pytest with "ValueError: ... environment variable RANK
expected, but not set": its WORLD_SIZE guard only skips when the
variable is set and != 1, so without torchrun it falls through to
dist.init_process_group(backend="nccl"), whose default env:// rendezvous
requires torchrun's RANK/MASTER_* variables.

This is only reachable on CI jobs that combine plain-pytest discovery of
tests/moe_ep, a cu13 image with the EP stack (deep_gemm) installed, and
a capability-10 GPU — i.e. the B300 cu130 unit-test job, where it
currently errors on every run.

Fix: when RANK is absent, self-bootstrap a 1-rank NCCL group via an
explicit tcp://127.0.0.1:<free-port> init (rank=0, world_size=1) instead
of env://. Deliberately avoids os.environ mutation so RANK/MASTER_*
don't leak to later tests in the same pytest process; teardown is
unchanged (conftest.pytest_sessionfinish destroys the group). The
torchrun path is preserved and run_tests.sh still exercises it.

Verified on GB200 (single GPU): both launch modes pass with
rel_l2=0.0027 vs the torch oracle; the plain-pytest mode was confirmed
with RANK/WORLD_SIZE/MASTER_ADDR/MASTER_PORT/LOCAL_RANK explicitly
unset.

AI-assisted (Claude Code).

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

## 📌 Description

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

## 🔍 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.
-->

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
mhoqueanik added a commit to mhoqueanik/flashinfer-moe_ep that referenced this pull request Aug 13, 2026
…hinfer-ai#4449

Five of the six findings are actionable in our tree:

- test_sm90_push_fp8_packaging.py: the import-boundary gate built
  backend_root with the pre-taxonomy flat name
  kernel/sm90_fp8_fp8_bf16_push_cuda, which no longer exists —
  Path.rglob on a missing dir yields nothing, so the gate passed
  vacuously. Fixed to kernel/sm90/fp8_fp8_bf16_push_cuda and added
  is_dir/non-empty asserts; the gate now validates 5 files / 5 marker
  imports (verified standalone).
- _sm90_push_fp8_baseline.py: _weight_cache entries now evict via
  weakref callbacks when either source tensor dies, instead of pinning
  four dead GPU tensors per (w13, w2) pair. In-place weight mutation
  versioning is intentionally skipped — the oracle tests never mutate
  expert weights after quantization.
- cutedsl_megamoe/shim/__main__.py: added, so the documented
  `python -m ...cutedsl_megamoe.shim` invocations in correctness.py
  actually resolve (delegates to correctness.main()).
- cutedsl_megamoe/VENDOR.md: filled the provenance TODOs with what is
  honestly known — repo URL (internal GitLab, already referenced in the
  pending-diffs section), drop date 2026-07-13 via PR flashinfer-ai#3980, and an
  explicit note that the base SHA was never recorded and MUST be pinned
  at the next full re-sync.
- test_sm90_push_fp8_orchestrator.py: RUF043 — raw-string the
  pytest.raises match pattern.

The sixth (missing CHECK_DIM(2, out) in the vendored
push_style_megamoe/src/a2a/sm90_push_a2a_ops.cu) is real but sits in a
verbatim-vendored src/ tree: per kernel_src/README.md it routes to
upstream (now flashinfer main's own copy of the file) rather than being
patched here.

AI-assisted (Claude Code).

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants