Repository navigation
Conversation
ep.cuda.moe_gemv_max_expanded_rows caps the routed row count (tokens x k) for which QMoE runs its INT2/INT4/INT8 MoE GEMV. Larger counts take the prefill path, which is the grouped GEMM for INT4/INT8. The value is an integer in [0, 80]: 0 never uses the GEMV, the default of 80 keeps the current routing, and any other value fails session creation. QMoE reads the option once at kernel construction and hands it to the runner through QuantParams. There it joins the existing FC1 and FC2 GEMV disable terms, so the skip-expand prediction in runMoe and the FC1 launch in gemm1 keep making the same decision. gemm1 also passes the split-K SwiGLU partials to the GEMV only up to kMoeGemvSplitKSwiGLUMaxRows, the constant getWorkspaceDeviceBufferSizes now uses to size them, so the buffer and its consumer cannot drift apart. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This branch has not been deployed
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
Adds ep.cuda.moe_gemv_max_expanded_rows, a CUDA session option that caps the routed row count, tokens x k, for which QMoE runs its INT2/INT4/INT8 MoE GEMV. Larger counts take the prefill path, which is the grouped GEMM for INT4/INT8. The value is an integer in [0, 80]: 0 never uses the GEMV, and the default of 80 (moe_gemv::kMaxProfiledExpandedRows, the GEMV's own limit) keeps today's routing. QMoE reads it once at kernel construction, and any other value fails session creation.
The value reaches the runner through QuantParams and joins the existing FC1 and FC2 GEMV disable terms (MoeGemvFc1Disabled and fc2_gemv_disabled), so the skip-expand prediction in runMoe and the FC1 launch in gemm1 keep making the same decision. QMoE's packed INT2/mixed-width GEMV checks it as well. The INT3 and FP4 decode kernels have their own selectors and are not affected.
This also adds the guard suggested in #34168 (comment): gemm1 hands the split-K SwiGLU partials to the GEMV only up to kMoeGemvSplitKSwiGLUMaxRows, the constant getWorkspaceDeviceBufferSizes now uses to size them, so the buffer and its consumer cannot drift apart. The buffer stays sized for 80 rows whatever the option says.
Motivation and Context
Follow-up to #34168, which capped the split-K partials at the GEMV's 80-row limit: should that limit be configurable, does prefill use the GEMV, and should prefill go to the GEMM instead?
Prefill uses the GEMV only when tokens x k <= 80. For Qwen3-30B-A3B (top-8) that means calls of at most 10 tokens, such as very short prompts, small decode batches or speculative verification; 11 or more tokens already run the grouped GEMM.
Measured on A100 80GB (SM80) with layer 0 of Qwen3-30B-A3B-Instruct-2507 as a standalone QMoE model (FP16, INT4 block 128, 128 experts, router included), IOBinding, sessions in A-B-B-A order with 200 timed calls per token count each. Median microseconds per call, limit 80 (current routing) vs limit 0 (grouped GEMM everywhere):
End to end with the full Qwen3-30B-A3B-Instruct-2507 through ORT GenAI on the same GPU (30 requests per prompt length and setting, A-B-B-A processes), prefill of 2, 4, 8 and 10-token prompts takes 8.05, 9.44, 11.74 and 14.26 ms with the current limit vs 9.29, 10.34, 12.31 and 14.77 ms with limit 0, while 11 and 16-token prompts take the same time with both (15.05 vs 15.05 ms, 16.01 vs 15.97 ms). That run set the limit through an environment variable that an earlier revision of this branch also read; this PR only has the session option, and for a given limit the routing code is the same. The limit applies to every call, so a value below 8 also moves batch-1 decode (8 rows at top-8) to the grouped GEMM.
Tests:
Limits: