Skip to content

feat(moe_ep): add SM90 push FP8 mega-MoE backend for Hopper - #4069

Merged
mhoqueanik merged 7 commits into
flashinfer-ai:mainfrom
leonardHONG:feat/sm90-push-fp8-migration
Aug 12, 2026
Merged

mhoqueanik merged 7 commits into
flashinfer-ai:mainfrom
leonardHONG:feat/sm90-push-fp8-migration

Conversation

@leonardHONG

@leonardHONG leonardHONG commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

📌 Description

This PR registers sm90_push_fp8, a whole-layer expert-parallel MoE backend for SM90 GPUs connected through single-node NVLink, as a fourth mega backend behind the existing register_mega_kernel plugin surface.

The public entry point is MoEEpLayer with Sm90PushFp8MegaMoeConfig. Existing backend behavior is unchanged.

The steady-state forward path covers dispatch, FP8 block-scale expert FFN, and combine without host-side synchronization, and supports CUDA Graph capture after initialization. The diagram below shows the path with all three optional optimizations enabled:

bump_tag
  -> wait_acks
  -> deduplicated dispatch
  -> wait_prefix
  -> compact
  -> FC1 with fused SwiGLU + 1x128 quantization
  -> FC2
  -> grouped combine with owner-side FP32 route reduction
  -> BF16 reduction of received owner rows
  -> ack

The backend provides three independently configurable optimizations. Validation and performance results below use all three unless stated otherwise.

  • Deduplicated dispatch stores one payload row per token and destination rank instead of one per route. In the tested configurations, with all other options fixed, its output matched the per-route path under torch.equal. For K=6, EP=4, and random routing, it reduces dispatch payload bytes by approximately 43%.
  • Grouped combine performs FP32 route reduction on the owner rank before pushing one quantized row per token. Its correctness gate compares the final output against the same reference used by the per-route path because the quantization point moves.
  • Fused FC1 epilogue performs SwiGLU and 1x128 activation quantization inside the FC1 DeepGEMM epilogue, eliminating an approximately 1 GiB intermediate BF16 activation buffer per rank at the DSV3 EP8 shape. In the tested configurations, with all other options fixed, its output matched the unfused path under torch.equal. The unfused path remains the default and maintained reference; the fused path is opt-in.

Motivation

The existing Hopper EP stack composes NCCL all-to-all with a local fused-MoE operator, paying for additional launches, intermediate buffers, and quantization boundaries.

sm90_push_fp8 writes quantized payloads directly into peer-mapped symmetric memory and feeds received rows directly into the expert GEMM pipeline.

Contracts introduced by this PR

  • capacity_factor bounds the GEMM, TMA, and scale buffers, rather than only the protocol window. The workspace query takes explicit expected_m and max_rows, so block-M tactics and cubin keys remain independent of the memory bound.

  • Building the grouped GEMM requires CUDA Toolkit 12.8 or newer. The generator explicitly rejects older toolkits, and AOT skips this module below CUDA 12.8, allowing JIT-cache wheels to continue building on CUDA 12.6. Loading and running the generated module also requires a CUDA runtime version 12.8 or newer.

  • Runner initialization elects one NVCC-capable rank per cache and tactic group to compile cold DeepGEMM cubins. Other ranks load the resulting cubins from disk.

    • A warm cache works without NVCC.
    • A cold cache without NVCC fails during initialization with an error explaining how to select the fallback.
    • TRTLLM_DG_ENABLED=0 selects the fixed-tactic CUTLASS fallback on the unfused path.
  • Wait kernels use a %globaltimer deadline and publish a shared abort marker before trapping. Peers polling the same protocol window observe the marker and fail. Because a trap leaves a sticky CUDA error, the process launcher must terminate the full rank group from the CPU.

  • This backend is a stateful variant of the mega-kernel contract:

    • construction binds transformed static weights;
    • stage_inputs pre-binds the caller's output tensor.

    The architecture and runbook documentation are updated accordingly, including narrowing the two-entry wording to buffer-oriented kernels. The MegaKernelBackend lifecycle itself is unchanged.

Correctness

Full kernel acceptance ran on an 8× H800 NVLink node:

Suite Scale Result
Single-GPU suite, 105 configurations 1 GPU all passed
Distributed suites EP2 / EP4 / EP8 all passed
Uneven token distributions, zero-token ranks, launch skew EP2–EP8 all passed
CUDA Graph capture and replay tested configurations all passed
200-round oracle-checked soaks every tested configuration all passed
Eight-GPU acceptance matrix at DSV3 shapes EP8 all passed, no skipped cases

The final PR tree was then revalidated across three Hopper variants:

Suite Hardware Result
Single-GPU suite, all configurations 1× H20 130 passed, 0 failed
torchrun EP2 2× H100 NVLink 15 passed, 0 failed
torchrun EP4 4× H20 NVLink 15 passed, 0 failed
compute-sanitizer memcheck / racecheck / initcheck, excluding trap and soaks 1× H20 Clean

Additional hardware-verified properties on the final PR tree:

  • In the tested configurations, the fused FC1 output matched the unfused reference under torch.equal.
  • Leader deduplication was verified by counting NVCC invocations against a shared cold cache: exactly one compilation per cubin across four ranks.
  • Warm-cache execution without NVCC was exercised end to end.
  • Cold-cache fail-fast behavior was exercised end to end.
  • The CUTLASS fallback was exercised end to end.

Performance

Performance numbers were collected during kernel acceptance on the 8× H800 node. The final PR tree was functionally revalidated as listed above.

Setup

  • H800 NVLink
  • CUDA 12.9
  • PyTorch 2.8
  • barrier-aligned timing
  • maximum latency across ranks
  • autotuned baseline tactics
  • identical correctness gates on both sides
  • T denotes live tokens per rank

The tables report baseline latency / sm90_push_fp8 latency; values greater than favor this backend. H, E, and K denote hidden size, expert count, and top-k routes. random distributes routes across experts, while hot1 concentrates routes on one expert.

Versus NCCL all-to-all with the same FP8 block-scale compute

Configuration Decode T64 Throughput T2048
EP4 SMALL, H4096 E32 K6 1.1–1.5× 2.0–2.3×
EP4 DSV3, H7168 E256 K8 1.01× 2.0–2.3×

Against a wire-only NCCL transport reference that excludes compaction and combine reduction, the push transport measured 1.9–3.2× faster.

This number should be interpreted only as a comparison against that specific measured reference. It is not a claim against third-party EP libraries.

Versus NCCL + cutlass_fused_moe FP8

Configuration Decode T64 T2048 random T2048 hot1
EP4 DSV3 1.06× 2.3× 2.0×
EP8 DSV3 1.10× 2.2–2.4× 1.6×
EP4 SMALL 3.1× 2.1× See limitations

At EP1 and equal FP8 precision, sm90_push_fp8 measured 2.3–3.4× faster than cutlass_fused_moe over T64–T2048.

EP8 scaling

Configuration:

DSV3
capacity = 2048
all three optimizations enabled

Results:

T64:    0.758 ms
T2048:  3.238 ms

With capacity fixed at 2048, a 32× increase in live token count increased latency by 4.27×.

Additional observations:

  • no scheduling cliff near T128;
  • all-remote routing adds approximately 5%;
  • running 64 live tokens with buffers sized for 2048 adds approximately 0.7%.

Comparison with Pull Style

[From comment]

Ran the six suggested shapes on 8× H100 80GB SXM. These are back-to-back steady-state measurements with 20 warmups and 100 samples per path. The table reports p50 latency in milliseconds; the last column is Pull / Push, so values above 1 mean Push is faster.

Geometry Tokens/rank Pull Push Speedup
DeepSeek V3 8 0.572 0.614 0.93×
64 0.657 0.704 0.93×
512 1.699 1.065 1.60×
2048 3.497 2.728 1.28×
8192 12.935 9.886 1.31×
Kimi K2.6 8 0.716 0.749 0.96×
64 0.913 0.949 0.96×
512 2.038 1.163 1.75×
2048 3.867 2.776 1.39×
8192 13.023 10.046 1.30×
DeepSeek V4 Flash 8 0.386 0.410 0.94×
64 0.436 0.468 0.93×
512 0.871 0.581 1.50×
2048 1.960 1.410 1.39×
8192 5.752 4.609 1.25×
DeepSeek V4 Pro 8 0.951 0.966 0.98×
64 1.301 1.294 1.01×
512 2.660 1.479 1.80×
2048 4.315 3.168 1.36×
8192 14.172 10.757 1.32×

Push is about 4–5% slower at the smallest token counts, but becomes consistently faster from T512 onward, with gains of roughly 1.25–1.80×.

Correctness also passed across all measured cases (minimum cosine similarity 0.99816, maximum NRMSE 0.06063).

GPT-OSS 120B is not supported yet because its hidden dimensions are not 128-aligned, and Qwen3.5 397B uses top-k 10 while the current limit is 8.

Limitations

  • Supports:

    • SM90;
    • single-node, peer-accessible NVLink;
    • protocol limit ep_size <= 32, with hardware validation in this PR covering up to EP8;
    • top_k in {1, 2, 4, 6, 8};
    • DeepSeek-style FP8 block scaling;
    • SwiGLU.

    Unsupported configurations raise an explicit error rather than silently falling back.

  • The unfused FC1 path supports intermediate_size <= 16384; larger configurations require fuse_fc1_epilogue=True.

  • At DSV3 decode shapes, the advantage over NCCL + cutlass_fused_moe is 1.06–1.10×, because computation dominates.

  • In the artificial SMALL T2048 case where all K routes target one expert, the backend is 25% slower than the autotuned CUTLASS stack. Realistic hot1 routing remains faster at EP4 and EP8.

  • The fused FC1 epilogue does not yet have a small-M swapAB variant and can be disabled independently below its break-even point.

  • Round tags use uint32. Reuse after 2^32 forwards on one pipe is a documented limit.

  • Custom raw-stream execution through bootstrap.stream is rejected explicitly rather than silently ignored.

How to run

Single-GPU and host-only suites

pytest \
  tests/moe_ep/test_sm90_push_fp8_kernel.py \
  tests/moe_ep/test_sm90_push_fp8_backend.py \
  tests/gemm/test_sm90_moe_gemm.py \
  tests/gemm/test_sm90_moe_gemm_contract.py

Multi-rank tests

