Skip to content

[PyTorch] DeepSeekV3Layer: full MoE transformer layer (MLA + DeepSeek MoE) - #3471

Open
pggPL wants to merge 58 commits into
NVIDIA:mainfrom
pggPL:deepseek_v3_layer
Open

pggPL wants to merge 58 commits into
NVIDIA:mainfrom
pggPL:deepseek_v3_layer

Conversation

@pggPL

@pggPL pggPL commented Sep 3, 2026 •

Copy link
Copy Markdown
Collaborator

Description

Adds transformer_engine.pytorch.models, a namespace for model-specific layers built from TE modules, with a DeepSeek-V3 transformer layer analogous to TransformerLayer.

  • MultiLatentAttention: low-rank q/kv latents with RMSNorm fused into the up-projections, decoupled RoPE/NoPE heads, DotProductAttention with asymmetric head dims (cuDNN fused attention), fused Triton MLA RoPE kernels, optional YaRN scaling, TP.
  • DeepSeekV3MoE: fused sigmoid router with aux-loss-free bias and grouped top-k, experts as te.ops.Sequential(GroupedLinear, ScaledSwiGLU, GroupedLinear) (auto-fuses into the CuTe grouped MLP where available), local routing or expert parallelism over NCCL EP, optional shared expert.
  • DeepSeekV3Layer: pre-RMSNorm + MLA, then dense LayerNormMLP or DeepSeekV3MoE, residual connections.

Type of change

  • Documentation change (change only to the documentation, either a fix or a new content)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Infra/Build change
  • Code refactoring

Changes

  • transformer_engine/pytorch/models/deepseek_v3/: MultiLatentAttention, DeepSeekV3MoE, DeepSeekV3Layer, MLA RoPE kernels; exported via transformer_engine.pytorch.models
  • Docs: Model-specific layers section in docs/api/pytorch.rst
  • Tests: tests/pytorch/test_models.py (RoPE, YaRN, MoE vs dense reference), test_sanity.py::test_sanity_deepseek_v3_layer (all recipes), tests/pytorch/distributed/test_models.py (EP vs all-experts-local, L1)
  • examples/pytorch/deepseek_v3/: runnable EP example with a plain-PyTorch MoE baseline and a README with measurements
  • Follow-up: optional seq-wise aux loss

Performance

DeepSeekV3Layer with DeepSeek-V3 dims, 8 experts per rank, top-k 8, 4096 tokens per rank, fwd+bwd, bf16 unless noted. GB300, 4 GPUs per node, one NVLink domain. naive = torch all_to_all_single + Python loop of per-expert MLPs, naive_grouped = same all_to_all with a TE grouped GEMM, te = DeepSeekV3MoE. Attention and norms identical in all three.

ms / iteration (median of 3 runs) 4 GPUs 8 GPUs
naive 27.0 27.4
naive_grouped 16.5 16.9
te 12.4 14.0
te, MXFP8 10.3 12.1
te, MXFP8 fused grouped MLP 9.5 10.5

Where the time goes in the optimized variant (te, MXFP8 fused, 8 GPUs), per GPU and iteration, from nsys stats --report cuda_gpu_kern_sum (kernel time 10.8 ms, iteration 10.5 ms without the profiler; no memsets or copies left):

Group ms Kernels
NCCL EP all-to-all 2.3 nccl_ep_jit_ht_dispatch_kernel (1.05), nccl_ep_jit_ht_combine_kernel (1.29), each twice per iteration (fwd + bwd)
NCCL EP local permute 0.6 local_permute_dup/reduce: staging buffer to expert-major layout, zero-filled padding
rank skew wait 0.5 ncclDevKernel_AllGather_RING_LL (routing-map all-gather in prepare, ~0.06 ms of transfer); the first collective of the layer absorbs load imbalance between ranks
fused grouped MLP (cuDNN, MXFP8) 2.4 fc1+SwiGLU fwd (0.53), fc2 fwd (0.82), dGLU bwd (0.31), wgrad (0.78)
MXFP8 quantization 1.1 group_quantize_mxfp8 on the recv buffer (0.4), quantize_mxfp8_kernel_cast_only for dense GEMM inputs (0.7)
dense MXFP8 GEMMs (MLA projections, shared expert) 1.4 nvjet_sm103_qqtst_*
attention (cuDNN SDPA) 0.9 flash fprop (0.18) + bprop (0.51) + dq / dO helpers
RMSNorm, RoPE, adds 0.8 rmsnorm_fwd/bwd (0.39), rotary_*_kv (0.18), residual adds (0.2)

