Conversation
Adds a dedicated top-k routing path for the degenerate grouped-topk case
(n_group == 1 and topk_group == 1), which is plain top-k over all experts.
`invokeNoAuxTc` now tries this path first and falls back to the existing
grouped kernels otherwise.
Two kernels are dispatched from a compile-time (num_experts, topk) tier
list: a block-per-token kernel that stages biased/unbiased scores in shared
memory (with a hierarchical per-warp reduction for the high-expert-count
tiers), and a warp-per-token kernel for the large-batch/small-expert case.
`moeTopKFuncs.cuh` is extended to support this:
* generalized bitonic / odd-even-merge `Sort<N>` for any N in [1, 64],
lifting the previous N <= 4 (or multiple-of-4 up to 16) restriction
* `reduceTopKForLane`, which leaves the k-th result in lane k instead of
replicating all K results in every lane
* `redux.sync.max.u32`-based warp reduction on SM100+
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
SituGLU is the gated activation used by Kimi models:
gate_out = beta * tanh(gate / beta) * sigmoid(gate)
up_out = linear_beta * tanh(up / linear_beta) # if linear_beta > 0
out = gate_out * up_out
`linear_beta <= 0` means "unset" and passes `up` through, matching
`SituAndMul(linear_beta=None)` on the Python side.
Adds the `SituAndMul` CustomOp plus two CUDA kernels: `situ_and_mul` for
the dense `[..., 2 * d]` layout, and `masked_situ_and_mul` for the batched
`[E, T, 2 * d]` MoE layout, which reads `expert_num_tokens` and leaves
padded rows untouched. Both accumulate in fp32 and write straight to the
output buffer; the pure-torch fallback allocated ~8 fp32 temporaries per
call, which is prohibitive inside a MoE layer.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
Adds `MoEActivation.SITU` and the `activation_situ_beta` /
`activation_situ_linear_beta` parameters, plumbed from `FusedMoE(...)`
through `FusedMoEConfig` down to the experts implementations.
`apply_moe_activation` dispatches SITU to the fused `situ_and_mul` kernel.
Declared supported on Marlin and DeepGEMM FP4. Both betas reach the kernel
from `FusedMoEConfig` on every one of those paths:
* the Marlin helpers (`fused_marlin_moe`, `batched_fused_marlin_moe`,
`_fused_marlin_moe`) take the two betas as explicit arguments and
forward them to their activation callback, alongside the existing
`clamp_limit` / `gemm1_alpha` / `gemm1_beta` quant-config parameters.
That keeps the module-level `apply_moe_activation` default correct
rather than depending on the caller supplying a config-aware callback
* `FusedMoEExpertsModular.activation` accepts the betas too, preferring an
explicit value and falling back to `self.moe_config`, so it stays
interchangeable with the module-level function as a callback
* batched Marlin's callback routes SITU to `masked_situ_and_mul`, which
respects `expert_num_tokens` instead of running over padded rows
* DeepGEMM FP4 calls `self.activation`; SILU keeps its fused
gate+mul+quant kernels, while the general gated activations
(SWIGLUSTEP, SITU) apply the activation and then FP8-requant into the
layout matching the active scale format
`beta` is required rather than defaulted: a missing value means the caller
bypassed the config plumbing, so both the generic path and the batched
Marlin callback assert instead of silently substituting 1.0.
`linear_beta` stays optional -- <= 0 signals "unset" to the kernel and
passes `up` through, matching `SituAndMul(linear_beta=None)`.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
`TrtLlmMxfp4ExpertsMonolithic` hardcoded the routing arguments to the renormalize case: the router logits were cast to BF16 and the bias, group counts and routed scaling factor were all passed as None. That silently excluded sigmoid + grouped-topk (DeepSeekV3-style) routing, even though the TRTLLM-Gen kernel implements it and the caller already threads `num_expert_group`, `topk_group`, `e_score_correction_bias` and `routed_scaling_factor` into `apply`. Forwards those four arguments and stops downcasting the logits (DeepSeekV3 routing wants FP32; the renormalize path is unaffected because `RoutingMethodType.Renormalize` still receives BF16 logits from the caller), then adds `RoutingMethodType.DeepSeekV3` to the supported list. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
The TRTLLM-Gen SituGLU cubin computes
left = gatedActAlpha * tanh(x0 / gatedActAlpha) * sigmoid(x0)
right = gatedActBeta * tanh(x1 / gatedActBeta)
which is exactly `situ_and_mul`, so `activation_situ_beta` maps to
`gemm1_alpha` and `activation_situ_linear_beta` to `gemm1_beta`. Unlike
the SwiGLU-OAI clamp/beta, these act on the dequantized gate/up rather
than the raw GEMM1 accumulator, so on the NVFP4 path they are registered
as-is instead of being folded by `g1_alphas`.
`linear_beta` must be > 0 here: the cubin has no up-passthrough path.
Requires a FlashInfer build exposing `ActivationType.Situ` and the
`is_private` kernel selector; the currently pinned flashinfer-python does
not have it, so this commit should land after that dependency is bumped.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
Adds `KimiK3LatentMoETailOp`, a CuTe DSL implementation of the latent-MoE
tail: given the TP-partial latent routed output and the TP-partial shared
expert output, it produces the fully reduced hidden-size result in one
fused sequence instead of two NCCL all-reduces plus a separate up
projection.
Three kernels back it, all over symmetric memory:
* `CollectiveKernel` — all-reduce + RMSNorm + reduce-scatter with a
Lamport-flag early exit, so ranks proceed as their peers' data lands
* `AdaptiveUpProjectionKernel` — multicast GEMM for the replicated latent
up projection that folds the shared-expert add into its epilogue
* `LamportCopyKernel` — staging copies into the Lamport buffers
The op is process-wide cached on a contract (TP group, dtype, shapes, eps)
and registers a CuTe DSL warmup provider so the JIT compile happens during
warmup rather than on the first forward.
Decode-shaped only: TP 8 or 16, BF16, SM100, and up to 16 tokens per call.
Gated behind `VLLM_ENABLE_K3_LATENT_MOE_TAIL_FUSION`.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
Latent MoE keeps the routed experts in a lower-dimensional latent space and projects back to hidden size afterwards, so the runner has to reduce the routed output *before* the output transform: that transform contains an RMSNorm, and normalizing TP-partial values then summing is not the same as summing then normalizing. `MoERunner` gains `_maybe_reduce_routed_output_before_transform` for this, and the two downstream reduction helpers now take the resulting "already reduced" flag explicitly instead of always reading `_fused_output_is_reduced`. `LatentMoERunner` adds a fused path for the common case (TP > 1, shared expert present, un-reduced combine output, no sequence parallelism). The up projection is replicated, so the latent partial can be all-reduced and RMSNormed in one FlashInfer fused collective, then up-projected locally; the shared-expert all-reduce overlaps with that GEMM on the aux stream at decode batch sizes. Optionally dispatches to the CuTe DSL latent-MoE tail when `VLLM_ENABLE_K3_LATENT_MOE_TAIL_FUSION` is set and the token count is within the compiled kernel's range. The base path stays correct at any TP size, just with two collectives instead of one. `forward` also accepts `shared_experts_input`, letting a caller pre-apply the routed input transform (e.g. to overlap it on another stream) and pass the original hidden states through for the shared experts. Also generalizes the modular-kernel `output_alias` fast path to CUDA: it was gated on ROCm + AITER, but the redundant write-back copy it avoids is platform-independent. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Yongye Zhu <zyy1102000@gmail.com>
Contributor
|
This pull request has merge conflicts that must be resolved before it can be |
ZJY0516
added a commit
that referenced
this pull request
Jul 28, 2026
Signed-off-by: zjy0516 <riverclouds.zhu@qq.com>
This was referenced Jul 29, 2026
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.
Purpose
Extends the existing fused-MoE stack with the pieces needed by latent-MoE
models, split out of the much larger Kimi K3 model PR
(#50000) so the MoE work can be
reviewed independently of the model, attention, tokenizer and parser changes.
Nothing here registers a new model. Every addition is either a new kernel, a
new opt-in code path, or plumbing for a new activation/routing mode; default
execution paths are unchanged.
What's in it
Seven self-contained commits, reviewable in order:
[Kernel][MoE]fused single-group top-k routing — adds a dedicatedpath for the degenerate grouped-topk case (
n_group == 1 && topk_group == 1),which is plain top-k over all experts.
invokeNoAuxTctries it first andfalls back to the existing grouped kernels. Two kernels are dispatched from a
compile-time
(num_experts, topk)tier list: a block-per-token kernel with ahierarchical per-warp reduction for high expert counts, and a warp-per-token
kernel for the large-batch case.
moeTopKFuncs.cuhgains a generalizedbitonic / odd-even-merge
Sort<N>for anyNin[1, 64](wasN <= 4, ora multiple of 4 up to 16), a
reduceTopKForLanethat leaves the k-th resultin lane k instead of replicating all K results in every lane, and a
redux.sync.max.u32warp reduction on SM100+.[Kernel]SituGLU (situ_and_mul) — the gated activation used by Kimimodels:
Adds the
SituAndMulCustomOp plus two CUDA kernels:situ_and_mulfor thedense
[..., 2 * d]layout andmasked_situ_and_mulfor the batched[E, T, 2 * d]MoE layout, which readsexpert_num_tokensand leaves paddedrows untouched. Both accumulate in fp32 and write straight to the output
buffer — the pure-torch fallback allocates ~8 fp32 temporaries per call,
which is prohibitive inside a MoE layer.
[MoE]wire SituGLU through fused MoE —MoEActivation.SITUplus theactivation_situ_beta/activation_situ_linear_betaparameters, plumbedfrom
FusedMoE(...)throughFusedMoEConfigto the experts.Declared supported on Marlin and DeepGEMM FP4 only (plus the TRTLLM-Gen
backends in commit 5). Both betas reach the kernel from
FusedMoEConfigonevery one of those paths:
fused_marlin_moe,batched_fused_marlin_moe,_fused_marlin_moe) take the two betas as explicit arguments and forwardthem to their activation callback, alongside the existing
clamp_limit/gemm1_alpha/gemm1_betaquant-config parameters. This keeps themodule-level
apply_moe_activationdefault correct instead of depending onthe caller supplying a config-aware callback.
FusedMoEExpertsModular.activationaccepts the betas as well, preferringan explicit value and falling back to
self.moe_config, so it staysinterchangeable with the module-level function as a callback.
masked_situ_and_mul, whichrespects
expert_num_tokensinstead of running over padded rows.self.activation. SILU keeps its fused gate+mul+quantkernels, while the general gated activations apply the activation then
FP8-requant into the layout matching the active scale format.
betais required rather than defaulted — a missing value means the callerbypassed the config plumbing, so both the generic path and the batched
Marlin callback assert instead of silently substituting 1.0.
linear_betastays optional:
<= 0signals "unset" to the kernel and passesupthrough, matching
SituAndMul(linear_beta=None).[MoE]DeepSeekV3 routing on the TRTLLM-Gen MXFP4 monolithic path —TrtLlmMxfp4ExpertsMonolithichardcoded the routing arguments to therenormalize case: logits cast to BF16, and bias / group counts / routed
scaling factor all passed as
None. That silently excluded sigmoid +grouped-topk routing even though the TRTLLM-Gen kernel implements it and the
caller already threads all four values into
apply. Forwards them and stopsdowncasting the logits (DeepSeekV3 routing wants FP32; the renormalize path
still receives BF16 from the caller).
[MoE]SituGLU for the TRTLLM-Gen MXFP4/NVFP4 backends — the TRTLLM-GenSituGLU cubin computes exactly
situ_and_mul, soactivation_situ_betamapsto
gatedActAlphaandactivation_situ_linear_betatogatedActBeta.Unlike the SwiGLU-OAI clamp/beta these act on the dequantized gate/up rather
than the raw GEMM1 accumulator, so on the NVFP4 path they are registered
as-is instead of being folded by
g1_alphas.[Kernel]CuTe DSL fused latent-MoE tail —KimiK3LatentMoETailOp.Given the TP-partial latent routed output and the TP-partial shared-expert
output, it produces the fully reduced hidden-size result in one fused
sequence instead of two NCCL all-reduces plus a separate up projection.
Three kernels back it, all over symmetric memory:
CollectiveKernel(all-reduce + RMSNorm + reduce-scatter with a Lamport-flag early exit, so
ranks proceed as their peers' data lands),
AdaptiveUpProjectionKernel(multicast GEMM for the replicated latent up projection, folding the
shared-expert add into its epilogue) and
LamportCopyKernel. The op isprocess-wide cached on a contract (TP group, dtype, shapes, eps) and
registers a CuTe DSL warmup provider so the JIT compile happens during warmup
rather than on the first forward.
Decode-shaped only: TP 8 or 16, BF16, SM100, up to 16 tokens per call. Gated
behind
VLLM_ENABLE_K3_LATENT_MOE_TAIL_FUSION(default off).[MoE]LatentMoERunner— latent MoE keeps the routed experts in alower-dimensional latent space and projects back to hidden size afterwards,
so the runner must reduce the routed output before the output transform:
that transform contains an RMSNorm, and normalizing TP-partial values then
summing is not the same as summing then normalizing.
MoERunnergains_maybe_reduce_routed_output_before_transformfor this, and the twodownstream reduction helpers now take the resulting "already reduced" flag
explicitly instead of always reading
_fused_output_is_reduced.LatentMoERunneradds a fused path for the common case (TP > 1, sharedexpert present, un-reduced combine output, no sequence parallelism): the up
projection is replicated, so the latent partial can be all-reduced and
RMSNormed in one FlashInfer fused collective then up-projected locally, with
the shared-expert all-reduce overlapped on the aux stream at decode batch
sizes. It optionally dispatches to the commit-6 CuTe DSL tail. The base path
stays correct at any TP size, just with two collectives instead of one.
Also generalizes the modular-kernel
output_aliasfast path to CUDA. It wasgated on ROCm + AITER, but the redundant write-back copy it avoids is
platform-independent; ROCm behavior is unchanged.
Not duplicating an existing PR
Checked per
AGENTS.md:fast path.
maxvstop2for bias-free models) and [Kernel] Fuse softmax into grouped_topk CUDA kernel #45120 (fusingsoftmax into
grouped_topk) also editgrouped_topk_kernels.cu, but fordifferent features — neither adds an
n_group == 1 && topk_group == 1fastpath. Textual conflicts are likely; happy to rebase behind whichever lands
first.
internal-router crash fix) also edit
moe_runner.py, but neither overlapswith the reduce-before-transform change or
LatentMoERunner.[New model] Kimi K3 #50000 will be rebased onto whatever lands here.
Test plan
Intended commands:
pytest tests/kernels/moe/test_grouped_topk.py -v pytest tests/kernels/core/test_activation.py -v -k situ pytest tests/models/kimi_k3/test_latent_moe_tail.py -v # needs 8x SM100Kernel performance for the latent-MoE tail is measured with the benchmark added
in this PR:
Test results
To be added.
Model evaluation results
To be added. Commits 3–5 and 7 can affect model output, so accuracy numbers are
required before this is ready for review.
AI assistance
AI assistance (Claude Code) was used to extract these changes from #50000 into a
reviewable commit series and to write the commit messages and this description.
The underlying implementation is from #50000. A human submitter is reviewing
every changed line and will run the tests and evals above before marking this
ready for review.