Requires at least two SM90 GPUs and uses torch.distributed only.

bash tests/moe_ep/run_tests.sh sm90_push

Soak tests

SM90_PUSH_SOAK_ROUNDS=200 \
  bash tests/moe_ep/run_tests.sh sm90_push

Benchmark

torchrun \
  --standalone \
  --nproc-per-node=8 \
  benchmarks/bench_sm90_push_megamoe.py \
  --config DSV3 \
  --dedup \
  --grouped-combine \
  --fuse-fc1 \
  --assert-cos-min 0.997

🔍 Related issues

This PR contributes the Hopper FP8 block-scale milestone under #3692 and the SM90 sub-issue #3780, and follows the whole-layer integration direction described in #3704.

✅ 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.

🧪 Tests

  • Tests added and updated.
  • Single-GPU suite passes.
  • EP2 distributed suite passes.
  • EP4 distributed suite passes.
  • CUDA Graph tests pass.
  • Soak tests pass on the listed hardware.

Reviewer notes

Native NVFP4 and MXFP8 execution are out of scope because SM90 does not provide native block-scaled tensor-core instructions for those formats. A follow-up PR stacked on this one adds NVFP4 checkpoint support for SM90 on top of this backend, through online W4A8 decode kernels and a one-time requantization path that rides this FP8 backend unchanged.
The highest-value review areas are:

Entry points for the highest-risk areas:

  • Symmetric-window ordering and the acknowledgement protocol:
    kernel_src/sm90_push_megamoe/src/a2a/sm90_push_a2a_ops.cu

  • Grouped-combine numerical behavior, where the quantization point moves:
    The combine kernels in kernel_src/sm90_push_megamoe/src/a2a/sm90_push_a2a_ops.cu, gated by the reference comparison in tests/moe_ep/test_sm90_push_fp8_kernel.py

  • DeepGEMM fused-epilogue and workspace contracts:
    src/fp8_gemm/fp8_moe_fc1_fused.cuh``shim/gemm.py

  • CUDA 12.8 gating and the AOT skip:
    shim/gemm.py``flashinfer/aot.py

  • Collective initialization failure handling:
    shim/protocol.py

Summary by CodeRabbit

  • New Features

    • Added SM90 FP8 MegaMoE support with pull- and push-style execution paths.
    • Added configurable FP8 formats, scaling modes, routing, reduction, CUDA graph, and distributed execution options.
    • Added weight preprocessing and public configuration interfaces.
    • Added benchmark tools for correctness, performance, token sweeps, and peer-to-peer bandwidth.
  • Documentation

    • Expanded architecture guidance, setup instructions, tuning information, and reproducibility runbooks.
  • Tests

    • Added broad correctness, validation, packaging, lifecycle, distributed, and CUDA graph coverage.

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds a new SM90 push-style FP8 MegaMoE backend (protocol, GEMM, tests, benchmarks) and vendors two large CuTeDSL kernel source drops: an SM100 tree (MXFP8 and NVFP4 fused kernels) and an SM90 pull-style tree (FP8 GLU and NVFP4 kernels). Shared mega backends, docs, and packaging are updated to reference the new source layout.

Changes

SM90 Push FP8 MegaMoE Backend

Layer / File(s) Summary
Backend contracts and lifecycle
flashinfer/moe_ep/backends/mega/kernel/sm90_push_fp8/*, flashinfer/moe_ep/backends/mega/kernel/sm90_pull_fp8/*, flashinfer/moe_ep/core/..., flashinfer/moe_ep/modes/mega_layer.py
Adds the SM90 push and pull FP8 backend classes, config, weights, and staging modules. Updates core kernel/runtime/validation code and mega_layer output allocation order.
Shared mega backend updates
flashinfer/moe_ep/backends/mega/kernel/{deep_gemm_mega,mxfp8_cutedsl,nvfp4_cutedsl}/*
Retargets shim imports to the new sm100 source path. Adds fused staging and output-optional compute support.
SM90 push protocol and GEMM sources
flashinfer/moe_ep/kernel_src/sm90/push_style_megamoe/*
Adds the push protocol shim, runner, weight transforms, JIT generation, and CUDA A2A/GEMM kernel sources.
Correctness and contract validation
tests/moe_ep/test_sm90_push_fp8_*, tests/moe_ep/test_moe_ep_*mega_multirank.py, tests/moe_ep/conftest.py
Adds CPU, distributed, graph-replay, soak, trap, and packaging tests. Adds a DeepGEMM cache isolation fixture.
Benchmarks, baseline, and reference paths
benchmarks/bench_sm90_push_megamoe.py, benchmarks/sm90_push_megamoe_*.py
Adds a pure-Torch reference, a grouped-GEMM baseline, and an end-to-end benchmark with correctness gates.
Documentation and packaging wiring
flashinfer/aot.py, pyproject.toml, .pre-commit-config.yaml, docs/design_docs/*
Updates AOT module generation, package data, pre-commit exclusions, and architecture/runbook documentation.

SM100 CuTeDSL MegaMoE Kernel Drop

Layer / File(s) Summary
Shim adapters and docs
flashinfer/moe_ep/kernel_src/sm100/cutedsl_megamoe/shim/*, *.md
Adds path bootstrap, comm, autotune, knob cache, quant staging, and tuner modules for the SM100 shim layer.
Common utilities
.../src/common/*
Adds shared dtype constants, quantization helpers, and SwiGLU/PTX arithmetic used by both kernel variants.
MXFP8 GLU kernel
.../src/moe_mxfp8_glu/*
Implements the MXFP8 epilogue, fused fc12 kernel, MegaMoE wrapper, reference/runner, and test scripts.
NVFP4 swap-AB kernel
.../src/moe_nvfp4_swapab/*
Implements the NVFP4 contract, scheduler, dynamic mainloop, epilogue, fused kernel, and topk reduce.
Shared low-level infrastructure
.../src/src/*
Adds the symmetric-heap bootstrap, cleanup kernel, config, dispatch/token-comm kernels, and PTX helpers.

SM90 Pull-Style CuTeDSL MegaMoE Kernel Drop

Layer / File(s) Summary
Shim adapters and docs
flashinfer/moe_ep/kernel_src/sm90/pull_style_cutedsl_megakernel/{__init__.py,shim/*,*.md}
Adds path bootstrap, comm, and lazy kernel-helper exports for the SM90 pull shim.
Common utilities
.../src/common/*
Adds shared dtype constants, quantization/comparison helpers, and SwiGLU/PTX arithmetic.
Hopper FP8 GLU kernel
.../src/moe_hopper_fp8/*
Implements the FP8 lazy-compile frontend, epilogues, fused fc12 kernels, MegaMoE wrapper, reference/runner, and benchmark/test tooling.
NVFP4 swap-AB kernel
.../src/moe_nvfp4_swapab/*
Implements the NVFP4 swap-AB epilogue, scheduler, dynamic mainloop, fused kernel, topk reduce, and reference/runner/tests.
Shared low-level infrastructure
.../src/src/*
Adds the symmetric-heap bootstrap, cleanup kernel, config, dispatch/token-comm kernels, and PTX helpers.

Estimated code review effort: 5 (Critical) | ~180 minutes

Suggested labels: run-ci, op: moe

Suggested reviewers: aleozlx, anerudhan, iwakurarein

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 28.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the new SM90 push FP8 MegaMoE backend for Hopper and matches the primary changes.
Description check ✅ Passed The description is detailed, covers objectives, contracts, validation, performance, limitations, related issues, and testing evidence.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces the 'sm90_push_fp8' MegaMoE backend, which provides an optimized FP8 communication and computation path for Hopper (SM90) GPUs. The changes include new kernel sources, Python shims for JIT compilation, and comprehensive correctness tests. I have reviewed the code and identified two issues: a potential cross-device link failure in the JIT cache renaming logic and a strict aliasing violation in the tag increment kernel. Both issues have actionable suggestions provided in the comments.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +197 to +204
try {
std::filesystem::rename(tmp_cubin_path, cubin_path);
} catch (std::filesystem::filesystem_error const&) {
if (!std::filesystem::exists(cubin_path)) {
std::filesystem::remove_all(tmp_path);
throw;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

high

In containerized or multi-partition environments (such as Docker or cluster nodes), the temporary directory (e.g., /tmp) and the cache directory (e.g., ~/.tensorrt_llm/cache) are often located on different filesystems. In such cases, std::filesystem::rename will fail with EXDEV (Invalid cross-device link), causing the compilation to fail completely. To make this robust, add a fallback to std::filesystem::copy_file and then remove the temporary directory if rename fails.

    try {
      std::filesystem::rename(tmp_cubin_path, cubin_path);
    } catch (std::filesystem::filesystem_error const&) {
      if (!std::filesystem::exists(cubin_path)) {
        try {
          std::filesystem::copy_file(tmp_cubin_path, cubin_path, std::filesystem::copy_options::overwrite_existing);
        } catch (...) {
          std::filesystem::remove_all(tmp_path);
          throw;
        }
      }
    }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Both directories are under the same cache root, so this does not cross filesystems. The atomic rename is intentional to avoid exposing a partially written cubin to another rank.

Comment on lines +105 to +110
__global__ void bump_tag_kernel(int32_t* round_ctr) {
if (threadIdx.x == 0 && blockIdx.x == 0) {
auto* u = reinterpret_cast<uint32_t*>(round_ctr);
*u = *u + 1u;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Reinterpreting int32_t* as uint32_t* and dereferencing it violates the strict aliasing rule in C++, which is undefined behavior (UB). This can lead to compiler optimizations that misbehave or omit the write entirely. Instead of using reinterpret_cast, you can safely perform the increment by casting the dereferenced value to uint32_t, incrementing it, and casting it back to int32_t before writing it back.

  if (threadIdx.x == 0 && blockIdx.x == 0) {
    *round_ctr = static_cast<int32_t>(static_cast<uint32_t>(*round_ctr) + 1u);
  }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Access through the matching unsigned type is valid C++. We use it intentionally so the tag wraps modulo 2^32 without signed-overflow UB.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
tests/moe_ep/run_tests.sh (1)

111-127: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: run_sm90_push is gated behind require_nccl_ep inside run_multirank.

run_multirank returns early when require_nccl_ep fails (Line 112), so the SM90 push suite—which uses the push backend, not nccl_ep—won't run in the multirank flow on boxes without nccl_ep. The standalone sm90_push target still covers it, but consider running run_sm90_push before the nccl_ep gate if you want it exercised in all/multirank regardless.

🤖 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 `@tests/moe_ep/run_tests.sh` around lines 111 - 127, Move the run_sm90_push
invocation in run_multirank before the require_nccl_ep early-return gate so the
SM90 push suite runs regardless of NCCL EP availability. Preserve the existing
return-code aggregation by setting rc on failure, then run the NCCL-dependent
suites only after the gate succeeds.
flashinfer/moe_ep/kernel_src/sm90_push_megamoe/__init__.py (1)

19-33: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Optional: __all__ is not sorted (Ruff RUF022). Both new re-export modules declare __all__ in insertion order; apply isort-style sorting to satisfy the lint rule and keep the two surfaces consistent.

  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/__init__.py#L19-L33: sort the __all__ entries alphabetically.
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/__init__.py#L17-L31: sort the __all__ entries alphabetically.
🤖 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/moe_ep/kernel_src/sm90_push_megamoe/__init__.py` around lines 19 -
33, Sort the __all__ entries alphabetically in both
flashinfer/moe_ep/kernel_src/sm90_push_megamoe/__init__.py (lines 19-33) and
flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/__init__.py (lines 17-31),
preserving all existing exports and applying the same isort-style ordering to
both modules.

Source: Linters/SAST tools

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

Inline comments:
In `@tests/moe_ep/_sm90_push_fp8_baseline.py`:
- Around line 28-63: Update quant_weights so each _weight_cache entry retains
the source w13 and w2 tensors alongside the quantized result, preventing
data_ptr reuse from returning stale weights. On cache hits, return the cached
result at the entry’s first element, and store the source tensors with the
result when populating the cache.

In `@tests/moe_ep/test_sm90_push_fp8_backend.py`:
- Around line 307-325: Update the capacity assertion in
test_public_ep1_forward_validation_and_capacity to match the token_capacity
wording emitted by validate_forward, replacing the max_tokens_per_rank
expectation while leaving the dtype checks unchanged.

---

Nitpick comments:
In `@flashinfer/moe_ep/kernel_src/sm90_push_megamoe/__init__.py`:
- Around line 19-33: Sort the __all__ entries alphabetically in both
flashinfer/moe_ep/kernel_src/sm90_push_megamoe/__init__.py (lines 19-33) and
flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/__init__.py (lines 17-31),
preserving all existing exports and applying the same isort-style ordering to
both modules.

In `@tests/moe_ep/run_tests.sh`:
- Around line 111-127: Move the run_sm90_push invocation in run_multirank before
the require_nccl_ep early-return gate so the SM90 push suite runs regardless of
NCCL EP availability. Preserve the existing return-code aggregation by setting
rc on failure, then run the NCCL-dependent suites only after the gate succeeds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3c1c788f-1baf-46c3-8f7c-ba053d6f9bed

📥 Commits

Reviewing files that changed from the base of the PR and between c83607a and 0e01918ffb2b4189baf26cbb4deda5016d6987e7.

📒 Files selected for processing (47)
  • benchmarks/bench_sm90_push_megamoe.py
  • benchmarks/sm90_push_megamoe_baseline.py
  • benchmarks/sm90_push_megamoe_reference.py
  • docs/design_docs/moe_ep_architecture.md
  • docs/design_docs/moe_ep_runbook.md
  • flashinfer/aot.py
  • flashinfer/comm/mnnvl.py
  • flashinfer/moe_ep/__init__.py
  • flashinfer/moe_ep/backends/mega/kernel/__init__.py
  • flashinfer/moe_ep/backends/mega/kernel/deep_gemm_mega/backend.py
  • flashinfer/moe_ep/backends/mega/kernel/mxfp8_cutedsl/backend.py
  • flashinfer/moe_ep/backends/mega/kernel/nvfp4_cutedsl/backend.py
  • flashinfer/moe_ep/backends/mega/kernel/sm90_push_fp8/__init__.py
  • flashinfer/moe_ep/backends/mega/kernel/sm90_push_fp8/backend.py
  • flashinfer/moe_ep/backends/mega/kernel/sm90_push_fp8/config.py
  • flashinfer/moe_ep/backends/mega/kernel/sm90_push_fp8/staging.py
  • flashinfer/moe_ep/backends/mega/kernel/sm90_push_fp8/weights.py
  • flashinfer/moe_ep/core/kernel/base.py
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/ACKNOWLEDGEMENT.md
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/__init__.py
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/__init__.py
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/gemm.py
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/jit.py
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/protocol.py
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/runner.py
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/shim/weights.py
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/src/a2a/sm90_push_a2a.cuh
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/src/a2a/sm90_push_a2a_ops.cu
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/src/fp8_gemm/fp8_moe_binding.cu
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/src/fp8_gemm/fp8_moe_fc1_fused.cuh
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/src/fp8_gemm/fp8_moe_jit.cuh
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/src/fp8_gemm/fp8_moe_launcher.cuh
  • flashinfer/moe_ep/kernel_src/sm90_push_megamoe/src/fp8_gemm/fp8_moe_scheduler.cuh
  • flashinfer/moe_ep/modes/mega_layer.py
  • pyproject.toml
  • tests/conftest.py
  • tests/gemm/test_sm90_moe_gemm.py
  • tests/gemm/test_sm90_moe_gemm_contract.py
  • tests/moe_ep/_sm90_push_fp8_baseline.py
  • tests/moe_ep/_sm90_push_fp8_reference.py
  • tests/moe_ep/run_tests.sh
  • tests/moe_ep/test_mega_layer_validation.py
  • tests/moe_ep/test_sm90_push_fp8_backend.py
  • tests/moe_ep/test_sm90_push_fp8_backend_cpu.py
  • tests/moe_ep/test_sm90_push_fp8_kernel.py
  • tests/moe_ep/test_sm90_push_fp8_orchestrator.py
  • tests/moe_ep/test_sm90_push_fp8_packaging.py

Comment thread tests/moe_ep/_sm90_push_fp8_baseline.py
Comment thread tests/moe_ep/test_sm90_push_fp8_backend.py
@mhoqueanik

Copy link
Copy Markdown
Collaborator

@leonardHONG Thank you for the PR! let me run it on my end and get back to you!

@mhoqueanik

mhoqueanik commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

@leonardHONG My apologies for the delay! I am still restructuring moe_ep to adapt new dtypes and arch (+creating vLLM PR of fi moe_ep). Tentatively will process your PR and this #4113 by mid-next week

@mhoqueanik

Copy link
Copy Markdown
Collaborator

@leonardHONG I was able to reproduce your perf on my end. The numbers look great! Can you resolve the conflicts, and rebase it against this PR: #4113? That way we can cleanly merge it

@leonardHONG
leonardHONG force-pushed the feat/sm90-push-fp8-migration branch from 51ba0e9 to 6586c79 Compare July 30, 2026 02:27
@leonardHONG
leonardHONG force-pushed the feat/sm90-push-fp8-migration branch from 1a9d124 to 79e60a6 Compare August 8, 2026 23:29
@leonardHONG

Copy link
Copy Markdown
Contributor Author

Done, rebased onto the latest main and cleaned up the remaining coderabbit comments. Thanks!

@mhoqueanik mhoqueanik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Overall looks good to me. We can proceed with the merge once the LICENSE/ACKNOWLEDGE.md issue is clear.

Comment thread flashinfer/moe_ep/backends/mega/kernel/mxfp8_cutedsl/backend.py
Comment thread flashinfer/moe_ep/modes/mega_layer.py
@Anerudhan

Copy link
Copy Markdown
Collaborator

/bot run tests/moe_ep

@flashinfer-bot

Copy link
Copy Markdown
Collaborator

GitLab MR !1181 has been created, and the CI pipeline #62029339 is currently running. I'll report back once the pipeline job completes.

@mhoqueanik
mhoqueanik enabled auto-merge (squash) August 11, 2026 00:43
@mhoqueanik mhoqueanik self-assigned this Aug 11, 2026
@flashinfer-bot

Copy link
Copy Markdown
Collaborator

[SUCCESS] Pipeline #62029339: 18/18 executed test jobs passed

@mhoqueanik
mhoqueanik merged commit f9b13ef into flashinfer-ai:main Aug 12, 2026
77 of 86 checks passed
mhoqueanik added a commit to mhoqueanik/flashinfer-moe_ep that referenced this pull request Aug 13, 2026
…flashinfer-ai#4069)

Ports flashinfer-ai#4069 (head 301f8ce, PR still open) onto the
taxonomy/provenance layout, per the incorporation plan:

- kernel_src/sm90/push_style_megamoe/: verbatim byte-for-byte drop from the
  PR head (src/{a2a,fp8_gemm} CUDA sources, shim/, ACKNOWLEDGEMENT.md; no
  {$nv-internal-release} markers at this SHA) + VENDOR.md provenance record.
  Re-diff against the merged SHA when the PR lands.
- backends/mega/kernel/sm90/fp8_fp8_bf16_push_cuda/: the 5 wrapper files
  relocated from upstream's flat kernel/sm90_push_fp8/, import depths fixed
  for the extra package level, config renamed to
  Sm90_Fp8_Fp8_Bf16_PushCuda_MegaMoeConfig with kernel_name
  "sm90_fp8_fp8_bf16_push_cuda", registered with
  deprecated_aliases=("sm90_push_fp8",).
- Package wiring: moe_ep/__init__.py taxonomy import + Sm90PushFp8MegaMoeConfig
  deprecated alias + preprocess_sm90_push_fp8_mega_weights; kernel/sm90
  re-exports; alias row in test_deprecated_aliases.py.
- Core deltas from the PR: mega_layer.py allocates the output before
  stage_inputs; pyproject package-data ships the drop's .cu/.cuh for
  non-editable installs; conftest isolated_deep_gemm_cache fixture; the
  mega-layer allocation-order regression test.
- Tests: the 9 sm90_push_fp8 test files (names kept for re-sync friction)
  with imports/config names rewritten to the taxonomy. Deviation from
  upstream: run_tests.sh exposes sm90_push as its own 2-GPU Hopper target
  instead of folding it into multirank — on non-Hopper nodes the arch-marked
  files collect 0 tests and torchrun turns pytest exit 5 into a failure.
- Docs: runbook + architecture doc gain the new drop and backend.

Also hardens the unit target against the long-known suite-accumulated heap
corruption: with every test passing, the process aborted either at the first
heavy import burst (the isolated nvfp4 warmup test) or — new signature, job
2388315 — in CPython teardown after the pytest summary ("malloc(): unaligned
tcache chunk detected"). Both unit pytest invocations now exit via
os._exit(pytest_rc), skipping interpreter finalization; rationale in the
runbook. Root cause still open (needs ASAN).

Validated on B200 (jobs 2388315/2388326): registry/alias smoke, deprecated
aliases, unit x3 green (396 passed / 72 skipped; push cpu/packaging/contract
tests run, Hopper-marked kernel tests skip). Kernel/orchestrator/GEMM tests
need an H100 node — tracked as a follow-up.

AI-assisted (Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@huangzhilin-hzl

Copy link
Copy Markdown
Contributor

Hi @leonardHONG, Thanks for the great work! Is there a benchmark comparing the pull- and push-style backends under the same setup? Which workloads currently favor each style?

@leonardHONG

Copy link
Copy Markdown
Contributor Author

Hi @leonardHONG, Thanks for the great work! Is there a benchmark comparing the pull- and push-style backends under the same setup? Which workloads currently favor each style?

yep, i posted the 8× h100 comparison [above]. pull is a little better for small token counts, while push wins on larger workloads. push currently needs 128-aligned dimensions and top-k ≤ 8, but i may support more shapes later.

mhoqueanik added a commit to mhoqueanik/flashinfer-moe_ep that referenced this pull request Aug 13, 2026
Aligns the branch with flashinfer TOT (13 commits), resolving the
conflicts GitHub flagged on PR flashinfer-ai#4449. The bulk of the collision is the
squash-merge of flashinfer-ai#4069 (f9b13ef) — the SM90 push-style FP8 backend this
branch already carries in taxonomy form (7984140, vendored drop
verified byte-identical to the merged SHA):

- moe_ep/__init__.py, backends/mega/kernel/__init__.py: keep the
  taxonomy spellings (upstream's side is the pre-restructure flat
  imports of the same content, nothing new).
- tests/moe_ep/test_sm90_push_fp8_{backend,backend_cpu,packaging}.py
  add/add: keep ours (taxonomy imports; verified a strict superset of
  upstream's copies).
- pyproject.toml: keep the package-data comment for the push drop.
- Upstream's flat backends/mega/kernel/sm90_push_fp8/ wrapper (5 files,
  auto-merged as new) removed — ours lives at
  sm90/fp8_fp8_bf16_push_cuda/.
- run_tests.sh: restored to our version — the auto-merge duplicated
  run_sm90_push and re-folded it into run_multirank (upstream's shape,
  deliberately rejected in 7984140: on non-Hopper nodes the arch-marked
  files collect 0 tests and torchrun turns pytest exit 5 into a
  failure). Upstream's delta to this file is sm90_push wiring only.

No other moe_ep deltas in this upstream range.

AI-assisted (Claude Code).

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
…lean vs f9b13ef

PR flashinfer-ai#4069 merged upstream 2026-08-12 as squash f9b13ef. Re-diffed the
vendored kernel_src/sm90/push_style_megamoe tree against the merged SHA:
byte-for-byte identical (no post-review deltas between the vendored PR
head 301f8ce and the merge). Future syncs diff against main.

AI-assisted (Claude Code).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@leonardHONG leonardHONG mentioned this pull request Aug 14, 2026
4 tasks
aleozlx pushed a commit that referenced this pull request Aug 14, 2026
…ush-style FP8 backend; sync CuTe-DSL 4.7 quant-staging fix (#4449)

## Summary

Three things: a layout/naming refactor of `flashinfer.moe_ep`'s
mega-kernel layer, the incorporation of the SM90 push-style FP8 backend
(#4069, since merged upstream) as the first new backend added in the
restructured shape, and one vendored-kernel sync that fixes the fused
activation-quant staging crash on CuTe-DSL 4.7 — un-blocking 4.7.x and
lifting the temporary `==4.6.1` pin. The branch is merged up to
upstream/main (2febce5, past the v0.6.17 line and the #4069 squash).
The refactor organizes the layer around two orthogonal views:

1. **Taxonomy (user view)** — backends move to
`backends/mega/kernel/sm<arch>/<act_dtype>_<weight_dtype>_<out_dtype>_<kernel_style>/`,
and registry `kernel_name` strings plus config classes carry the same
fully-qualified names. One glance at a name now tells you the
architecture, the activation/weight/output dtypes, and the kernel style:

   | old kernel_name | new kernel_name | new config class |
   |---|---|---|
| `deep_gemm_mega` | `sm100_fp8_fp4_bf16_deepgemm` |
`Sm100_Fp8_Fp4_Bf16_Deepgemm_MegaMoeConfig` |
| `nvfp4_cutedsl` | `sm100_nvfp4_nvfp4_bf16_cutedsl` |
`Sm100_Nvfp4_Nvfp4_Bf16_Cutedsl_MegaMoeConfig` |
| `mxfp8_cutedsl` | `sm100_mxfp8_mxfp8_bf16_cutedsl` |
`Sm100_Mxfp8_Mxfp8_Bf16_Cutedsl_MegaMoeConfig` |
| `sm90_pull_fp8` | `sm90_fp8_fp8_bf16_pull_cutedsl` |
`Sm90_Fp8_Fp8_Bf16_PullCutedsl_MegaMoeConfig` |
| `sm90_push_fp8` (new, from #4069) | `sm90_fp8_fp8_bf16_push_cuda` |
`Sm90_Fp8_Fp8_Bf16_PushCuda_MegaMoeConfig` |

Naming conventions: deep_gemm dtypes are plain `fp8`/`fp4`, matching
upstream `deep_gemm.fp8_fp4_mega_moe`; the mx/nv prefixes are reserved
for the cutedsl kernels' block-scaled formats. Output dtype is always
bf16 — nvfp4's `combine_dtype` is comm-wire compression, not an output
format.

2. **Provenance (kernel-dev view)** — vendored kernel sources in
`kernel_src/` are keyed by upstream repo snapshot, not by architecture:
`kernel_src/sm100/cutedsl_megamoe` moves to `kernel_src/cutedsl_megamoe`
(the mother repo ships kernels for multiple arches, so an smXX level
misrepresents it). Each drop mirrors the vendor repo layout — `src/`
byte-for-byte upstream, all adaptation in `shim/` — and gains a
`VENDOR.md` recording upstream repo/commit/sync state and pending local
diffs. A new `kernel_src/README.md` states the contract explicitly: **no
edits to `src/` of any kind — including docstrings, comments, and lint
fixes**; tool warnings against vendored files (docstring-coverage gates,
review bots) are handled by excluding the path, never by editing the
file. The sm90 fork trees
(`kernel_src/sm90/pull_style_cutedsl_megakernel` from #4113,
`kernel_src/sm90/push_style_megamoe` from #4069) intentionally stay
separate snapshots — one kernel_src dir = one upstream commit — and fold
into the mother drop if/when upstream merges them.

**Why:** verbatim snapshots must stay diffable against one upstream
commit, and splitting vendored trees per-dtype or per-arch breaks
re-sync; meanwhile users navigate by architecture and dtype, not by
which vendor repo a kernel came from. Putting each concern where its
audience looks resolves the tension. The layout rule is documented in
`docs/design_docs/moe_ep_architecture.md`, and it is what makes new
backend families routine — demonstrated in this very PR by the SM90
push-style incorporation below, and next by the follow-up backend-family
PRs (SM100 BF16 #4386, SM120 MXFP8).

## Directories affected

All changes live under `flashinfer/moe_ep/` plus its tests and docs:

-
`backends/mega/kernel/sm100/{fp8_fp4_bf16_deepgemm,nvfp4_nvfp4_bf16_cutedsl,mxfp8_mxfp8_bf16_cutedsl}/`
and
`backends/mega/kernel/sm90/{fp8_fp8_bf16_pull_cutedsl,fp8_fp8_bf16_push_cuda}/`
— taxonomy backend wrappers (moved/renamed; push_cuda is new).
- `kernel_src/cutedsl_megamoe/` (moved from
`kernel_src/sm100/cutedsl_megamoe/`),
`kernel_src/sm90/pull_style_cutedsl_megakernel/`,
`kernel_src/sm90/push_style_megamoe/` (new) — provenance-keyed vendored
drops, each with `VENDOR.md`; new `kernel_src/README.md` states the
no-edits contract.
- `backends/mega/kernel/tuning.py` + per-backend `tuner.py` files —
tuning machinery moved out of `tune.py` (now a CLI shim).
- `core/kernel/registry.py`, `moe_ep/__init__.py` — deprecated-alias
resolution and re-exports.
- `tests/moe_ep/`, `docs/design_docs/moe_ep_{architecture,runbook}.md`,
`pyproject.toml`/`.pre-commit-config.yaml` excludes, `run_tests.sh` (new
2-GPU `sm90_push` target).

## Test results

- **Full `run_tests.sh` matrix — all 12 targets green** on 4xH100 (job
2389821, 2026-08-13), including the new `sm90_push` Hopper target and
the fault-tolerance suites after the deadlock fixes.
- **B200** (jobs 2388315/2388326): registry/alias smoke, deprecated
aliases, unit x3 green — 396 passed / 72 skipped (push
cpu/packaging/contract tests run; Hopper-marked kernel tests skip).
- **Unit target re-validated green** after the second upstream merge
(job 2389880) and again after the round-2 CodeRabbit fixes (job
2389916), same 396/72 counts, B200.
- **8x B200** (jobs 2384640/2384641/2384650): quant-staging sync matrix
fully green on both dsl 4.6.1 and 4.7.0 (details in the vendored-sync
section below).
- **GB200 + B200**: mxfp8/nvfp4 multirank oracle suites with the
per-cell tolerance band.
- Microbenchmark re-run: no regressions vs pre-restructure reference
numbers (deep_gemm parity; cutedsl kernels at or above their previous
points).
- `pre-commit run -a` fully green at the branch head (e9f791a).

## SM90 push-style FP8 backend (incorporates #4069)

Ports #4069 (head 301f8ce; since merged to main
as f9b13ef — re-diffed, byte-identical, no post-review deltas) onto the
taxonomy/provenance layout, serving as the first proof of the "one
taxonomy backend dir + one provenance-keyed kernel drop" recipe:

- **`kernel_src/sm90/push_style_megamoe/`** — verbatim byte-for-byte
drop from the PR head (`src/{a2a,fp8_gemm}` CUDA sources, `shim/`,
ACKNOWLEDGEMENT.md) plus a `VENDOR.md` provenance record.
- **`backends/mega/kernel/sm90/fp8_fp8_bf16_push_cuda/`** — the five
wrapper files relocated from upstream's flat `kernel/sm90_push_fp8/`,
config renamed to `Sm90_Fp8_Fp8_Bf16_PushCuda_MegaMoeConfig`, registered
with `deprecated_aliases=("sm90_push_fp8",)`.
- **Core deltas carried from the PR:** `mega_layer.py` allocates the
output before `stage_inputs`; pyproject package-data ships the drop's
`.cu`/`.cuh` for non-editable installs; the `isolated_deep_gemm_cache`
conftest fixture; the mega-layer allocation-order regression test.
- **Tests:** the nine sm90_push_fp8 test files (names kept to minimize
re-sync friction) rewritten to the taxonomy. Deviation from upstream:
`run_tests.sh` exposes `sm90_push` as its own 2-GPU Hopper target
instead of folding it into multirank — on non-Hopper nodes the
arch-marked files collect 0 tests and torchrun turns pytest exit 5 into
a failure.

## CuTe-DSL 4.7 quant-staging fix (vendored sync)

The `CUDA_ERROR_MISALIGNED_ADDRESS` crash on cutlass-dsl 4.7.0 — which
presented as a deep_gemm mega multirank failure — was root-caused to the
**fused bf16→quantized activation staging** (`DataPreprocess` in the
vendored cutedsl_megamoe tree), which every mega staging path shares,
deep_gemm's included. The kernel team's fix is synced in as a
single-file partial re-sync per the vendoring policy:

- `kernel_src/cutedsl_megamoe/src/src/inputs_process.py` +
`src/common/host_utils.py` taken **verbatim** from upstream
`bangyus/cutedsl_megamoe @ 50117315d`, recorded in `VENDOR.md` under
pending-diffs (resolves at the next full re-sync). The mxfp8 quant
kernel is reworked so each lane owns one contiguous 16-byte fp8 store
(adjacent lanes reduce the 32-element block amax via
`shuffle_sync_bfly`, even lane writes the E8M0 scale), and `__init__`
gains a hidden-size row-alignment guard.
- Also fixes a stale pre-commit exclude left by the directory move
(`kernel_src/sm100/cutedsl_megamoe` → `kernel_src/cutedsl_megamoe`) so
hooks stop reformatting the verbatim `src/` tree.

Validated on 8x B200 (jobs 2384640/2384641/2384650), full matrix green
on **both** DSL versions:

| section | dsl 4.6.1 | dsl 4.7.0 |
|---|---|---|
| drop's own harness (`python -m src.inputs_process`: bit-exact scales +
SNR vs reference, nvfp4 offline/online + mxfp8) | 3/3 | 3/3 |
| `test_fused_quant_stage.py` | 11/11 | 11/11 |
| mega multirank x4 ranks (deep_gemm + nvfp4 + mxfp8) | 20/rank |
20/rank |
| single-rank kernel-vs-reference oracles | 6/6 | 6/6 |

The deep_gemm multirank suite previously crashed deterministically on
4.7.0; it now passes there. On the strength of this, the runbook's
temporary `==4.6.1` pin is lifted (see the DSL guidance bullet below).

## Also in this PR

- **Per-backend tuners.** `flashinfer/moe_ep/tune.py` becomes a pure CLI
shim (surface unchanged: `python -m flashinfer.moe_ep.tune`);
dtype-specific tuning moves into the backends
(`sm100/{nvfp4,mxfp8}.../tuner.py`), shared sweep machinery (dist
lifecycle, skewed restage, schedule grid, timed sweep tail) into
`backends/mega/kernel/tuning.py`.
- **CUTLASS DSL guidance updated (pin lifted).** The test-container
recipe briefly carried a hard `nvidia-cutlass-dsl==4.6.1` pin because
4.7.0 crashed the mega multirank path; with the crash root-caused and
fixed above, the runbook now allows `-U` installs again. 4.6.1 remains
the perf-validated reference (pin it when producing numbers meant to
compare against the TUNING.md tables); 4.7.0 is correctness-validated.
The library's supported floor remains 4.5.2 (the MR!27 WAR already in
main).
- **Per-cell bf16 term-magnitude tolerance band** for the mxfp8
multirank oracle compares — a principled per-cell bound derived from the
bf16 accumulation term magnitudes, replacing the global rtol that
produced rare single-cell false failures. Validated on GB200 and B200.
- **One-direction import layering rules** codified in the architecture
doc, with all `cutedsl_megamoe` access routed through the drop's
`__init__` rather than deep-path imports.

## Merge with upstream/main and follow-up fixes

The branch is merged up to upstream/main in two steps. First to aaf97df
(95 commits, incl. the v0.6.17 release line): conflict resolution keeps
the restructure spellings everywhere; upstream's one real kernel advance
in the moved tree — the 4fbac49 singleton-expert TMA-modes fix (#4296)
— is ported onto the renamed paths and recorded in `VENDOR.md`. Notable
upstream picks now in-tree: `BootstrapConfig.device` (#4348) and the
E_local=1 nvfp4 oracle regression test.

Second merge to 2febce5 (13 commits), resolving the conflicts created
when #4069 itself squash-merged upstream (f9b13ef) with the same moe_ep
files in the pre-restructure flat layout. Every conflict resolves to the
taxonomy spellings (upstream's side is the flat spelling of content this
branch already carries); upstream's flat
`backends/mega/kernel/sm90_push_fp8/` wrapper and its re-folding of
`sm90_push` into the multirank target are dropped in favor of this
branch's layout. The vendored push drop was re-diffed against the merged
SHA: byte-for-byte identical, no post-review deltas (recorded in
`VENDOR.md`).

Post-merge hardening found and fixed by full-suite runs:

- **Merge fallout:** auto-merged regions had re-introduced
pre-restructure `kernel_src.sm100.cutedsl_megamoe` spellings in 12
files, silently skipping entire GPU test files via `importorskip`;
restored, and upstream's re-added flat `sm90_pull_fp8/` wrapper removed.
- **FT test deadlocks (4xH100):** the fault-tolerance multirank test's
evicted victim ran a collective `destroy()` against the survivors'
barrier sequence, deadlocking until the NCCL watchdog — the victim tail
now mirrors the survivors' barrier→destroy→barrier shape. The FT smoke's
survivors now keep forwarding past the kill window so they actually
observe the fault, and `run_tests.sh` judges the smoke by counting
`SMOKE_RESULT` markers (torchrun interleaves lines).
- **Unit-suite crasher isolation:** the long-known in-suite-only
interpreter abort (heap corruption accumulating over the ~200-test
single-process run, firing during a plain module import or in CPython
teardown) is worked around by running the trigger test in its own pytest
process and exiting the unit invocations via `os._exit(pytest_rc)`;
rationale in the runbook, root cause tracked (needs ASAN). All tests
pass — this is process-teardown hygiene, not a kernel bug.

**CodeRabbit review responses.** Two rounds of actionable findings are
fixed in-branch (640b75f, 57926a9) — highlights from round 2: the push
packaging test's import-boundary gate was building the pre-taxonomy flat
backend path and passing vacuously (fixed, now validates all 5 wrapper
files); the test baseline's weight cache gains weakref eviction;
`cutedsl_megamoe/shim/__main__.py` added so the documented `python -m
...shim` commands resolve; the cutedsl_megamoe `VENDOR.md` provenance
TODOs are filled. Findings inside verbatim-vendored `kernel_src/**/src/`
trees are deliberately not patched locally — they route upstream per the
vendoring policy in `kernel_src/README.md`.

**Lint.** `pre-commit run -a` is fully green (clang-format, mypy, ruff
check/format, whitespace hooks). The final e9f791a is a pure
ruff-format pass over 13 moe_ep files — line wraps where the longer
taxonomy class names pushed calls past the limit. The vendored `src/`
trees are untouched by hooks (the exclude set holds).

## Backward compatibility

External callers keep working unchanged — both the old config-class
names and the old kernel_name strings remain as deprecated aliases:

- **Config classes**: `DeepGemmMegaMoeConfig`,
`Nvfp4CutedslMegaMoeConfig`, `Mxfp8CutedslMegaMoeConfig`,
`Sm90PullFp8MegaMoeConfig`, and `Sm90PushFp8MegaMoeConfig` are plain
aliases of the new `Sm<arch>_..._MegaMoeConfig` classes, defined (with a
removal note) in `flashinfer/moe_ep/__init__.py` right below the
taxonomy imports, and still exported via `__all__`.
- **Registry kernel_name strings**: `deep_gemm_mega`, `nvfp4_cutedsl`,
`mxfp8_cutedsl`, `sm90_pull_fp8`, and `sm90_push_fp8` resolve to the
taxonomy backends through the `deprecated_aliases=` parameter of each
backend's `@register_mega_kernel(...)` decoration; the resolution
machinery lives in `flashinfer/moe_ep/core/kernel/registry.py`. Using
one emits a `DeprecationWarning`, and aliases are excluded from the
available-kernels listing.
- Both alias families WILL BE REMOVED in a future release (noted at both
locations above).

## Testing

- Directory moves and renames are behavior-preserving by construction;
registry tests exercise both the taxonomy names and the deprecated
aliases (alias use warns; the kernel listing shows taxonomy names only).
- Full `run_tests.sh` matrix (all 12 targets) green on 4xH100 (job
2389821); B200 unit/registry/alias validation (jobs 2388315/2388326) —
see Test results above.
- The quant-staging sync validated on both dsl 4.6.1 and 4.7.0 (matrix
above); mxfp8/nvfp4 multirank oracle suites validated on GB200 and B200.
- The standalone MoE-EP microbenchmark was re-run against this branch
with no regressions vs the pre-restructure reference numbers (deep_gemm
parity; cutedsl kernels at or above their previous points).

## Relation to other PRs

Re-layering on top of #4113 (SM90 pull-style FP8 backend, merged) and
incorporating #4069 (SM90 push-style FP8 backend, merged upstream
2026-08-12; the vendored drop was re-diffed against the merged SHA
f9b13ef and is byte-identical). This is the base branch for the
upcoming backend-family PRs — SM100 BF16 (#4386) and SM120 MXFP8 — each
of which adds one taxonomy backend directory plus one provenance-keyed
kernel drop in the shape this restructure establishes. Both follow-up
branches are already rebased onto this branch's head (unit target green
on each), so they apply as exactly their backend-specific commits once
this merges.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Md Anik <mhoqueanik@cw-dfw-cs-001-login-01.cm.cluster>
Co-authored-by: Md Saidul Hoque Anik <mhoqueanik@login-preos01.a51.clusters.nvidia.com>
jefby pushed a commit to jefby/flashinfer that referenced this pull request Aug 19, 2026
…er-ai#4069)

## 📌 Description

This PR registers `sm90_push_fp8`, a whole-layer expert-parallel MoE
backend for SM90 GPUs connected through single-node NVLink, as a fourth
mega backend behind the existing `register_mega_kernel` plugin surface.

The public entry point is `MoEEpLayer` with `Sm90PushFp8MegaMoeConfig`.
Existing backend behavior is unchanged.

The steady-state forward path covers dispatch, FP8 block-scale expert
FFN, and combine without host-side synchronization, and supports CUDA
Graph capture after initialization. The diagram below shows the path
with all three optional optimizations enabled:

```text
bump_tag
  -> wait_acks
  -> deduplicated dispatch
  -> wait_prefix
  -> compact
  -> FC1 with fused SwiGLU + 1x128 quantization
  -> FC2
  -> grouped combine with owner-side FP32 route reduction
  -> BF16 reduction of received owner rows
  -> ack
