Repository navigation
[Triton/Gluon] Add MoE GEMM variants and weight-gradient kernel - #5310
Conversation
🏷️ CI GuideRuns automatically on every PR:
Extended tests (opt-in via labels):
PR title tags & labels: |
70fbece to
995124f
Compare
995124f to
147e227
Compare
147e227 to
60fd45d
Compare
|
can we split this into 3 PRs - one per kernel |
There was a problem hiding this comment.
🟡 Changes recommended
Ragged expert groups can produce incorrect GEMM output, the weight-gradient path mishandles routing and padding, and its benchmark cannot run.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Pull request overview
Adds three Triton MoE kernels for quantized grouped GEMM and training weight-gradient computation.
Changes:
- Adds MXFP8 and per-token-scaled grouped GEMM kernels and wrappers.
- Adds an atomic MoE weight-gradient kernel.
- Adds shared mapping utilities, correctness tests, and benchmarks.
File summaries
| File | Description |
|---|---|
aiter/ops/triton/moe/moe_gemm_mxfp8.py |
MXFP8 GEMM wrapper. |
aiter/ops/triton/moe/moe_gemm_per_token.py |
Per-token FP8 GEMM wrapper. |
aiter/ops/triton/moe/moe_wgrad.py |
Weight-gradient wrapper. |
aiter/ops/triton/moe/moe_utils.py |
Builds expert block mappings. |
aiter/ops/triton/_triton_kernels/moe/moe_gemm_mxfp8.py |
MXFP8 Triton kernel. |
aiter/ops/triton/_triton_kernels/moe/moe_gemm_per_token.py |
Per-token Triton kernel. |
aiter/ops/triton/_triton_kernels/moe/moe_wgrad.py |
Atomic weight-gradient kernel. |
op_tests/triton_tests/moe/test_moe_gemm_mxfp8_per_token.py |
GEMM correctness tests. |
op_tests/triton_tests/moe/test_moe_wgrad.py |
Weight-gradient correctness tests. |
op_tests/op_benchmarks/triton/bench_moe_gemm_mxfp8_per_token.py |
GEMM benchmarks. |
op_tests/op_benchmarks/triton/bench_moe_wgrad.py |
Weight-gradient benchmark. |
Review details
Suppressed comments (1)
aiter/ops/triton/_triton_kernels/moe/moe_wgrad.py:61
- This launchable kernel omits the config-aware
reprrequired by the AITER Triton kernel convention. The noted autotune interaction explains the omission but does not provide the required trace identity; resolve the Triton incompatibility or use a supported launch/config structure that permitsmake_kernel_repr.
# NOTE: repr= is intentionally omitted here. Combining @triton.autotune with
# @triton.jit(repr=...) corrupts the kernel execution on current Triton versions.
# The autotune key ["N", "K"] already encodes the compiled variant identity.
@triton.jit
- Files reviewed: 11/11 changed files
- Comments generated: 11
- Review effort level: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
ok, I will do that |
08522ae to
0ee494d
Compare
Split from ROCm#5310 (per reviewer request). Adds moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. Shares build_block_mapping() from moe_utils (introduced in the mxfp8 PR). m_mask bounded by block_token_ends to prevent cross-expert reads.
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
There was a problem hiding this comment.
🟡 Changes recommended
Sentinel handling, E8M0 decoding, bias indexing, and API validation contain correctness issues.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
Review details
Suppressed comments (3)
aiter/ops/triton/_triton_kernels/moe/moe_gemm_mxfp8.py:98
- Every non-empty mapping has padded slots: this upper bound is at least one larger than the maximum possible actual block count, and those slots contain expert ID
-1.tl.assume(expert_id >= 0)is not a runtime guard, so these programs continue and readrhs, weight scales, and optional bias before the output mask suppresses stores. Return immediately for the sentinel, as the utility contract describes.
expert_id = tl.load(block_expert_ids_ptr + pid_mb).to(tl.int64)
token_offset = tl.load(block_token_offsets_ptr + pid_mb)
token_end = tl.load(block_token_ends_ptr + pid_mb)
tl.assume(expert_id >= 0)
aiter/ops/triton/_triton_kernels/moe/moe_gemm_mxfp8.py:155
- This bit shift does not fully decode E8M0: byte
0x00must become2^-127(0x00400000), but becomes zero, and0xFFis E8M0 NaN but becomes infinity. This disagrees with the repository's canonical decoder inaiter/utility/fp4_utils.py:213-220and produces incorrect results for valid edge encodings.
a_scale = (a_scale_e8m0.to(tl.uint32) << 23).to(tl.float32, bitcast=True)
b_scale = (b_scale_e8m0.to(tl.uint32) << 23).to(tl.float32, bitcast=True)
aiter/ops/triton/_triton_kernels/moe/moe_gemm_mxfp8.py:169
- Bias indexing assumes a contiguous
[E, N]tensor by hardcoding strides(N, 1), but the public API documents no layout restriction and does not make or require the bias contiguous. A same-shaped sliced/non-contiguous bias therefore reads incorrect elements; pass both bias strides or explicitly enforce/materialize the required layout.
if HAS_BIAS:
bias = tl.load(
bias_ptr + expert_id * N + offs_n,
mask=n_mask,
other=0.0,
- Files reviewed: 6/6 changed files
- Comments generated: 6
- Review effort level: Balanced
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
0ee494d to
c4ca644
Compare
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
c4ca644 to
2c02791
Compare
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
dc964b5 to
7f4e685
Compare
Introduces three components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls; padding slots carry block_expert_ids=-1 so the kernel's off_expert==-1 guard handles them. moe_gemm_mxfp8: Triton kernel (_moe_gemm_mxfp8_kernel) and wrapper (moe_gemm_mxfp8) for fused grouped MoE GEMM with MXFP8 quantisation. E8M0 microscales (one uint8 per quant_block_size elements along K) are converted to FP32 power-of-two factors inside the kernel. Supports an optional per-expert bias. m_mask is bounded by block_token_ends to prevent cross-expert reads. moe/__init__.py: package-level export for moe_gemm_mxfp8.
7f4e685 to
9558598
Compare
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
…ng utils Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
…ng utils (#5338) Split from #5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads.
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
Split from #5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
…ng utils (ROCm#5338) Split from ROCm#5310 (per reviewer request). Introduces two components: moe_utils.py: build_block_mapping() pre-computes per-M-block expert IDs, token offsets, and valid row ends using pure GPU tensor ops (torch.bucketize + cumsum + torch.where). Zero .item() calls. moe_gemm_per_token: fused grouped MoE GEMM applying per-token FP32 activation scales and per-expert FP32 weight scales after FP8 dot-product accumulation. m_mask bounded by block_token_ends to prevent cross-expert reads. Signed-off-by: Xiake Sun <xiake.sun@amd.com>
Split from ROCm#5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order. Signed-off-by: Xiake Sun <xiake.sun@amd.com>
Split from #5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
Split from #5310 (per reviewer request). Adds _moe_wgrad_kernel and moe_wgrad wrapper: computes the weight gradient for a MoE expert block using the moe_align_block_size sorting interface. Independent of the mxfp8/per_token PRs — no shared dependencies, can be reviewed and merged in any order.
Summary
Three new Triton kernels for MoE training and quantized inference.
moe_gemm_mxfp8— Block-scaled MXFP8 grouped GEMM for MoE expert layers. All expert GEMMs are dispatched in a single kernel launch. E8M0 microscales (one uint8 perquant_block_sizeelements along K) are converted to FP32 power-of-two valuesinside the kernel and applied per K-iteration. Supports optional per-expert bias.
moe_gemm_per_token— Per-token FP8 scale grouped GEMM. Per-token activation scales and per-expert weight scales are applied after the FP8 dot-product accumulation. Uses agroup_sizes-based interface (1-D tensor of per-expert token counts)compatible with Lumen's routing infrastructure.
moe_wgrad— Fused weight-gradient kernel operating directly onsorted_token_ids/expert_idsfrommoe_align_block_size. ComputesdW[e] = Σ_{t→e} grad[t].T @ input[t]viatl.atomic_add, eliminating the sort+pad+bmm + CPU-GPU sync in thebackward pass. Uses
@triton.autotunewithreset_to_zero=["dw_ptr"]— required because atomic accumulation across benchmark trials would otherwise corrupt the final result.Notes
expert_idcast totl.int64before pointer arithmetic to prevent int32 overflow at production scale (E=256, N×K>8M: 255 × 14.7M = 3.74B > INT32_MAX)_moe_gemm_mxfp8_kernelassertsBLOCK_K == QUANT_BLOCK_SIZEat JIT time viatl.static_assert@triton.jit(repr=...)intentionally omitted on_moe_wgrad_kernel— combining@triton.autotunewithrepr=corrupts kernel execution on current Triton; the autotune key["N", "K"]encodes variant identity_build_block_mappingextracted tomoe/moe_utils.pyto avoid duplication between the two GEMM wrappersmoe/__init__.pyexports all three public wrappersTests & Benchmarks
test_moe_gemm_mxfp8_per_token.py: constant-input roundtrip, bias variant, empty-token guardstest_moe_wgrad.py: analytical constant-input check, linearity, zero-padded-token guardbench_moe_gemm_mxfp8_per_token.py/bench_moe_wgrad.py: DSv4-representative shapes,--metric time/bandwidth