[TRTLLM-14054][perf] Load Qwen3-Next GDN in_proj in dense layout and add a multi-row gated RMSNorm#16314
Conversation
…lti-row gated RMSNorm HF GDN checkpoints pack in_proj_qkvz rows per key-head group as [q_g|k_g|v_g|z_g] (and in_proj_ba as [b_g|a_g]), so every step ran a split/reshape kernel (fused_qkvzba_split_reshape_cat, one launch per GDN layer) to rearrange the projection output before the GDN kernels could consume it. Instead, permute the in_proj rows once at weight-load time into dense per-TP-rank [Q|K|V|Z] / [b|a] layouts. The projection output components become plain column slices consumed in place, and the per-step split/reshape kernel is deleted along with fix_query_key_value_ordering. The permutation handles row tensors of any dtype (including packed FP4 via a uint8 view), FP8 2D-block weight scales at scale-block granularity, and TP sharding; the downstream Triton helpers (gating, conv1d extract/transpose, QKV split) take explicit row strides so the strided column-slice views are read in place without packing copies. Also add a multi-row gated RMSNorm kernel: at GDN decode shapes (thousands of short 128-wide rows) the single-row kernel is launch-limited; processing 4 rows per CTA with the same reduction order is bitwise identical to the single-row kernel and roughly halves the kernel time (5.43 us to 3.11 us at batch 256). The gate z is read token-major directly from the projection slice. The transformation is bitwise-lossless by construction. Unit tests cover the row/scale-block permutation against the grouped reference (including quantized dtypes), strided-view inputs for all touched kernels, and the multi-row norm against the single-row path. Measured with trtllm-serve + benchmark_serving on Qwen3.6-35B-A3B-NVFP4 (hybrid gated-delta-net + MoE) on 1x B200, ISL=1024/OSL=6144, over a concurrency sweep 1..256: output throughput improves 2.44% on average across the 9-point curve (worst point +1.25%, no regressions), saving ~185 us and 30 kernel launches per decode step at concurrency 256. Signed-off-by: Kaiyu Xie <26294424+kaiyux@users.noreply.github.com>
📝 WalkthroughWalkthroughChangesQwen3Next GDN optimization
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant HFCheckpoint
participant WeightMapper
participant GDNMixer
participant TritonKernels
participant GatedRMSNorm
HFCheckpoint->>WeightMapper: load grouped in_proj tensors
WeightMapper->>GDNMixer: provide dense per-rank QKVZ and BA tensors
GDNMixer->>TritonKernels: prepare strided token inputs and gating values
TritonKernels->>GatedRMSNorm: provide GDN output and gate tensor
GatedRMSNorm->>GDNMixer: return normalized token-major output
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tensorrt_llm/_torch/modules/mamba/layernorm_gated.py (1)
167-188: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueEligibility logic is duplicated and slightly divergent across the two entry points.
rms_norm_gated_token_majorinlines its own eligibility check (x.stride(-1)==1 and z.stride(2)==1 and z.stride(1)==N and N<=_MULTIROW_MAX_N and pow2), while_layer_norm_fwduses_multirow_gated_rmsnorm_eligible(...)(which omits the stride checks, relying on upstream asserts). Consider routing both through a single predicate so the constraints can't drift apart as the kernel evolves.🤖 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 `@tensorrt_llm/_torch/modules/mamba/layernorm_gated.py` around lines 167 - 188, Unify multi-row gated RMSNorm eligibility by extending or reusing _multirow_gated_rmsnorm_eligible for the stride constraints currently inlined by rms_norm_gated_token_major. Update both rms_norm_gated_token_major and _layer_norm_fwd to use this single predicate, preserving their existing eligibility inputs and ensuring all required shape, stride, normalization, gating, and power-of-two constraints remain enforced.
🤖 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.
Nitpick comments:
In `@tensorrt_llm/_torch/modules/mamba/layernorm_gated.py`:
- Around line 167-188: Unify multi-row gated RMSNorm eligibility by extending or
reusing _multirow_gated_rmsnorm_eligible for the stride constraints currently
inlined by rms_norm_gated_token_major. Update both rms_norm_gated_token_major
and _layer_norm_fwd to use this single predicate, preserving their existing
eligibility inputs and ensuring all required shape, stride, normalization,
gating, and power-of-two constraints remain enforced.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 3c527273-05d0-4f35-99fd-5ff8101aeca9
📒 Files selected for processing (5)
tensorrt_llm/_torch/models/checkpoints/hf/qwen3_next_weight_mapper.pytensorrt_llm/_torch/modules/mamba/fuse_elementwise_ops.pytensorrt_llm/_torch/modules/mamba/gdn_mixer.pytensorrt_llm/_torch/modules/mamba/layernorm_gated.pytests/unittest/_torch/modules/mamba/test_gdn_kernel_optimizations.py
|
/bot run --disable-fail-fast |
|
PR_Github #58951 [ run ] triggered by Bot. Commit: |
|
PR_Github #58951 [ run ] completed with state
|
|
/bot run --disable-fail-fast |
👎 Promotion blocked, new vulnerability foundVulnerability report
|
|
/bot run --disable-fail-fast |
|
PR_Github #59065 [ run ] triggered by Bot. Commit: |
|
PR_Github #59065 [ run ] completed with state |
Description
HF gated-delta-net (GDN) checkpoints pack
in_proj_qkvzrows per key-head group as[q_g|k_g|v_g|z_g](andin_proj_baas[b_g|a_g]), so every step ran a split/reshape kernel (fused_qkvzba_split_reshape_cat, one launch per GDN layer) to rearrange the projection output before the GDN kernels could consume it. This PR:in_projrows once at weight-load time into dense per-TP-rank[Q|K|V|Z]/[b|a]layouts (Qwen3NextHfWeightMapper). The projection output components become plain column slices consumed in place, and the per-step split/reshape kernel is deleted along withfix_query_key_value_ordering. The permutation handles row tensors of any dtype (packed FP4 included, via a uint8 view), FP8 2D-blockweight_scale_invtensors at scale-block granularity, per-row scales/bias, and TP sharding (rows are permuted per TP-rank chunk so the column-parallel contiguous row split still hands each rank its own heads).zis read token-major directly from the projection slice.The transformation is bitwise-lossless by construction: the same values flow through the same kernels, only their layout in the projection output changes. Net effect at concurrency 256: ~185 us and 30 kernel launches saved per decode step.
Test Coverage
tests/unittest/_torch/modules/mamba/test_gdn_kernel_optimizations.pyextended: the row/scale-block permutation against the grouped reference (multiple head configs and TP sizes, quantized dtypes, FP8 scale-block consistency), strided column-slice inputs for the gating / transpose / split kernels against packed inputs (bitwise), and the multi-row gated RMSNorm against the reference and (bitwise) against the single-row path.trtllm-serve+benchmark_servingon Qwen3.6-35B-A3B-NVFP4 (hybrid GDN + MoE), 1x B200, ISL 1024 / OSL 6144, random dataset with--ignore-eos, one point per concurrency (paired num_prompts 8...1024), measured at base8ef7190ebfon top of [TRTLLM-14054][perf] Reduce per-step host preparation overhead in the PyTorch executor decode path #16313:Output throughput improves +2.44% on average across the 9-point curve (worst point +1.25%, no regressions). Independent of #16313 (no shared files); together the two compose to about +5.6% over the common baseline on this harness.
PR Checklist
PR description clearly explains what and why. If using CodeRabbit's summary, please make sure it makes sense.
PR Follows TRT-LLM CODING GUIDELINES to the best of your knowledge.
Test cases are provided for new code paths (see test instructions)
If PR introduces API changes, an appropriate PR label is added - either
api-compatibleorapi-breaking. Forapi-breaking, includeBREAKINGin the PR title.Any new dependencies have been scanned for license and vulnerabilities
CODEOWNERS updated if ownership changes
Documentation updated as needed
Update tava architecture diagram if there is a significant design change in PR.
The reviewers assigned automatically/manually are appropriate for the PR.
Please check this after reviewing the above items as appropriate for this PR.
GitHub Bot Help
To see a list of available CI bot commands, please comment
/bot help.🤖 Generated with Claude Code
Summary by CodeRabbit
Performance
Bug Fixes
Tests