```

The backend provides three independently configurable optimizations.
Validation and performance results below use all three unless stated
otherwise.

* **Deduplicated dispatch** stores one payload row per token and
destination rank instead of one per route. In the tested configurations,
with all other options fixed, its output matched the per-route path
under `torch.equal`. For `K=6`, `EP=4`, and random routing, it reduces
dispatch payload bytes by approximately 43%.
* **Grouped combine** performs FP32 route reduction on the owner rank
before pushing one quantized row per token. Its correctness gate
compares the final output against the same reference used by the
per-route path because the quantization point moves.
* **Fused FC1 epilogue** performs SwiGLU and 1x128 activation
quantization inside the FC1 DeepGEMM epilogue, eliminating an
approximately 1 GiB intermediate BF16 activation buffer per rank at the
DSV3 EP8 shape. In the tested configurations, with all other options
fixed, its output matched the unfused path under `torch.equal`. The
unfused path remains the default and maintained reference; the fused
path is opt-in.

## Motivation

The existing Hopper EP stack composes NCCL all-to-all with a local
fused-MoE operator, paying for additional launches, intermediate
buffers, and quantization boundaries.

`sm90_push_fp8` writes quantized payloads directly into peer-mapped
symmetric memory and feeds received rows directly into the expert GEMM
pipeline.

## Contracts introduced by this PR

* `capacity_factor` bounds the GEMM, TMA, and scale buffers, rather than
only the protocol window. The workspace query takes explicit
`expected_m` and `max_rows`, so block-M tactics and cubin keys remain
independent of the memory bound.
* Building the grouped GEMM requires CUDA Toolkit 12.8 or newer. The
generator explicitly rejects older toolkits, and AOT skips this module
below CUDA 12.8, allowing JIT-cache wheels to continue building on CUDA
12.6. Loading and running the generated module also requires a CUDA
runtime version 12.8 or newer.
* Runner initialization elects one NVCC-capable rank per cache and
tactic group to compile cold DeepGEMM cubins. Other ranks load the
resulting cubins from disk.

  * A warm cache works without NVCC.
* A cold cache without NVCC fails during initialization with an error
explaining how to select the fallback.
* `TRTLLM_DG_ENABLED=0` selects the fixed-tactic CUTLASS fallback on the
unfused path.
* Wait kernels use a `%globaltimer` deadline and publish a shared abort
marker before trapping. Peers polling the same protocol window observe
the marker and fail. Because a trap leaves a sticky CUDA error, the
process launcher must terminate the full rank group from the CPU.
* This backend is a stateful variant of the mega-kernel contract:

  * construction binds transformed static weights;
  * `stage_inputs` pre-binds the caller's output tensor.

The architecture and runbook documentation are updated accordingly,
including narrowing the two-entry wording to buffer-oriented kernels.
The `MegaKernelBackend` lifecycle itself is unchanged.

## Correctness

Full kernel acceptance ran on an 8× H800 NVLink node:

| Suite | Scale | Result |
| --------------------------------------------------------- |
--------------: | ---------------------------- |
| Single-GPU suite, 105 configurations | 1 GPU | all passed |
| Distributed suites | EP2 / EP4 / EP8 | all passed |
| Uneven token distributions, zero-token ranks, launch skew | EP2–EP8 |
all passed |
| CUDA Graph capture and replay | tested configurations | all passed |
| 200-round oracle-checked soaks | every tested configuration | all
passed |
| Eight-GPU acceptance matrix at DSV3 shapes | EP8 | all passed, no
skipped cases |

The final PR tree was then revalidated across three Hopper variants:

| Suite | Hardware | Result |
|
------------------------------------------------------------------------------
| -------------- | -------------------: |
| Single-GPU suite, all configurations | 1× H20 | 130 passed, 0 failed |
| `torchrun` EP2 | 2× H100 NVLink | 15 passed, 0 failed |
| `torchrun` EP4 | 4× H20 NVLink | 15 passed, 0 failed |
| `compute-sanitizer` memcheck / racecheck / initcheck, excluding trap
and soaks | 1× H20 | Clean |

Additional hardware-verified properties on the final PR tree:

* In the tested configurations, the fused FC1 output matched the unfused
reference under `torch.equal`.
* Leader deduplication was verified by counting NVCC invocations against
a shared cold cache: exactly one compilation per cubin across four
ranks.
* Warm-cache execution without NVCC was exercised end to end.
* Cold-cache fail-fast behavior was exercised end to end.
* The CUTLASS fallback was exercised end to end.

## Performance

Performance numbers were collected during kernel acceptance on the 8×
H800 node. The final PR tree was functionally revalidated as listed
above.

### Setup

* H800 NVLink
* CUDA 12.9
* PyTorch 2.8
* barrier-aligned timing
* maximum latency across ranks
* autotuned baseline tactics
* identical correctness gates on both sides
* `T` denotes live tokens per rank

The tables report `baseline latency / sm90_push_fp8 latency`; values
greater than `1×` favor this backend. `H`, `E`, and `K` denote hidden
size, expert count, and top-k routes. `random` distributes routes across
experts, while `hot1` concentrates routes on one expert.

### Versus NCCL all-to-all with the same FP8 block-scale compute

| Configuration           | Decode T64 | Throughput T2048 |
| ----------------------- | ---------: | ---------------: |
| EP4 SMALL, H4096 E32 K6 |   1.1–1.5× |         2.0–2.3× |
| EP4 DSV3, H7168 E256 K8 |      1.01× |         2.0–2.3× |

Against a wire-only NCCL transport reference that excludes compaction
and combine reduction, the push transport measured **1.9–3.2×** faster.

This number should be interpreted only as a comparison against that
specific measured reference. It is not a claim against third-party EP
libraries.

### Versus NCCL + `cutlass_fused_moe` FP8

| Configuration | Decode T64 | T2048 random |      T2048 hot1 |
| ------------- | ---------: | -----------: | --------------: |
| EP4 DSV3      |      1.06× |         2.3× |            2.0× |
| EP8 DSV3      |      1.10× |     2.2–2.4× |            1.6× |
| EP4 SMALL     |       3.1× |         2.1× | See limitations |

At EP1 and equal FP8 precision, `sm90_push_fp8` measured **2.3–3.4×**
faster than `cutlass_fused_moe` over `T64–T2048`.

### EP8 scaling

Configuration:

```text
DSV3
capacity = 2048
all three optimizations enabled
```

Results:

```text
T64:    0.758 ms
T2048:  3.238 ms
```

With capacity fixed at 2048, a 32× increase in live token count
increased latency by 4.27×.

Additional observations:

* no scheduling cliff near `T128`;
* all-remote routing adds approximately 5%;
* running 64 live tokens with buffers sized for 2048 adds approximately
0.7%.

## Limitations

* Supports:

  * SM90;
  * single-node, peer-accessible NVLink;
* protocol limit `ep_size <= 32`, with hardware validation in this PR
covering up to EP8;
  * `top_k` in `{1, 2, 4, 6, 8}`;
  * DeepSeek-style FP8 block scaling;
  * SwiGLU.

Unsupported configurations raise an explicit error rather than silently
falling back.

* The unfused FC1 path supports `intermediate_size <= 16384`; larger
configurations require `fuse_fc1_epilogue=True`.

* At DSV3 decode shapes, the advantage over NCCL + `cutlass_fused_moe`
is **1.06–1.10×**, because computation dominates.

* In the artificial SMALL `T2048` case where all `K` routes target one
expert, the backend is 25% slower than the autotuned CUTLASS stack.
Realistic `hot1` routing remains faster at EP4 and EP8.

* The fused FC1 epilogue does not yet have a small-M `swapAB` variant
and can be disabled independently below its break-even point.

* Round tags use `uint32`. Reuse after `2^32` forwards on one pipe is a
documented limit.

* Custom raw-stream execution through `bootstrap.stream` is rejected
explicitly rather than silently ignored.

## How to run

### Single-GPU and host-only suites

```bash
pytest \
  tests/moe_ep/test_sm90_push_fp8_kernel.py \
  tests/moe_ep/test_sm90_push_fp8_backend.py \
  tests/gemm/test_sm90_moe_gemm.py \
  tests/gemm/test_sm90_moe_gemm_contract.py