For comparison the naive variant spends 3.4 ms in all_to_all, 6.5 ms in 8 separate GEMM pairs, and ~12 ms in indexing, zero-fills, copies and adds; naive_grouped removes the loop (about 10.5 ms) and NCCL EP the remaining all_to_all plus sort / gather / scatter overhead (2.9 ms on 8 GPUs). Full breakdowns in examples/pytorch/deepseek_v3/README.md.

Checklist:

  • I have read and followed the contributing guidelines
  • The functionality is complete
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

🤖 Generated with Claude Code

pggPL and others added 30 commits August 18, 2026 12:37
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
MultiLatentAttention: low-rank q/kv latents (RMSNorm fused into
LayerNormLinear up-projections), decoupled RoPE/NoPE head split with a
shared key rope head, DotProductAttention with kv_channels=(qk, v) for
the cuDNN fused backend.

DeepSeekV3MoE: fused sigmoid router with aux-loss-free expert bias and
grouped top-k, routed experts as te.ops GroupedLinear+ScaledSwiGLU+
GroupedLinear (CuTe fused grouped MLP on supported HW), probs applied
per-token in the activation, local permute/unpermute or NCCL expert
parallelism via ep_dispatch/ep_combine, optional shared expert.

DeepSeekV3Layer: pre-RMSNorm + MLA and dense LayerNormMLP (RMSNorm,
swiglu) or MoE with residual connections.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
run_deepseek_ep.py checks the EP path against the all-experts-local
path numerically (forward, input/gate grads, all-reduced expert wgrads)
and smoke-tests the full layer with EP. Also size the default EP recv
capacity for per-expert alignment padding and the fused grouped MLP's
row-count requirement.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
The per-expert wgrad check called all_reduce on different tensors per
rank (rank-local experts), corrupting the reference grads; reduce every
expert's grad on every rank instead. Also pass zero-filled recv/grad
buffers to ep_dispatch/ep_combine so alignment-padding rows inside the
grouped-GEMM m_splits can never poison expert wgrads.

Verified on lyris (4x GB300, arm64): run_test_deepseek_ep.sh passes on
all ranks (EP forward/dgrad/gate-grad/expert-wgrad match the all-local
reference; full-layer EP smoke passes).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Move the Triton MLA RoPE kernels (Megatron-LM
fused_mla_yarn_rope_apply port) from tests/pytorch/attention/
mla_rope_utils.py into models/deepseek_v3/mla_rope.py and use them in
MultiLatentAttention: the q kernel rotates the rope slice in place and
the kv kernel assembles key/value in a single pass, removing the
torch.cat/expand/contiguous copies (~10% of layer GPU time). PyTorch
fallback (same convention) covers missing Triton and bshd.

Fix a latent bug from the test util: the q backward kernel assumed a
contiguous incoming gradient, but cuDNN attention backward can hand
over a strided one (allocator-state dependent IMA). The old test file
stays as a compat shim. Add a Triton-vs-PyTorch parity test.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Maps HF DeepseekV3DecoderLayer weights into DeepSeekV3Layer (GLU
interleave for routed experts, fused latent norms) and checks forward
and input grads match within bf16 tolerance. Expose layernorm_epsilon
on MultiLatentAttention (HF latent RMSNorms use 1e-6).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
docs/api/pytorch_models.rst: usage (local and EP), fused-path notes,
HF checkpoint weight mapping, and the class API; linked from the
PyTorch API page via a toctree entry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Keep the verified weight-mapping table in the docs; the comparison
itself stays as an out-of-tree script.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…scripts

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…eek_v3.mla_rope directly

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…rical comparison

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…hell launcher

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
… grouped GEMM in local MoE path

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…; expose MLA softmax_scale; clean docstrings

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…ters

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
… routed experts

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…ad of syncing bincount

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…p standard layers, autocast and other utilities

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
pggPL and others added 2 commits September 9, 2026 20:14
DeepSeekV3MoE.make_ep_buffer() builds a buffer for the module's routing
config; forward(ep_buffer=...) on the MoE and the layer reuses it instead
of creating one per call. The example reuses one buffer by default
(--ep-buffer-per-call restores per-call buffers).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
…kdown from an unskewed node

