refactor(moe): enforce build() for all runners and refine CUTLASS autotuning in unified MoE API - #4376
Merged
feih-nv merged 3 commits intoAug 12, 2026
Conversation
Contributor
|
Caution Review failedAn error occurred during the review process. Please try again later. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
feih-nv
force-pushed
the
feih/unified-moe-runner-build-lifecycle
branch
from
August 6, 2026 09:54
2f77ea3 to
fc61572
Compare
feih-nv
force-pushed
the
feih/unified-moe-runner-build-lifecycle
branch
from
August 7, 2026 03:33
fc61572 to
f666f9c
Compare
3 tasks
Retain the fastest tactics from each GEMM stage so the outer tuner can evaluate a bounded set of end-to-end combinations instead of assuming independent winners compose optimally. Developed with AI assistance.
feih-nv
force-pushed
the
feih/unified-moe-runner-build-lifecycle
branch
from
August 10, 2026 06:42
c9cb3bb to
59dfdba
Compare
Keep complete process-local tactic rankings so warm and persisted winner caches retain compound refinement, and repair the CuTe DSL build lifecycle configuration access.
feih-nv
marked this pull request as ready for review
August 10, 2026 09:20
feih-nv
requested review from
Aneureka,
IwakuraRein,
StudyingShao,
aleozlx,
bkryu,
jiahanc,
nv-yunzheq,
qiching,
samuellees and
yzh119
as code owners
August 10, 2026 09:20
Collaborator
Author
|
/bot run /tests/moe |
Collaborator
Collaborator
|
[FAILED] Pipeline #61933729 — 9/18 executed test jobs passed Compared with nightly #61930115. Unit Tests
✅ Pass · 🟡 Old failure · ❌ New failure · ⏱ Test timeout · Multi-GPU and Multi-Node Tests — 6/6 passed
Failure detailsNew relative to nightly (attribution uncertain)
Pre-existing failures
Timeouts, infrastructure, or incomplete jobs
|
aleozlx
reviewed
Aug 12, 2026
aleozlx
reviewed
Aug 12, 2026
aleozlx
left a comment
Member
There was a problem hiding this comment.
code review: lgtm. minor comment posted
aleozlx
approved these changes
Aug 12, 2026
feih-nv
added a commit
that referenced
this pull request
Aug 13, 2026
## 📌 Description Adds non-EP fused shared experts to the unified TRTLLM-generated MoE API for DeepSeekV3 `FromLogits` routing. The unified block-FP8 runner supports DeepSeekFp8 and MxFp8, while the unified FP4 runner supports NVFP4, MXFP4, and W4A16. The underlying kernels already append shared expert IDs `E + k` with weight `1.0`; this PR completes the unified API integration: - Adds `ExpertConfig.num_fused_shared_experts` and validates the shared-expert contract: DeepSeekV3 routing, `K + S <= 32`, `E + S <= 512`, and no expert parallelism. - Updates `TrtllmFp8BlockRunner` for DeepSeekFp8 and MxFp8 and `TrtllmFp4RoutedRunner` for NVFP4, MXFP4, and W4A16 to accept `E + S` physical expert rows and forward `S` to the kernel. - Allocates `K + S` caller-owned routing outputs for FP4 in-kernel routing while keeping `ExpertConfig.local_num_experts` routed-only; block-FP8 instead passes empty placeholders and uses kernel-internal routing storage. - Requires explicit backend opt-in so unsupported runners reject `S > 0` through the mandatory `check_support()` lifecycle. - Adds separate block-FP8 and FP4 trace templates for `S > 0`, preserving routed `E`, physical `E + S`, and shared `S` as distinct geometry. - Fixes unified MoE autotune cache identity so in-memory and persisted keys include tactic-relevant expert geometry, routing, activation, and quantization state. ### Why the prerequisites are needed **Autotune cache keying (`c5cbe0c38`, `b4eb94bd5`).** Expert geometry is stored outside the profiled tensor list, so configurations differing only in `S` could otherwise share a tactic even though enumeration depends on `top_k + S`. Persisted keys also omit `runner_hash`. These changes introduce a shared `_cache_key_extras()` for tactic-relevant geometry, routing, activation, and quantization state and remove all runner-specific `__hash__()` overrides so every unified MoE runner follows the same cache-key contract. CUTLASS and CuTeDSL override `_cache_key_extras()` and call `super()` to append architecture, PDL, and inner-runner state without dropping the common fields. **Trace artifact refresh (`ec50a94b8`).** The committed block-FP8 DeepSeek artifact had drifted after the SwiGLU OA inputs were added, and its rendered reference omitted `_fp8_moe_run_experts`. Refreshing it separately establishes a clean baseline for the shared-expert artifacts. **Runner lifecycle ([#4376](#4376 `check_support()` is mandatory before `build()`, and packing or execution requires a built runner. Unsupported `S > 0` configurations are therefore rejected centrally without duplicating guards in every `pack_inputs()`. ## 🚀 Pull Request Checklist ### ✅ Pre-commit Checks - [x] I have installed `pre-commit` by running `pip install pre-commit` (or used my preferred method). - [x] I have installed the hooks with `pre-commit install`. - [x] I have run the hooks manually with `pre-commit run --all-files` and fixed any reported issues. ## 🧪 Tests Coverage added: - Numerical parity with the legacy flat API for DeepSeekFp8 at `S=1`, MxFp8 at `S=2`, NVFP4 at `S=1`, MXFP4 at `S=2`, and W4A16 at `S=1`. - DeepSeekFp8 `S=1` CUDA-graph capture and replay. - Configuration boundaries, unsupported-backend rejection, and in-memory and persisted autotune cache-key coverage. - Focused block-FP8 and FP4 trace dispatch, naming, geometry validation, generated artifacts, and curated `S > 0` fuzzer cases. Validation completed: - SM100: `tests/trace/` plus unified MoE and cache-key coverage — 1341 passed, 179 skipped, 0 failed before the final test-pruning and lifecycle-alignment edits. - SM103 (B300): focused block-FP8 shared-expert selection — 102 passed, 3 skipped, plus the DeepSeekFp8 and MxFp8 curated fuzzer cases. - Focused FP4 shared-expert tests in the CUDA container — 8 passed, including NVFP4, MXFP4, and W4A16 parity; all three curated FP4 fuzzer cases passed. - Focused cache-key integration tests for the unified contract, CUTLASS, and CuTeDSL — 3 passed. ## Reviewer Notes and Follow-ups - **Pre-routed inputs and routing replay:** callers can append shared IDs `E + k` and weights `1.0` themselves. Adding them inside FlashInfer would require re-striding `[T, K]` routing arrays to `[T, K + S]` or carrying dual-stride semantics through the post-routing pipeline. The replay buffer remains routed-only `[T, K]`, while the shared-expert routing kernel currently indexes it with the internal `K + S` stride, so replay is disabled for `S > 0`. Follow-up: fix the flat block-FP8 and FP4 replay paths while preserving the routed-only output contract. - **Cache invalidation:** the persisted-key change causes existing on-disk autotune entries to miss once and re-tune. This is benign but user-visible. - **EP remains unsupported:** the kernel maps a shared global ID to a weight row using `global_id - local_expert_offset`, which is valid only when the rank holds the full routed expert set. - **Physical versus routed rows:** `prepare_weights(num_local_experts=...)` takes the physical row count `E + S`, while `ExpertConfig.local_num_experts` remains routed-only `E`. - **Mixed quantization:** the current runners require homogeneous expert rows and therefore do not yet cover checkpoints such as `dsv4-flash-nvfp4`, whose routed experts use NVFP4 while shared experts use block-FP8. An initial implementation can compose an NVFP4 routed launch with a block-FP8 shared-expert launch and sum their BF16 outputs; a true single-launch implementation requires upstream kernel ABI and cubin support for separate routed/shared weight pointers, scale layouts, and dtypes. - **Test oracle:** shared-expert numerical tests compare with the legacy flat APIs because the existing self-fused pre-routed oracle cannot represent the important `S=1` and `S=2` cases under its expert-count divisibility constraints. - **Tracing:** artifacts were regenerated directly because `tests/trace/example.py` currently aborts in `recurrent_kda` with `nvidia-cutlass-dsl` 4.5.2. Follow-ups are to make Trace Apply select the concrete dispatched template per call, register reference-helper dependencies transitively and execute emitted functions in standalone tests, then repair example generation and add CI that regenerates `fi_trace_out` from an empty directory and rejects drift.
jefby
pushed a commit
to jefby/flashinfer
that referenced
this pull request
Aug 19, 2026
…otuning in unified MoE API (flashinfer-ai#4376) ## 📌 Description Enforce an explicit `check_support() -> build() -> execute` lifecycle for all registered unified MoE runners, and refine CUTLASS compound-tactic autotuning. ### Runner lifecycle - Centralize check_support() → build() → execute enforcement in MoERunner. - Make builds idempotent and defer backend initialization until support validation succeeds. - Keep shape-dependent TRTLLM and b12x inner runners lazy. - Update direct-runner callers and add lifecycle regression tests. ### CUTLASS staged autotuning - Add AutoTuner.rank_tactics() for ranked stage candidates. - Retain the top two tactics for GEMM1 and GEMM2, then profile their four combinations end to end. - Cache complete ranked shortlists in-process while preserving the existing persistent winner format. - Rebuild shortlists from persisted winners when tuning resumes. - Select the correct dynamic optimization profile before ranking. This reduces the search cost from O(n1 × n2) to O(n1 + n2 + k²) while improving compound tactic selection. ## 🔍 Related Issues - Follow up tasks for CUTLASS BF16/W4A16 runners merged in flashinfer-ai#4328. ## 🚀 Pull Request Checklist ### ✅ Pre-commit Checks - [x] I have installed `pre-commit` by running `pip install pre-commit` (or used my preferred method). - [x] I have installed the hooks with `pre-commit install`. - [x] I have run the hooks manually with `pre-commit run --all-files` and fixed any reported issues. ## 🧪 Tests Focused validation: - 496 passed - 31 skipped - All changed autotuner and unified MoE test files passed ## Reviewer Notes - `MoELayer` now validates each runner with `check_support()` before calling its idempotent `build()` method. Direct-runner users must perform the same lifecycle explicitly. - TRTLLM and b12x inner runners remain lazily constructed because their configuration depends on runtime shapes. - CUTLASS staged ranking is currently orchestrated by get_valid_tactics(); a future autotuner abstraction could model multi-stage tuning declaratively.
jefby
pushed a commit
to jefby/flashinfer
that referenced
this pull request
Aug 19, 2026
## 📌 Description Adds non-EP fused shared experts to the unified TRTLLM-generated MoE API for DeepSeekV3 `FromLogits` routing. The unified block-FP8 runner supports DeepSeekFp8 and MxFp8, while the unified FP4 runner supports NVFP4, MXFP4, and W4A16. The underlying kernels already append shared expert IDs `E + k` with weight `1.0`; this PR completes the unified API integration: - Adds `ExpertConfig.num_fused_shared_experts` and validates the shared-expert contract: DeepSeekV3 routing, `K + S <= 32`, `E + S <= 512`, and no expert parallelism. - Updates `TrtllmFp8BlockRunner` for DeepSeekFp8 and MxFp8 and `TrtllmFp4RoutedRunner` for NVFP4, MXFP4, and W4A16 to accept `E + S` physical expert rows and forward `S` to the kernel. - Allocates `K + S` caller-owned routing outputs for FP4 in-kernel routing while keeping `ExpertConfig.local_num_experts` routed-only; block-FP8 instead passes empty placeholders and uses kernel-internal routing storage. - Requires explicit backend opt-in so unsupported runners reject `S > 0` through the mandatory `check_support()` lifecycle. - Adds separate block-FP8 and FP4 trace templates for `S > 0`, preserving routed `E`, physical `E + S`, and shared `S` as distinct geometry. - Fixes unified MoE autotune cache identity so in-memory and persisted keys include tactic-relevant expert geometry, routing, activation, and quantization state. ### Why the prerequisites are needed **Autotune cache keying (`c5cbe0c38`, `b4eb94bd5`).** Expert geometry is stored outside the profiled tensor list, so configurations differing only in `S` could otherwise share a tactic even though enumeration depends on `top_k + S`. Persisted keys also omit `runner_hash`. These changes introduce a shared `_cache_key_extras()` for tactic-relevant geometry, routing, activation, and quantization state and remove all runner-specific `__hash__()` overrides so every unified MoE runner follows the same cache-key contract. CUTLASS and CuTeDSL override `_cache_key_extras()` and call `super()` to append architecture, PDL, and inner-runner state without dropping the common fields. **Trace artifact refresh (`ec50a94b8`).** The committed block-FP8 DeepSeek artifact had drifted after the SwiGLU OA inputs were added, and its rendered reference omitted `_fp8_moe_run_experts`. Refreshing it separately establishes a clean baseline for the shared-expert artifacts. **Runner lifecycle ([flashinfer-ai#4376](flashinfer-ai#4376 `check_support()` is mandatory before `build()`, and packing or execution requires a built runner. Unsupported `S > 0` configurations are therefore rejected centrally without duplicating guards in every `pack_inputs()`. ## 🚀 Pull Request Checklist ### ✅ Pre-commit Checks - [x] I have installed `pre-commit` by running `pip install pre-commit` (or used my preferred method). - [x] I have installed the hooks with `pre-commit install`. - [x] I have run the hooks manually with `pre-commit run --all-files` and fixed any reported issues. ## 🧪 Tests Coverage added: - Numerical parity with the legacy flat API for DeepSeekFp8 at `S=1`, MxFp8 at `S=2`, NVFP4 at `S=1`, MXFP4 at `S=2`, and W4A16 at `S=1`. - DeepSeekFp8 `S=1` CUDA-graph capture and replay. - Configuration boundaries, unsupported-backend rejection, and in-memory and persisted autotune cache-key coverage. - Focused block-FP8 and FP4 trace dispatch, naming, geometry validation, generated artifacts, and curated `S > 0` fuzzer cases. Validation completed: - SM100: `tests/trace/` plus unified MoE and cache-key coverage — 1341 passed, 179 skipped, 0 failed before the final test-pruning and lifecycle-alignment edits. - SM103 (B300): focused block-FP8 shared-expert selection — 102 passed, 3 skipped, plus the DeepSeekFp8 and MxFp8 curated fuzzer cases. - Focused FP4 shared-expert tests in the CUDA container — 8 passed, including NVFP4, MXFP4, and W4A16 parity; all three curated FP4 fuzzer cases passed. - Focused cache-key integration tests for the unified contract, CUTLASS, and CuTeDSL — 3 passed. ## Reviewer Notes and Follow-ups - **Pre-routed inputs and routing replay:** callers can append shared IDs `E + k` and weights `1.0` themselves. Adding them inside FlashInfer would require re-striding `[T, K]` routing arrays to `[T, K + S]` or carrying dual-stride semantics through the post-routing pipeline. The replay buffer remains routed-only `[T, K]`, while the shared-expert routing kernel currently indexes it with the internal `K + S` stride, so replay is disabled for `S > 0`. Follow-up: fix the flat block-FP8 and FP4 replay paths while preserving the routed-only output contract. - **Cache invalidation:** the persisted-key change causes existing on-disk autotune entries to miss once and re-tune. This is benign but user-visible. - **EP remains unsupported:** the kernel maps a shared global ID to a weight row using `global_id - local_expert_offset`, which is valid only when the rank holds the full routed expert set. - **Physical versus routed rows:** `prepare_weights(num_local_experts=...)` takes the physical row count `E + S`, while `ExpertConfig.local_num_experts` remains routed-only `E`. - **Mixed quantization:** the current runners require homogeneous expert rows and therefore do not yet cover checkpoints such as `dsv4-flash-nvfp4`, whose routed experts use NVFP4 while shared experts use block-FP8. An initial implementation can compose an NVFP4 routed launch with a block-FP8 shared-expert launch and sum their BF16 outputs; a true single-launch implementation requires upstream kernel ABI and cubin support for separate routed/shared weight pointers, scale layouts, and dtypes. - **Test oracle:** shared-expert numerical tests compare with the legacy flat APIs because the existing self-fused pre-routed oracle cannot represent the important `S=1` and `S=2` cases under its expert-count divisibility constraints. - **Tracing:** artifacts were regenerated directly because `tests/trace/example.py` currently aborts in `recurrent_kda` with `nvidia-cutlass-dsl` 4.5.2. Follow-ups are to make Trace Apply select the concrete dispatched template per call, register reference-helper dependencies transitively and execute emitted functions in standalone tests, then repair example generation and add CI that regenerates `fi_trace_out` from an empty directory and rejects drift.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
📌 Description
Enforce an explicit
check_support() -> build() -> executelifecycle for all registered unified MoE runners, and refine CUTLASS compound-tactic autotuning.Runner lifecycle
CUTLASS staged autotuning
This reduces the search cost from O(n1 × n2) to O(n1 + n2 + k²) while improving compound tactic selection.
🔍 Related Issues
🚀 Pull Request Checklist
✅ Pre-commit Checks
pre-commitby runningpip install pre-commit(or used my preferred method).pre-commit install.pre-commit run --all-filesand fixed any reported issues.🧪 Tests
Focused validation:
Reviewer Notes
MoELayernow validates each runner withcheck_support()before calling its idempotentbuild()method. Direct-runner users must perform the same lifecycle explicitly.