```

### Multi-rank tests

Requires at least two SM90 GPUs and uses `torch.distributed` only.

```bash
bash tests/moe_ep/run_tests.sh sm90_push
```

### Soak tests

```bash
SM90_PUSH_SOAK_ROUNDS=200 \
  bash tests/moe_ep/run_tests.sh sm90_push
```

### Benchmark

```bash
torchrun \
  --standalone \
  --nproc-per-node=8 \
  benchmarks/bench_sm90_push_megamoe.py \
  --config DSV3 \
  --dedup \
  --grouped-combine \
  --fuse-fc1 \
  --assert-cos-min 0.997
```

## 🔍 Related issues

This PR contributes the Hopper FP8 block-scale milestone under flashinfer-ai#3692 and
the SM90 sub-issue flashinfer-ai#3780, and follows the whole-layer integration
direction described in flashinfer-ai#3704.

### ✅ 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`.
- [ ] I have run the hooks manually with `pre-commit run --all-files`
and fixed any reported issues.

## 🧪 Tests

* [x] Tests added and updated.
* [x] Single-GPU suite passes.
* [x] EP2 distributed suite passes.
* [x] EP4 distributed suite passes.
* [x] CUDA Graph tests pass.
* [x] Soak tests pass on the listed hardware.

## Reviewer notes

Native NVFP4 and MXFP8 execution are out of scope because SM90 does not
provide native block-scaled tensor-core instructions for those formats.
A follow-up PR stacked on this one adds NVFP4 checkpoint support for
SM90 on top of this backend, through online W4A8 decode kernels and a
one-time requantization path that rides this FP8 backend unchanged.
The highest-value review areas are:



Entry points for the highest-risk areas:

* **Symmetric-window ordering and the acknowledgement protocol:**
  `kernel_src/sm90_push_megamoe/src/a2a/sm90_push_a2a_ops.cu`

* **Grouped-combine numerical behavior, where the quantization point
moves:**
The combine kernels in
`kernel_src/sm90_push_megamoe/src/a2a/sm90_push_a2a_ops.cu`, gated by
the reference comparison in `tests/moe_ep/test_sm90_push_fp8_kernel.py`

* **DeepGEMM fused-epilogue and workspace contracts:**
  `src/fp8_gemm/fp8_moe_fc1_fused.cuh``shim/gemm.py`

* **CUDA 12.8 gating and the AOT skip:**
  `shim/gemm.py``flashinfer/aot.py`

* **Collective initialization failure handling:**
  `shim/protocol.py`



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

* **New Features**
* Added SM90 FP8 MegaMoE support with pull- and push-style execution
paths.
* Added configurable FP8 formats, scaling modes, routing, reduction,
CUDA graph, and distributed execution options.
  * Added weight preprocessing and public configuration interfaces.