All timings are now medians of three runs (spread within 0.3 ms). The
TE kernel breakdown is taken from the node not slowed down by nsys, so
the rank-skew wait reflects load imbalance rather than profiler skew.
Also folds in the pending README restructuring (throughput table,
historical naive_grouped profile, results interpretation).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
@pggPL
pggPL marked this pull request as ready for review September 9, 2026 18:47
@pggPL
pggPL requested a review from ksivaman as a code owner September 9, 2026 18:47
@greptile-apps

greptile-apps Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium risk] Adds DeepSeek-V3 transformer layer models and MoE components.

The PR does not appear safe to merge until EP model construction works before bootstrap; the outstanding benchmark-provenance concern is non-blocking.

Findings

  1. P1 Model construction requires bootstrap ▶
  2. P2 Benchmark provenance removed ▶

Summary

The PR adds DeepSeek-V3 PyTorch model layers, MLA RoPE support, expert-parallel MoE composition, documentation, examples, and tests. Changes since the previous review also add EP reference tests, NVFP4 amax kernels, and optional FlashAttention import handling.

Reviews (8) · Last reviewed commit: "Compose DeepSeek expert parallel path wi..."

Comment thread transformer_engine/pytorch/models/deepseek_v3/moe.py
Comment thread tests/pytorch/distributed/run_models.py
…dates

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
@pggPL

pggPL commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Hi @vthumbe1503 @phu0ngng this is PR with DeepSeek layer which uses fused MLP and EP, can you have a look?

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.

I wonder if this file should sit together with our other RoPE implementations, i.e. in transformer_engine/pytorch/attention? I feel this models/ directory should contain only the model-level/higher-level implementations.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Move reusable MLA RoPE kernels and YaRN helpers alongside attention primitives. Update model and MXFP8 attention imports, and move standalone RoPE tests into the attention suite while retaining L0 coverage.

Validation: 46 passed, 3 skipped on RTX 5880 Ada using the installed TE primitives; Black, targeted pylint and git diff --check passed.
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Make MLA query RoPE allocate by default and preserve incoming gradients. Keep explicit in-place execution limited to eager autograd.

Align MXFP8 and NVFP4 experts to 256 rows independently of fusion, remove the EP tail margin, and cover fused and unfused padding in numerical tests.

Simplify model APIs, documentation and the EP benchmark following review.

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Comment thread examples/pytorch/deepseek_v3/deepseek_v3_layer_ep.py
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
device = torch.device("cuda", torch.cuda.current_device())
return EpBuffer(**self._ep_buffer_kwargs, device=device)

def _forward_ep(self, tokens: torch.Tensor, ep_buffer=None) -> torch.Tensor:

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.

I think it might make sense to use the te.Sequential to define the entire MOE block after this PR gets merged.
#3503
Would be more cleaner from API usage standpoint

Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>

GB200 and GB300 GPUs, 4096 tokens per rank, top-k 8, 8 local experts per GPU.
Times cover one layer's forward + backward; throughput is global, in millions of tokens/s.
Every number is the median of three independent runs (spread within 0.3 ms).

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.

P2 Benchmark provenance removed

The benchmark results no longer identify when they were measured or which commit was used. That makes the reported timings harder to reproduce or compare with later changes. Please keep the measurement date and commit alongside the results.

Suggested change
Every number is the median of three independent runs (spread within 0.3 ms).
Every number is the median of three independent runs (spread within 0.3 ms).
Measured on September 30, 2026, at commit
[`939c9db3`](https://github.com/NVIDIA/TransformerEngine/commit/939c9db36afcdb2617392bab57b4249c7cd4bcb0),
before the subsequent merge of `main`.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

pggPL added 2 commits October 1, 2026 11:56
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
Signed-off-by: Pawel Gadzinski <pgadzinski@nvidia.com>
@pggPL
pggPL requested a review from timmoon10 as a code owner October 1, 2026 10:03
Comment on lines +166 to +168
drop_on_overflow = get_ep_drop_on_overflow()
if drop_on_overflow is None:
raise RuntimeError("EP requires ep_bootstrap before constructing DeepSeekV3MoE.")

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.

P1 Model construction requires bootstrap

If a caller builds a DeepSeekV3MoE with ep_group before calling ep_bootstrap(), this new check raises during construction. Previously, bootstrap was required when creating the EP buffer for forward, so callers could build the model first and bootstrap before execution. That workflow can no longer instantiate the layer.

This branch has not been deployed

No deployments
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.

3 participants