ScatterMoE Guide: Fast Fused Kernels for Sparse MoE LLMs

The Memory Explosion and Padding Inefficiency in Sparse Mixture-of-Experts
As frontier artificial intelligence models transition from dense transformers to sparse Mixture-of-Experts (SMoE) architectures—such as Mixtral 8x7B, Mixtral 8x22B, DBRX, and DeepSeek-V3—engineering teams can scale total parameter counts into hundreds of billions while activating only a small subset of parameters per token. In enterprise knowledge platforms, continuous automated real-time data fetching pipelines, and high-throughput systems engineered by AiFetchly, MoE architectures enable frontier reasoning at a fraction of dense inference and training FLOPs.
However, executing sparse routing across dynamic workloads on modern GPUs exposes severe systems-level inefficiencies: the token padding problem and the intermediate activation duplication tax.
The Sparse MoE Execution Pipeline and Memory Multiplication:
Input Batch: S = 4,096 tokens, Hidden Dimension D = 4,096, Top-k Routing: k = 2 experts per token
Number of Routed Experts: E = 8 experts
Conventional Token Replication Overhead:
1. Gating Network computes router probabilities: R(x) -> Top-2 experts per token.
2. Token Duplication Step:
Each token routed to k=2 experts is physically COPIED into memory for each expert!
Tensor size expands from [S, D] to [S * k, D]:
Memory Footprint = 4,096 * 2 * 4,096 * 2 bytes (FP16) ≈ 67.1 MB per layer per forward step!
Across 32 transformer layers during backward pass:
Replicated Activation Footprint ≈ 2.15 GB just for duplicate routing buffers! 1. The Dynamic Routing Imbalance Dilemma
In a sparse MoE layer, input tokens $X \in \mathbb{R}^{S \times D}$ are assigned to experts via a parameterized router:
$$H(X) = \text{Softmax}(\text{TopK}(X \cdot W_{\text{gate}}, k))$$
Because token semantics dictate routing, expert workloads are inherently non-uniform. Certain specialized experts (such as code or mathematical reasoning experts) may receive hundreds of tokens in a batch, while generalist experts receive thousands, and dormant experts receive only a handful.
Historically, hardware accelerators (GPUs and TPUs) struggle with irregular, non-uniform matrix operations. Modern Tensor Cores achieve peak computing throughput only when executing dense General Matrix Multiplications (GEMMs) with fixed dimensions aligned to multiples of 16, 64, or 128.
To run these irregular token assignments on standard GEMM libraries (such as cuBLAS or CUTLASS), earlier MoE systems resorted to two flawed strategies:
Two Flawed Historical Strategies for GPU MoE Execution:
┌─────────────────────────────────────────────────────────────────────────────┐
│ Strategy A: Token Dropping with Fixed Capacity Factors (T5 / Switch / GShard) │
│ - Enforces an artificial capacity cap: C = (S * k / E) * Capacity_Factor. │
│ - Overloaded experts DROP excess tokens entirely! │
│ - Underloaded experts pad unused slots with ZERO TENSORS! │
│ ❌ Flaw: Dropping tokens degrades model reasoning quality and causes │
│ training instability; padding wastes 30% to 50% of GPU compute cycles! │
├─────────────────────────────────────────────────────────────────────────────┤
│ Strategy B: Block-Sparse Dropless MoE (MegaBlocks / Tutel) │
│ - Eliminates token dropping by reformulating expert execution as a │
│ Block-Sparse Row (BSR) matrix multiplication (bsr_dense_mm). │
│ - Avoids dropping tokens, but requires sorting all tokens by expert index, │
│ replicating tokens k times, and padding expert buckets to block sizes │
│ (e.g., multiples of 128 tokens) to satisfy hardware Tensor Core tiles. │
│ ❌ Flaw: Sorting overhead, intermediate memory replication, and block │
│ boundary padding still consume substantial GPU bandwidth and memory! │
└─────────────────────────────────────────────────────────────────────────────┘ 2. The Hidden Cost of Activation Materialization
Even in dropless block-sparse systems like MegaBlocks, the physical mechanics of sorting and gathering tokens create a massive memory tax:
- Replication and Permutation: Every token routed to $k$ experts must be read from global memory, duplicated $k$ times, and written back to a permuted global buffer.
- Intermediate GEMM Activations: The first feed-forward projection ($W_1$) produces an intermediate tensor of shape $[S \cdot k, D_{\text{ffn}}]$ that must be written to High-Bandwidth Memory (HBM). For SwiGLU architectures with $D_{\text{ffn}} = \frac{8}{3} D$, this intermediate activation buffer is enormous.
- Scatter-Add Accumulation: After passing through the second projection ($W_2$), the expert outputs must be multiplied by gating weights and reduced back into the original token order $[S, D]$ using an explicit scatter-add kernel.
This constant shuffling and materialization of intermediate buffers saturates the GPU memory bus, turning compute-bound matrix multiplications into memory-bandwidth-bound bottlenecks.
Enter ScatterMoE: Fused GPU Kernels for Sparse Mixture-of-Experts
Developed by systems researchers (arXiv:2403.08245) and integrated across high-performance frameworks including Together AI and Hugging Face, ScatterMoE fundamentally re-engineers sparse expert execution from the silicon up.
The Paradigm Shift: From Permute-GEMM-Unpermute to Fused Scatter-Gather
═══════════════════════════════════════════════════════════════════════════════
Conventional Block-Sparse MoE (MegaBlocks / Tutel):
Input [S, D] ──► Duplicate (k-copies) ──► Global Sort & Permute
──► Pad to 128 Tokens ──► Block-Sparse GEMM
──► Unpermute & Scatter-Add ──► Output [S, D]
* Memory: Severe k-copy activation blowup | Wasteful boundary padding!
ScatterMoE Pipeline (ParallelLinear):
Input [S, D] ──► Fused ParallelLinear (Direct Indexed Load & Tile Compute)
──► Fused In-Register Activation
──► Fused In-Place Atomic Scatter-Accumulation ──► Output [S, D]
* Memory: ZERO intermediate token copies | ZERO padding waste!
═══════════════════════════════════════════════════════════════════════════════ Rather than treating routing, projection, and accumulation as distinct operational stages separated by global memory reads and writes, ScatterMoE unifies these operations inside custom fused OpenAI Triton kernels.
Core Technical Breakthroughs of ScatterMoE
- Zero-Padding Execution: ScatterMoE operates on exact token assignments without rounding or padding expert token counts to block boundaries, completely eliminating inactive GPU compute cycles.
- Zero Token Replication: The input tensor $X \in \mathbb{R}^{S \times D}$ is read directly using indexed memory offsets. The kernel eliminates the $k$-fold duplication buffer, saving up to 40% peak activation memory.
- The
ParallelLinearPrimitive: Introduces a modular GPU building block that generalizes grouped and scattered linear transformations, enabling not only expert MLPs but also novel architectures like Mixture of Attention (MoA). - 38% Higher Training Throughput: By fusing gather, matrix multiplication, and scatter-add operations into single streaming multiprocessor (SM) executions, ScatterMoE dramatically reduces HBM memory traffic and outpaces MegaBlocks on NVIDIA A100 and H100 GPUs.
Deep Dive into the ScatterMoE Architecture
The architectural centerpiece of ScatterMoE is the ParallelLinear abstraction and its co-designed forward and backward Triton execution kernels.
The ParallelLinear Core Abstraction:
┌─────────────────────────────────────────────────────────────────────────────┐
│ Inputs: │
│ - Token Tensor: X in R^{S x D_in} (Unsorted, single copy in HBM) │
│ - Weight Tensor: W in R^{E x D_in x D_out} (Stacked expert weight matrices) │
│ - Routing Map: Sorted token indices & corresponding expert IDs │
│ - Gating Weights: routing probabilities in R^{S x k} │
└──────────────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────────────┐
│ Fused Triton GPU Kernel (Grid: Program IDs mapped to (Expert, Tile_M, Tile_N)│
│ │
│ [Step 1] In-Register Gather: │
│ Threads load token vectors directly from X using pointer offsets: │
│ ptr = X + token_idx[i] * stride_s │
│ │
│ [Step 2] Tile Matrix Multiplication: │
│ Compute tile GEMM against expert weight slice W[expert_id]: │
│ Acc = tl.dot(X_tile, W_tile) │
│ │
│ [Step 3] In-Register Gating & Fused Scatter-Write: │
│ Scale accumulator by gating weight: Acc = Acc * gate_weight │
│ Directly atomic-add or write to destination output buffer: │
│ Output[token_idx[i]] += Acc │
└─────────────────────────────────────────────────────────────────────────────┘ 1. The Anatomy of ParallelLinear
In standard deep learning libraries, a linear layer computes $Y = X W^T$. In a Mixture-of-Experts layer, we have $E$ distinct linear transformations ${W_e}_{e=1}^E$, where each input token $x_i$ is mapped to a dynamic subset of $k$ experts.
ScatterMoE models this through ParallelLinear:
$$\text{ParallelLinear}(X, W, \mathcal{I}, \mathcal{G})$$
where:
- $X \in \mathbb{R}^{S \times D_{\text{in}}}$ is the continuous input activation matrix.
- $W \in \mathbb{R}^{E \times D_{\text{in}} \times D_{\text{out}}}$ is the tensor of stacked expert weights.
- $\mathcal{I} \in \mathbb{Z}^{S \cdot k}$ contains the flattened expert-sorted token indices.
- $\mathcal{G} \in \mathbb{R}^{S \cdot k}$ contains the router coefficients.
Instead of materializing an intermediate expanded tensor $X_{\text{expanded}} \in \mathbb{R}^{S \cdot k \times D_{\text{in}}}$, ParallelLinear uses the index array $\mathcal{I}$ as an on-the-fly indirect addressing table directly inside the GPU register file.
2. Grouped Coordinate Indexing Without Padding
To avoid padding, ScatterMoE constructs an expert boundary histogram:
$$\text{Histogram}(e) = \sum_{i=1}^{S \cdot k} \mathbb{I}(\text{Expert}(i) = e)$$
$$\text{Offsets}(e) = \sum_{j \< e} \text{Histogram}(j)$$
During kernel launch:
- The GPU grid is configured with blocks of size $B_M \times B_N$ (e.g., $64 \times 64$ or $128 \times 128$).
- An expert with $N_e$ tokens is partitioned into $\lceil N_e / B_M \rceil$ computational tiles.
- For the final tile of an expert where $N_e \bmod B_M \neq 0$, Triton’s dynamic masking feature (
tl.load(..., mask=mask)) cleanly excludes out-of-bound indices without requiring any physical zero-padding in memory.
3. Fused Backward Pass Dynamics
The backward pass of a sparse MoE layer requires computing two distinct gradients:
- Gradient with respect to Input ($\nabla_X$): Requires scattering gradient tiles back to original token coordinates: $$\nabla X_i = \sum_{e \in \text{TopK}(i)} g_{i, e} \cdot \left( \nabla Y_{i, e} \cdot W_e \right)$$
- Gradient with respect to Weights ($\nabla W_e$): Requires accumulating outer products of tokens assigned to expert $e$: $$\nabla W_e = \sum_{i \in \text{Tokens}(e)} g_{i, e} \cdot \left( x_i^T \cdot \nabla Y_{i, e} \right)$$
ScatterMoE implements dedicated backward Triton kernels that fuse weight gradient reduction directly within shared memory (SMem), eliminating atomic write contention on global memory and maintaining identical numerical gradients to standard PyTorch autograd.
Hands-On Implementation: Building a Fused ScatterMoE Module
Let’s implement the core mechanics of ScatterMoE: writing the fused gather-scatter Triton kernel and assembling the complete ParallelLinear PyTorch module for an MoE Feed-Forward Network (FFN).
1. The Fused Scatter-GEMM Triton Kernel
Below is the Triton kernel that executes the fused gather, matrix multiply, and scatter-write:
import torch
import triton
import triton.language as tl
@triton.jit
def _parallel_linear_fwd_kernel(
X_ptr, W_ptr, Out_ptr,
sorted_token_idxs_ptr, expert_offsets_ptr, gates_ptr,
stride_xs, stride_xd,
stride_we, stride_wd_in, stride_wd_out,
stride_outs, stride_outd,
D_in: tl.constexpr, D_out: tl.constexpr,
BLOCK_M: tl.constexpr, BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr
):
"""
Fused Scatter-GEMM Triton Kernel.
Gathers tokens via sorted indices, performs GEMM against expert weights,
applies router gating, and scatters outputs directly to destination memory.
"""
# Identify tile coordinates
tile_m_id = tl.program_id(0)
tile_n_id = tl.program_id(1)
expert_id = tl.program_id(2)
# Determine token range for this expert
expert_start = tl.load(expert_offsets_ptr + expert_id)
expert_end = tl.load(expert_offsets_ptr + expert_id + 1)
num_expert_tokens = expert_end - expert_start
# Check if this program block falls within the expert's tokens
token_offset_in_expert = tile_m_id * BLOCK_M
if token_offset_in_expert >= num_expert_tokens:
return
# Compute M indices
offs_m = token_offset_in_expert + tl.arange(0, BLOCK_M)
mask_m = offs_m < num_expert_tokens
# Load original token indices and gating weights
global_sorted_pos = expert_start + offs_m
token_indices = tl.load(sorted_token_idxs_ptr + global_sorted_pos, mask=mask_m, other=0)
gate_weights = tl.load(gates_ptr + global_sorted_pos, mask=mask_m, other=0.0)
# Column indices for D_out
offs_n = tile_n_id * BLOCK_N + tl.arange(0, BLOCK_N)
mask_n = offs_n < D_out
# Initialize accumulator register
acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
# Loop over inner dimension D_in
offs_k = tl.arange(0, BLOCK_K)
for k in range(0, D_in, BLOCK_K):
# Gather input tokens from X using token_indices
x_ptrs = X_ptr + (token_indices[:, None] * stride_xs) + ((k + offs_k[None, :]) * stride_xd)
x_mask = mask_m[:, None] & ((k + offs_k[None, :]) < D_in)
x_tile = tl.load(x_ptrs, mask=x_mask, other=0.0)
# Load expert weight tile: W[expert_id, k:k+BLOCK_K, offs_n]
w_ptrs = (
W_ptr +
(expert_id * stride_we) +
((k + offs_k[:, None]) * stride_wd_in) +
(offs_n[None, :] * stride_wd_out)
)
w_mask = ((k + offs_k[:, None]) < D_in) & mask_n[None, :]
w_tile = tl.load(w_ptrs, mask=w_mask, other=0.0)
# Perform tile dot product
acc += tl.dot(x_tile, w_tile)
# Apply gating scaling: Y = (X * W) * Gate
acc = acc * gate_weights[:, None]
# Scatter-accumulate directly to output buffer
out_ptrs = Out_ptr + (token_indices[:, None] * stride_outs) + (offs_n[None, :] * stride_outd)
out_mask = mask_m[:, None] & mask_n[None, :]
# Atomic addition prevents race conditions across different experts routing to same token
tl.atomic_add(out_ptrs, acc.to(tl.float16), mask=out_mask) 2. The Complete ParallelLinear PyTorch Layer
Now let’s build the complete PyTorch module that wraps the kernel, handles routing, and provides standard forward execution:
import torch
import torch.nn as nn
import torch.nn.functional as F
class ParallelLinear(nn.Module):
"""
ScatterMoE ParallelLinear Module.
Executes grouped linear transformations across multiple experts without token padding.
"""
def __init__(self, num_experts: int, in_features: int, out_features: int, bias: bool = False):
super().__init__()
self.num_experts = num_experts
self.in_features = in_features
self.out_features = out_features
# Stacked expert weights: [E, In_Features, Out_Features]
self.weight = nn.Parameter(
torch.empty((num_experts, in_features, out_features), dtype=torch.float16)
)
if bias:
self.bias = nn.Parameter(torch.zeros((num_experts, out_features), dtype=torch.float16))
else:
self.register_parameter("bias", None)
self.reset_parameters()
def reset_parameters(self):
# Kaiming uniform initialization across all expert slices
nn.init.kaiming_uniform_(self.weight, a=math.sqrt(5))
def forward(
self,
x: torch.Tensor,
expert_indices: torch.Tensor,
routing_weights: torch.Tensor
) -> torch.Tensor:
"""
Forward execution of ParallelLinear.
Args:
x: Input tensor [Batch * SeqLen, In_Features]
expert_indices: [Batch * SeqLen, Top_K] (Target expert ID for each route)
routing_weights: [Batch * SeqLen, Top_K] (Softmax gate probabilities)
Returns:
out: Accumulated output tensor [Batch * SeqLen, Out_Features]
"""
S, D_in = x.shape
K = expert_indices.shape[1]
# Flatten routing assignments
flat_expert_ids = expert_indices.view(-1)
flat_token_ids = torch.arange(S, device=x.device).repeat_interleave(K)
flat_gates = routing_weights.view(-1)
# Sort tokens by expert ID to establish contiguous execution ranges
sort_order = torch.argsort(flat_expert_ids)
sorted_tokens = flat_token_ids[sort_order].contiguous()
sorted_experts = flat_expert_ids[sort_order].contiguous()
sorted_gates = flat_gates[sort_order].contiguous()
# Compute expert offsets in sorted array
expert_counts = torch.bincount(sorted_experts, minlength=self.num_experts)
expert_offsets = torch.zeros(self.num_experts + 1, dtype=torch.int32, device=x.device)
expert_offsets[1:] = torch.cumsum(expert_counts, dim=0)
# Allocate zeroed output buffer
out = torch.zeros((S, self.out_features), dtype=x.dtype, device=x.device)
# Launch configuration
BLOCK_M = 64
BLOCK_N = 64
BLOCK_K = 32
# Compute grid dimensions
max_tokens_per_expert = expert_counts.max().item() if expert_counts.numel() > 0 else 0
grid_m = triton.cdiv(max_tokens_per_expert, BLOCK_M)
grid_n = triton.cdiv(self.out_features, BLOCK_N)
grid = (grid_m, grid_n, self.num_experts)
if max_tokens_per_expert > 0:
_parallel_linear_fwd_kernel[grid](
x, self.weight, out,
sorted_tokens, expert_offsets, sorted_gates,
x.stride(0), x.stride(1),
self.weight.stride(0), self.weight.stride(1), self.weight.stride(2),
out.stride(0), out.stride(1),
D_in=D_in, D_out=self.out_features,
BLOCK_M=BLOCK_M, BLOCK_N=BLOCK_N, BLOCK_K=BLOCK_K
)
return out 3. Assembling the ScatterMoE FFN Block
Using two ParallelLinear layers, we can construct the full SwiGLU expert feed-forward block:
class ScatterMoEBlock(nn.Module):
"""
Production-grade SwiGLU Mixture-of-Experts Layer built on ScatterMoE ParallelLinear.
"""
def __init__(self, num_experts: int, d_model: int, d_ffn: int, top_k: int = 2):
super().__init__()
self.num_experts = num_experts
self.d_model = d_model
self.d_ffn = d_ffn
self.top_k = top_k
# Router network
self.gate = nn.Linear(d_model, num_experts, bias=False)
# Up-projection (fuses gate and up for SwiGLU) and Down-projection
self.w1 = ParallelLinear(num_experts, d_model, d_ffn * 2)
self.w2 = ParallelLinear(num_experts, d_ffn, d_model)
def forward(self, x: torch.Tensor) -> torch.Tensor:
orig_shape = x.shape
x_flat = x.view(-1, self.d_model)
# Step 1: Compute router logits and select top-k experts
router_logits = self.gate(x_flat)
weights, indices = torch.topk(F.softmax(router_logits, dim=-1), self.top_k, dim=-1)
# Renormalize top-k probabilities
weights = weights / weights.sum(dim=-1, keepdim=True)
# Step 2: Up-projection with fused routing
# Output shape: [S, d_ffn * 2]
h_up = self.w1(x_flat, indices, weights)
# Step 3: SwiGLU activation
gate, up = h_up.chunk(2, dim=-1)
h_act = F.silu(gate) * up
# Step 4: Down-projection back to model dimension
out = self.w2(h_act, indices, torch.ones_like(weights))
return out.view(orig_shape) Benchmark Comparison: ScatterMoE vs. MegaBlocks and Tutel
To evaluate execution speed and memory efficiency, researchers benchmarked ScatterMoE against leading sparse MoE systems across 8x NVIDIA A100-80GB and H100-80GB GPU platforms using Mixtral-style configurations (8 experts, top-2 routing, sequence lengths from 2K to 32K).
1. Training Throughput and Latency (Mixtral 8x7B Layer Benchmark)
The table below contrasts per-step execution time and relative throughput across batch sizes:
| Implementation | Token Padding Strategy | Intermediate Replicated Copy | Forward Step Latency (ms) | Peak Activation Memory (GB) | Relative Training Throughput |
|---|---|---|---|---|---|
| Standard PyTorch (Naive) | Full Zero Padding | Yes ($k \times$) | 42.8 ms | 14.6 GB | 1.00x (Baseline) |
| Tutel MoE | Capacity Factor Drop | Yes ($k \times$) | 28.4 ms | 11.2 GB | 1.51x |
| MegaBlocks (dMoE) | Dropless Block-Sparse | Yes ($k \times$) | 21.6 ms | 9.8 GB | 1.98x |
| ScatterMoE (ParallelLinear) | Zero Padding | No (Direct Index) | 15.6 ms | 5.9 GB | 2.74x (38% faster than MegaBlocks) |
Memory Footprint Comparison (Peak Activation Memory):
Standard PyTorch: ██████████████████████ (14.6 GB)
Tutel: ████████████████ (11.2 GB)
MegaBlocks: ██████████████ (9.8 GB)
ScatterMoE: ████████ (5.9 GB - 40% Reduction!)
═══════════════════════════════════════════════════════════════════════════════
ScatterMoE cuts activation memory by 40% by eliminating intermediate token buffers,
preventing Out-Of-Memory crashes during large-batch and long-context training! 2. Generalization to Mixture of Attention (MoA)
A unique architectural advantage of ScatterMoE’s ParallelLinear primitive is that it generalizes beyond expert MLPs to Mixture of Attention (MoA). In MoA, multiple parallel attention heads are conditionally routed based on token query semantics.
Because MegaBlocks relies strictly on 2D block-sparse matrix representations, extending it to 4D multi-head attention requires painful manual tensor unrolling. In contrast, ParallelLinear treats attention head projections as just another parallel linear dimension, executing MoA layers with zero code modifications and preserving the same 38% throughput advantage.
3. Detailed Architectural Feature Matrix
| Feature / Capability | Standard PyTorch MoE | Tutel | MegaBlocks (dMoE) | ScatterMoE |
|---|---|---|---|---|
| Dropless Routing | Yes | No (Capacity Drops) | Yes | Yes (Full Dropless) |
| Padding Requirement | Pad to Max Capacity | Pad to Capacity Factor | Pad to 128-token Block | Zero Padding (Exact) |
| Activation Duplication | Yes ($k \times$ copy) | Yes ($k \times$ copy) | Yes ($k \times$ copy) | Zero (In-Place Indexing) |
| Implementation Language | Python / C++ | CUDA / C++ | CUDA / C++ | OpenAI Triton (Pure Python) |
| Mixture of Attention (MoA) | Complex | Unsupported | Unsupported | Native Support |
| Kernel Fusion Level | Low | Moderate | Moderate (BSR GEMM) | High (Gather-GEMM-Scatter Fused) |
Production Deployment: Integrating ScatterMoE into Enterprise Clusters
Deploying ScatterMoE into enterprise clusters provides immediate training throughput gains and enables larger batch sizes without out-of-memory errors. When architecting enterprise MoE infrastructure, consult AiFetchly enterprise pricing and our developer API documentation.
1. Step-by-Step Production Configuration
Follow these steps to deploy ScatterMoE within a PyTorch-based distributed training repository:
Step 1: Install ScatterMoE and Triton Install the official ScatterMoE package from GitHub:
git clone https://github.com/shawntan/scattermoe.git
cd scattermoe
pip install -e . Step 2: Replace Hugging Face Transformers MoE Block Below is a drop-in patch replacing the default Mixtral sparse feed-forward layer with ScatterMoE:
import torch
import torch.nn as nn
from transformers.models.mixtral.modeling_mixtral import MixtralSparseMoeBlock
from scattermoe.mlp import MLP as ScatterMoEMLP
class PatchedMixtralMoE(nn.Module):
"""Drop-in high-performance replacement for Hugging Face MixtralSparseMoeBlock."""
def __init__(self, config):
super().__init__()
self.hidden_dim = config.hidden_size
self.ffn_dim = config.intermediate_size
self.num_experts = config.num_local_experts
self.top_k = config.num_experts_per_tok
# Router
self.gate = nn.Linear(self.hidden_dim, self.num_experts, bias=False)
# Fused ScatterMoE MLP Layer
self.scatter_mlp = ScatterMoEMLP(
num_experts=self.num_experts,
input_size=self.hidden_dim,
hidden_size=self.ffn_dim,
top_k=self.top_k,
activation="silu"
)
def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
orig_shape = hidden_states.shape
x_2d = hidden_states.view(-1, self.hidden_dim)
# Compute gating
router_logits = self.gate(x_2d)
routing_weights = torch.softmax(router_logits, dim=-1)
# Execute fused ScatterMoE computation
output = self.scatter_mlp(x_2d, routing_weights)
return output.view(orig_shape)
def patch_model_with_scattermoe(model):
"""Recursively replaces all standard MoE blocks with ScatterMoE."""
for name, module in model.named_modules():
if isinstance(module, MixtralSparseMoeBlock):
parent_name = name.rsplit(".", 1)[0]
child_name = name.rsplit(".", 1)[1]
parent = model.get_submodule(parent_name)
setattr(parent, child_name, PatchedMixtralMoE(model.config))
print("Successfully patched model with ScatterMoE fused kernels!")
return model 2. Operational Tuning and Best Practices
When operating ScatterMoE in high-concurrency production environments, apply the following engineering practices:
-
Tune Triton Tile Dimensions for Target Silicon:
- On NVIDIA Ampere (A100): Configure
BLOCK_M = 64, BLOCK_N = 64, BLOCK_K = 32to optimize shared memory occupancy. - On NVIDIA Hopper (H100/H200): Increase
BLOCK_M = 128, BLOCK_N = 128to take full advantage of Hopper’s larger 228KB Shared Memory per SM and asynchronous Tensor Memory Accelerator (TMA) pipelines.
- On NVIDIA Ampere (A100): Configure
-
Combine with Tensor and Pipeline Parallelism: ScatterMoE is fully compatible with Megatron-LM and DeepSpeed Expert Parallelism. Distribute expert weights across expert-parallel groups while using ScatterMoE to handle local multi-expert routing.
-
Reinvest Saved Memory into Batch Scale: Because ScatterMoE cuts activation memory by 40%, increase per-device micro-batch size by 1.5x to 2x. This pushes Tensor Core arithmetic intensity higher, yielding compounded training speedups.
By eliminating artificial token padding and fusing gather-scatter operations directly into high-speed Triton kernels, ScatterMoE removes the historical tax of sparse routing—empowering AI clusters to train and serve frontier Mixture-of-Experts models at peak hardware efficiency.