* Added benchmark tools for correctness, performance, token sweeps, and
peer-to-peer bandwidth.

* **Documentation**
* Expanded architecture guidance, setup instructions, tuning
information, and reproducibility runbooks.

* **Tests**
* Added broad correctness, validation, packaging, lifecycle,
distributed, and CUDA graph coverage.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
jefby pushed a commit to jefby/flashinfer that referenced this pull request Aug 19, 2026
…ush-style FP8 backend; sync CuTe-DSL 4.7 quant-staging fix (flashinfer-ai#4449)

## Summary

Three things: a layout/naming refactor of `flashinfer.moe_ep`'s
mega-kernel layer, the incorporation of the SM90 push-style FP8 backend
(flashinfer-ai#4069, since merged upstream) as the first new backend added in the
restructured shape, and one vendored-kernel sync that fixes the fused
activation-quant staging crash on CuTe-DSL 4.7 — un-blocking 4.7.x and
lifting the temporary `==4.6.1` pin. The branch is merged up to
upstream/main (2febce5, past the v0.6.17 line and the flashinfer-ai#4069 squash).
The refactor organizes the layer around two orthogonal views:

1. **Taxonomy (user view)** — backends move to
`backends/mega/kernel/sm<arch>/<act_dtype>_<weight_dtype>_<out_dtype>_<kernel_style>/`,
and registry `kernel_name` strings plus config classes carry the same
fully-qualified names. One glance at a name now tells you the
architecture, the activation/weight/output dtypes, and the kernel style:

   | old kernel_name | new kernel_name | new config class |
   |---|---|---|
| `deep_gemm_mega` | `sm100_fp8_fp4_bf16_deepgemm` |
`Sm100_Fp8_Fp4_Bf16_Deepgemm_MegaMoeConfig` |
| `nvfp4_cutedsl` | `sm100_nvfp4_nvfp4_bf16_cutedsl` |
`Sm100_Nvfp4_Nvfp4_Bf16_Cutedsl_MegaMoeConfig` |
| `mxfp8_cutedsl` | `sm100_mxfp8_mxfp8_bf16_cutedsl` |
`Sm100_Mxfp8_Mxfp8_Bf16_Cutedsl_MegaMoeConfig` |
| `sm90_pull_fp8` | `sm90_fp8_fp8_bf16_pull_cutedsl` |
`Sm90_Fp8_Fp8_Bf16_PullCutedsl_MegaMoeConfig` |
| `sm90_push_fp8` (new, from flashinfer-ai#4069) | `sm90_fp8_fp8_bf16_push_cuda` |
`Sm90_Fp8_Fp8_Bf16_PushCuda_MegaMoeConfig` |

Naming conventions: deep_gemm dtypes are plain `fp8`/`fp4`, matching
upstream `deep_gemm.fp8_fp4_mega_moe`; the mx/nv prefixes are reserved
for the cutedsl kernels' block-scaled formats. Output dtype is always
bf16 — nvfp4's `combine_dtype` is comm-wire compression, not an output
format.

2. **Provenance (kernel-dev view)** — vendored kernel sources in
`kernel_src/` are keyed by upstream repo snapshot, not by architecture:
`kernel_src/sm100/cutedsl_megamoe` moves to `kernel_src/cutedsl_megamoe`
(the mother repo ships kernels for multiple arches, so an smXX level
misrepresents it). Each drop mirrors the vendor repo layout — `src/`
byte-for-byte upstream, all adaptation in `shim/` — and gains a
`VENDOR.md` recording upstream repo/commit/sync state and pending local
diffs. A new `kernel_src/README.md` states the contract explicitly: **no
edits to `src/` of any kind — including docstrings, comments, and lint
fixes**; tool warnings against vendored files (docstring-coverage gates,
review bots) are handled by excluding the path, never by editing the
file. The sm90 fork trees
(`kernel_src/sm90/pull_style_cutedsl_megakernel` from flashinfer-ai#4113,
`kernel_src/sm90/push_style_megamoe` from flashinfer-ai#4069) intentionally stay
separate snapshots — one kernel_src dir = one upstream commit — and fold
into the mother drop if/when upstream merges them.

**Why:** verbatim snapshots must stay diffable against one upstream
commit, and splitting vendored trees per-dtype or per-arch breaks
re-sync; meanwhile users navigate by architecture and dtype, not by
which vendor repo a kernel came from. Putting each concern where its
audience looks resolves the tension. The layout rule is documented in
`docs/design_docs/moe_ep_architecture.md`, and it is what makes new
backend families routine — demonstrated in this very PR by the SM90
push-style incorporation below, and next by the follow-up backend-family
PRs (SM100 BF16 flashinfer-ai#4386, SM120 MXFP8).

## Directories affected

All changes live under `flashinfer/moe_ep/` plus its tests and docs:

-
`backends/mega/kernel/sm100/{fp8_fp4_bf16_deepgemm,nvfp4_nvfp4_bf16_cutedsl,mxfp8_mxfp8_bf16_cutedsl}/`
and
`backends/mega/kernel/sm90/{fp8_fp8_bf16_pull_cutedsl,fp8_fp8_bf16_push_cuda}/`
— taxonomy backend wrappers (moved/renamed; push_cuda is new).
- `kernel_src/cutedsl_megamoe/` (moved from
`kernel_src/sm100/cutedsl_megamoe/`),
`kernel_src/sm90/pull_style_cutedsl_megakernel/`,
`kernel_src/sm90/push_style_megamoe/` (new) — provenance-keyed vendored
drops, each with `VENDOR.md`; new `kernel_src/README.md` states the
no-edits contract.
- `backends/mega/kernel/tuning.py` + per-backend `tuner.py` files —
tuning machinery moved out of `tune.py` (now a CLI shim).
- `core/kernel/registry.py`, `moe_ep/__init__.py` — deprecated-alias
resolution and re-exports.
- `tests/moe_ep/`, `docs/design_docs/moe_ep_{architecture,runbook}.md`,
`pyproject.toml`/`.pre-commit-config.yaml` excludes, `run_tests.sh` (new
2-GPU `sm90_push` target).

## Test results

- **Full `run_tests.sh` matrix — all 12 targets green** on 4xH100 (job
2389821, 2026-08-13), including the new `sm90_push` Hopper target and
the fault-tolerance suites after the deadlock fixes.
- **B200** (jobs 2388315/2388326): registry/alias smoke, deprecated
aliases, unit x3 green — 396 passed / 72 skipped (push
cpu/packaging/contract tests run; Hopper-marked kernel tests skip).
- **Unit target re-validated green** after the second upstream merge
(job 2389880) and again after the round-2 CodeRabbit fixes (job
2389916), same 396/72 counts, B200.
- **8x B200** (jobs 2384640/2384641/2384650): quant-staging sync matrix
fully green on both dsl 4.6.1 and 4.7.0 (details in the vendored-sync
section below).
- **GB200 + B200**: mxfp8/nvfp4 multirank oracle suites with the
per-cell tolerance band.
- Microbenchmark re-run: no regressions vs pre-restructure reference
numbers (deep_gemm parity; cutedsl kernels at or above their previous
points).
- `pre-commit run -a` fully green at the branch head (e9f791a).

## SM90 push-style FP8 backend (incorporates flashinfer-ai#4069)

Ports flashinfer-ai#4069 (head 301f8ce; since merged to main
as f9b13ef — re-diffed, byte-identical, no post-review deltas) onto the
taxonomy/provenance layout, serving as the first proof of the "one
taxonomy backend dir + one provenance-keyed kernel drop" recipe:

- **`kernel_src/sm90/push_style_megamoe/`** — verbatim byte-for-byte
drop from the PR head (`src/{a2a,fp8_gemm}` CUDA sources, `shim/`,
ACKNOWLEDGEMENT.md) plus a `VENDOR.md` provenance record.
- **`backends/mega/kernel/sm90/fp8_fp8_bf16_push_cuda/`** — the five
wrapper files relocated from upstream's flat `kernel/sm90_push_fp8/`,
config renamed to `Sm90_Fp8_Fp8_Bf16_PushCuda_MegaMoeConfig`, registered
with `deprecated_aliases=("sm90_push_fp8",)`.
- **Core deltas carried from the PR:** `mega_layer.py` allocates the
output before `stage_inputs`; pyproject package-data ships the drop's
`.cu`/`.cuh` for non-editable installs; the `isolated_deep_gemm_cache`
conftest fixture; the mega-layer allocation-order regression test.
- **Tests:** the nine sm90_push_fp8 test files (names kept to minimize
re-sync friction) rewritten to the taxonomy. Deviation from upstream:
`run_tests.sh` exposes `sm90_push` as its own 2-GPU Hopper target
instead of folding it into multirank — on non-Hopper nodes the
arch-marked files collect 0 tests and torchrun turns pytest exit 5 into
a failure.

## CuTe-DSL 4.7 quant-staging fix (vendored sync)

The `CUDA_ERROR_MISALIGNED_ADDRESS` crash on cutlass-dsl 4.7.0 — which
presented as a deep_gemm mega multirank failure — was root-caused to the
**fused bf16→quantized activation staging** (`DataPreprocess` in the
vendored cutedsl_megamoe tree), which every mega staging path shares,
deep_gemm's included. The kernel team's fix is synced in as a
single-file partial re-sync per the vendoring policy:

- `kernel_src/cutedsl_megamoe/src/src/inputs_process.py` +
`src/common/host_utils.py` taken **verbatim** from upstream
`bangyus/cutedsl_megamoe @ 50117315d`, recorded in `VENDOR.md` under
pending-diffs (resolves at the next full re-sync). The mxfp8 quant
kernel is reworked so each lane owns one contiguous 16-byte fp8 store
(adjacent lanes reduce the 32-element block amax via
`shuffle_sync_bfly`, even lane writes the E8M0 scale), and `__init__`
gains a hidden-size row-alignment guard.
- Also fixes a stale pre-commit exclude left by the directory move
(`kernel_src/sm100/cutedsl_megamoe` → `kernel_src/cutedsl_megamoe`) so
hooks stop reformatting the verbatim `src/` tree.

Validated on 8x B200 (jobs 2384640/2384641/2384650), full matrix green
on **both** DSL versions:

| section | dsl 4.6.1 | dsl 4.7.0 |
|---|---|---|
| drop's own harness (`python -m src.inputs_process`: bit-exact scales +
SNR vs reference, nvfp4 offline/online + mxfp8) | 3/3 | 3/3 |
| `test_fused_quant_stage.py` | 11/11 | 11/11 |
| mega multirank x4 ranks (deep_gemm + nvfp4 + mxfp8) | 20/rank |
20/rank |
| single-rank kernel-vs-reference oracles | 6/6 | 6/6 |

The deep_gemm multirank suite previously crashed deterministically on
4.7.0; it now passes there. On the strength of this, the runbook's
temporary `==4.6.1` pin is lifted (see the DSL guidance bullet below).

## Also in this PR

- **Per-backend tuners.** `flashinfer/moe_ep/tune.py` becomes a pure CLI
shim (surface unchanged: `python -m flashinfer.moe_ep.tune`);
dtype-specific tuning moves into the backends
(`sm100/{nvfp4,mxfp8}.../tuner.py`), shared sweep machinery (dist
lifecycle, skewed restage, schedule grid, timed sweep tail) into
`backends/mega/kernel/tuning.py`.
- **CUTLASS DSL guidance updated (pin lifted).** The test-container
recipe briefly carried a hard `nvidia-cutlass-dsl==4.6.1` pin because
4.7.0 crashed the mega multirank path; with the crash root-caused and
fixed above, the runbook now allows `-U` installs again. 4.6.1 remains
the perf-validated reference (pin it when producing numbers meant to
compare against the TUNING.md tables); 4.7.0 is correctness-validated.
The library's supported floor remains 4.5.2 (the MR!27 WAR already in
main).
- **Per-cell bf16 term-magnitude tolerance band** for the mxfp8
multirank oracle compares — a principled per-cell bound derived from the
bf16 accumulation term magnitudes, replacing the global rtol that
produced rare single-cell false failures. Validated on GB200 and B200.
- **One-direction import layering rules** codified in the architecture
doc, with all `cutedsl_megamoe` access routed through the drop's
`__init__` rather than deep-path imports.

## Merge with upstream/main and follow-up fixes

The branch is merged up to upstream/main in two steps. First to aaf97df
(95 commits, incl. the v0.6.17 release line): conflict resolution keeps
the restructure spellings everywhere; upstream's one real kernel advance
in the moved tree — the 4fbac49 singleton-expert TMA-modes fix (flashinfer-ai#4296)
— is ported onto the renamed paths and recorded in `VENDOR.md`. Notable
upstream picks now in-tree: `BootstrapConfig.device` (flashinfer-ai#4348) and the
E_local=1 nvfp4 oracle regression test.

Second merge to 2febce5 (13 commits), resolving the conflicts created
when flashinfer-ai#4069 itself squash-merged upstream (f9b13ef) with the same moe_ep
files in the pre-restructure flat layout. Every conflict resolves to the
taxonomy spellings (upstream's side is the flat spelling of content this
branch already carries); upstream's flat
`backends/mega/kernel/sm90_push_fp8/` wrapper and its re-folding of
`sm90_push` into the multirank target are dropped in favor of this
branch's layout. The vendored push drop was re-diffed against the merged
SHA: byte-for-byte identical, no post-review deltas (recorded in
`VENDOR.md`).

Post-merge hardening found and fixed by full-suite runs:

- **Merge fallout:** auto-merged regions had re-introduced
pre-restructure `kernel_src.sm100.cutedsl_megamoe` spellings in 12
files, silently skipping entire GPU test files via `importorskip`;
restored, and upstream's re-added flat `sm90_pull_fp8/` wrapper removed.
- **FT test deadlocks (4xH100):** the fault-tolerance multirank test's
evicted victim ran a collective `destroy()` against the survivors'
barrier sequence, deadlocking until the NCCL watchdog — the victim tail
now mirrors the survivors' barrier→destroy→barrier shape. The FT smoke's
survivors now keep forwarding past the kill window so they actually
observe the fault, and `run_tests.sh` judges the smoke by counting
`SMOKE_RESULT` markers (torchrun interleaves lines).
- **Unit-suite crasher isolation:** the long-known in-suite-only
interpreter abort (heap corruption accumulating over the ~200-test
single-process run, firing during a plain module import or in CPython
teardown) is worked around by running the trigger test in its own pytest
process and exiting the unit invocations via `os._exit(pytest_rc)`;
rationale in the runbook, root cause tracked (needs ASAN). All tests
pass — this is process-teardown hygiene, not a kernel bug.

**CodeRabbit review responses.** Two rounds of actionable findings are
fixed in-branch (640b75f, 57926a9) — highlights from round 2: the push
packaging test's import-boundary gate was building the pre-taxonomy flat
backend path and passing vacuously (fixed, now validates all 5 wrapper
files); the test baseline's weight cache gains weakref eviction;
`cutedsl_megamoe/shim/__main__.py` added so the documented `python -m
...shim` commands resolve; the cutedsl_megamoe `VENDOR.md` provenance
TODOs are filled. Findings inside verbatim-vendored `kernel_src/**/src/`
trees are deliberately not patched locally — they route upstream per the
vendoring policy in `kernel_src/README.md`.

**Lint.** `pre-commit run -a` is fully green (clang-format, mypy, ruff
check/format, whitespace hooks). The final e9f791a is a pure
ruff-format pass over 13 moe_ep files — line wraps where the longer
taxonomy class names pushed calls past the limit. The vendored `src/`
trees are untouched by hooks (the exclude set holds).

## Backward compatibility

External callers keep working unchanged — both the old config-class
names and the old kernel_name strings remain as deprecated aliases:

- **Config classes**: `DeepGemmMegaMoeConfig`,
`Nvfp4CutedslMegaMoeConfig`, `Mxfp8CutedslMegaMoeConfig`,
`Sm90PullFp8MegaMoeConfig`, and `Sm90PushFp8MegaMoeConfig` are plain
aliases of the new `Sm<arch>_..._MegaMoeConfig` classes, defined (with a
removal note) in `flashinfer/moe_ep/__init__.py` right below the
taxonomy imports, and still exported via `__all__`.
- **Registry kernel_name strings**: `deep_gemm_mega`, `nvfp4_cutedsl`,
`mxfp8_cutedsl`, `sm90_pull_fp8`, and `sm90_push_fp8` resolve to the
taxonomy backends through the `deprecated_aliases=` parameter of each
backend's `@register_mega_kernel(...)` decoration; the resolution
machinery lives in `flashinfer/moe_ep/core/kernel/registry.py`. Using
one emits a `DeprecationWarning`, and aliases are excluded from the
available-kernels listing.
- Both alias families WILL BE REMOVED in a future release (noted at both
locations above).

## Testing

- Directory moves and renames are behavior-preserving by construction;
registry tests exercise both the taxonomy names and the deprecated
aliases (alias use warns; the kernel listing shows taxonomy names only).
- Full `run_tests.sh` matrix (all 12 targets) green on 4xH100 (job
2389821); B200 unit/registry/alias validation (jobs 2388315/2388326) —
see Test results above.
- The quant-staging sync validated on both dsl 4.6.1 and 4.7.0 (matrix
above); mxfp8/nvfp4 multirank oracle suites validated on GB200 and B200.
- The standalone MoE-EP microbenchmark was re-run against this branch
with no regressions vs the pre-restructure reference numbers (deep_gemm
parity; cutedsl kernels at or above their previous points).

## Relation to other PRs

Re-layering on top of flashinfer-ai#4113 (SM90 pull-style FP8 backend, merged) and
incorporating flashinfer-ai#4069 (SM90 push-style FP8 backend, merged upstream
2026-08-12; the vendored drop was re-diffed against the merged SHA
f9b13ef and is byte-identical). This is the base branch for the
upcoming backend-family PRs — SM100 BF16 (flashinfer-ai#4386) and SM120 MXFP8 — each
of which adds one taxonomy backend directory plus one provenance-keyed
kernel drop in the shape this restructure establishes. Both follow-up
branches are already rebased onto this branch's head (unit target green
on each), so they apply as exactly their backend-specific commits once
this merges.

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Md Anik <mhoqueanik@cw-dfw-cs-001-login-01.cm.cluster>
Co-authored-by: Md Saidul Hoque Anik <mhoqueanik@login-preos01.a51.clusters.nvidia.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.

7 participants