Skip to content

Add Triton grouped-GEMM for MoE experts on Ampere/Ada - #8180

Merged
hwchen2017 merged 13 commits into
masterfrom
hongwei/group_gemm
Aug 3, 2026
Merged

Add Triton grouped-GEMM for MoE experts on Ampere/Ada#8180
hwchen2017 merged 13 commits into
masterfrom
hongwei/group_gemm

Conversation

@hwchen2017

@hwchen2017 hwchen2017 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Motivation

AutoEP expert computation uses  torch._grouped_mm , which only has a fused grouped-GEMM kernel on Hopper (sm90) and newer. On Ampere/Ada GPUs (sm80/sm86 — A100/A6000/A40/RTX 30xx) it silently falls back to a per-group Python loop ( _grouped_mm_fallback ), issuing one  at::mm  per expert plus a device→host sync on  offs . This is slow for MoE, which invokes grouped GEMM 3× per layer in the forward and again in the backward.

What this PR does

Adds  deepspeed/moe/group_gemm_triton.py : an autograd-aware Triton grouped GEMM that is a drop-in for  torch._grouped_mm  (2D×3D +  offs ), and wires it into  GroupedExperts  so it is auto-selected on sm < 9.0 (native path is kept on sm90+).

Key points:

• Single fused kernel per grouped GEMM, no per-group launches and no device→host sync (per-tile → group mapping is computed on device from  offs ).
• Support forward + backward for both operands, supporting fp16/bf16/fp32.
•  trans_b  option: expert weights are passed in their native  [E, N, K]  layout and transposed via strides inside the autograd Function, keeping  .transpose  off the tape. The weight gradient is produced directly in the native layout, avoiding a contiguous-materialization copy in backward.
• Device-side group metadata via a small Triton kernel ( _group_meta_kernel ).
• Auto-selection is overridable via  DS_DISABLE_TRITON_GROUPED_MM=1 .
• Auto-tunning won't be called twice for same experts shape.

Performance
Configuration: dim=2048, hidden=768, avg_tokens/expert=64,
dtype=bf16, distribution: unbalanced

Experts Tokens Method Forward (ms) Fwd + Bwd (ms) Backward (ms)
16 760 Triton 0.107 0.589 0.482
16 760 Torch 0.256 1.093 0.837
16 760 For-loop 0.776 6.337 5.561
32 1,848 Triton 0.213 0.622 0.410
32 1,848 Torch 0.755 2.222 1.466
32 1,848 For-loop 1.603 22.732 21.129

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9c5e0335bc

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread deepspeed/moe/ep_experts.py Outdated
Comment thread deepspeed/moe/ep_experts.py Outdated
Comment thread deepspeed/moe/group_gemm_triton.py
Comment thread deepspeed/moe/ep_experts.py Outdated
Comment thread deepspeed/moe/ep_experts.py Outdated
Comment thread deepspeed/moe/ep_experts.py
# BLOCK_M (M/token tile): 32 / 64 / 128
# BLOCK_N (N output tile): 128 / 256
# BLOCK_K (K reduction): 32 / 64
return [

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.

Is it possible to abstract _gmm_configs into abstract accelerator interfaces to let different accelerator return their preferred configs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to keep  _gmm_configs  local to  group_gemm_triton.py  for now, for a few reasons:

• These configs are the autotune search space for this specific Triton kernel, not a final device-tuned choice —  triton.autotune  already picks the best one per problem shape at runtime. So they're an implementation detail of the kernel rather than a device capability.
• The candidates for different kinds of device are likely same.
• Keeping it here also keeps all the Triton-specific logic (kernels + their autotune configs) in one place.

@delock

delock commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Hi @hwchen2017 thanks for your PR. I have left my comments in the code. One comment is, this PR is intended for Ampere/Ada, but triton can be used for other accelertors as well. Current code contains many CUDA specific checks, these code should be generized. The suggested solution is inlined. @tjruwase @sfc-gh-truwase for accelerator abstraction topic.

@sfc-gh-truwase

Copy link
Copy Markdown
Collaborator

@hwchen2017 thanks for the PR. Please address @delock's feedback. Also, can you please include some perf numbers?

@hwchen2017

Copy link
Copy Markdown
Contributor Author

Hi @sfc-gh-truwase , I add some perf numbers and benchmark code.

Signed-off-by: Hongwei <hongweichen@microsoft.com>
Signed-off-by: Hongwei <hongweichen@microsoft.com>
Signed-off-by: Hongwei <hongweichen@microsoft.com>
Signed-off-by: Hongwei <hongweichen@microsoft.com>
Signed-off-by: Hongwei <hongweichen@microsoft.com>
Signed-off-by: Hongwei <hongweichen@microsoft.com>
Signed-off-by: Hongwei <hongweichen@microsoft.com>
Signed-off-by: Hongwei <hongweichen@microsoft.com>
@hwchen2017
hwchen2017 force-pushed the hongwei/group_gemm branch from 264b399 to ca2090b Compare July 30, 2026 00:33
Comment thread accelerator/cuda_accelerator.py Outdated
# grouped-GEMM kernel is preferred there when Triton is available.
# Set DS_DISABLE_TRITON_GROUPED_MM=1 to force the native path.
import os
if os.getenv("DS_DISABLE_TRITON_GROUPED_MM", "0") == "1":

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.

Runtime env vars can be brittle in multi-node settings, and so generally avoided. If this is unavoidable then see https://www.deepspeed.ai/getting-started/#multi-node-environment-variables for proper usage.

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.

@hwchen2017 any thoughts on this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will replace it with a variable in config.

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.

Feel free to create an accelerator section in the config.

Please also add some documentation. You can create an accelerator section here
https://deepspeed.readthedocs.io/en/latest/index.html

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code has been updated to replace env var with a variable in config.

Comment thread tests/unit/v1/moe/test_group_gemm_triton.py
Signed-off-by: Hongwei Chen <hongweichen@microsoft.com>
Signed-off-by: Hongwei Chen <hongweichen@microsoft.com>
This reverts commit 51aa775.

Signed-off-by: Hongwei Chen <hongweichen@microsoft.com>
@hwchen2017
hwchen2017 force-pushed the hongwei/group_gemm branch from fd3a792 to 1cca543 Compare July 31, 2026 21:37

@tohtana tohtana left a comment

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.

Hi @hwchen2017,

Thank you for submitting this amazing PR! This fills an important gap in AutoEP.
I left a few comments. I don't think any of them are blockers, but please consider addressing them.

Comment thread deepspeed/moe/group_gemm_triton.py Outdated
mask=mask_k[:, None] & mask_n[None, :],
other=0.0,
)
acc += tl.dot(a_tile, b_tile)

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.

Lines 32-34 say "Only float16, bfloat16 and float32 inputs are supported, and both operands must share the same dtype. Matmuls accumulate in fp32 and the result
is cast back to the input dtype, matching torch._grouped_mm numerics." but the default precision of tl.dot is TF32.
I think this GEMM is not precision sensitive. So we could use TF32 but we also should clarify it. If we pass ieee to input_precision, FP32 inputs will be computed in FP32. It would also be good to add a config item to control it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

• The FP32 path is an edge case since half precision is the default precision in training now.
• TF32 is the de-facto standard for FP32 matmuls on Ampere+;  torch.matmul /cuBLAS and  torch._grouped_mm  also use TF32 by default. Forcing  ieee  would actually diverge from stock PyTorch numerics and give up most of the tensor-core throughput. Fully disabling tensor cores for FP32 is rare these days.
• Adding an  input_precision  config knob is extra surface area for a path almost nobody exercises; we can add it later if a real use case shows up.

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.

Okay, how about noting it with "Only float16, bfloat16 and float32 inputs are supported"?

Comment thread accelerator/abstract_accelerator.py Outdated
Comment thread accelerator/cuda_accelerator.py
Signed-off-by: Hongwei Chen <hongweichen@microsoft.com>
(``offs[-1] == M``). Empty groups are allowed.
out : ``[M, N]`` same floating dtype as inputs.

Only ``float16``, ``bfloat16`` and ``float32`` inputs are supported, and both

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tohtana It's mentioned here.

@tohtana tohtana left a comment

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.

Thank you for the update, @hwchen2017! Looks good to me.

@hwchen2017
hwchen2017 added this pull request to the merge queue Aug 3, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Aug 3, 2026
@hwchen2017
hwchen2017 added this pull request to the merge queue Aug 3, 2026
Merged via the queue into master with commit df84f6d Aug 3, 2026
14 of 15 checks passed
@hwchen2017
hwchen2017 deleted the hongwei/group_gemm branch August 3, 2026 07:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants