Mixture of Experts (MoE)
About
The d9d.module.block.moe package implements sparse Mixture-of-Experts layers. MoELayer combines a router, a token dispatcher and the experts, with an optional shared expert.
Expert Parallelism
To set up expert parallelism, see Horizontal Parallelism. Expert parallelism requires the d9d[backend-expert-parallel-deepep] extra. You must build and install DeepEP manually first.
Components
Router
TopKRouter is a learned router that selects the top-k experts for each token. It computes the routing probabilities in fp32 for numerical stability. An optional expert bias changes which experts are selected but not their probabilities. You can use it for loss-free load balancing.
Token Dispatcher
ExpertCommunicationHandler is the interface that moves tokens to their experts and back.
NoCommunicationHandleris the default. It is used when all experts are local, such as on a single GPU or with tensor parallelism.- With expert parallelism,
parallelize_expert_parallelswitches the layer to a DeepEP handler. It runs the all-to-all communication over NVLink and RDMA with DeepEP.
Experts
GroupedSwiGLU holds a set of SwiGLU experts. It does not loop over experts. It runs all experts in one grouped GEMM per projection, for any number of tokens per expert. It computes SiLU(gate(x)) * up(x) with a fused Triton kernel.
Kernel Benchmarks (bf16, H100)

Grouped GEMM Backends
GroupedSwiGLU runs its projections with GroupedLinear, which passes the grouped GEMM to a backend. The available backends are:
kind |
Library | GPUs | Extra |
|---|---|---|---|
"quack" |
QuACK | sm90+ | d9d[backend-grouped-mm-quack] |
"grouped_gemm" |
grouped_gemm | sm80+ | d9d[backend-grouped-mm-grouped-gemm] |
"torch" |
PyTorch grouped_mm |
Any | - |
"eager" |
Plain PyTorch ops | Any | - |
The "quack" backend compiles its kernels on first use and does not support fp32 on sm90. The "torch" backend is fast only for bf16 on sm90 and sm100. The "eager" backend has no extra dependencies. Use it as a fallback and as a correctness reference for the other backends.
Backend Selection
A layer picks its backend in this order:
- The
grouped_mm_backendconfiguration passed toMoELayer,GroupedSwiGLUorGroupedLinear. - The
D9D_BACKEND_AUTO_GROUPED_MMenvironment variable. - Auto-detection:
"quack"on sm90+ if it is installed,"grouped_gemm"on sm80 if it is installed,"torch"on sm90 and sm100, and"eager"otherwise.
D9D_BACKEND_AUTO_GROUPED_MM holds a backend configuration as JSON, keyed by the kind field:
Shared Experts
A shared expert processes every token, whatever the router chooses. Configure it with SharedExpertParameters. It can scale its output with a learned sigmoid gate (enable_gate).
Usage
API Reference
d9d.module.block.moe
Building blocks for Mixture-of-Experts (MoE) layers.
GroupedLinear
Bases: Module, ModuleLateInit
Linear layer with a separate transformation for each group of tokens, computed with grouped GEMM.
Each group (expert) has its own weight and processes a variable number of tokens. This is the compute core of the Mixture-of-Experts layer.
__init__(n_groups, in_features, out_features, device=None, dtype=None, grouped_mm_backend=None)
Constructs the GroupedLinear object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
n_groups
|
int
|
Number of groups (experts). |
required |
in_features
|
int
|
Size of each input sample. |
required |
out_features
|
int
|
Size of each output sample. |
required |
device
|
device | str | None
|
Target device. |
None
|
dtype
|
dtype | None
|
Target data type. |
None
|
grouped_mm_backend
|
AnyGroupedMmBackendConfig | None
|
Grouped GEMM backend. If |
None
|
forward(x, x_groups)
Performs the grouped matrix multiplication.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
Input tokens of all groups, with the tokens of each group stored together.
Shape: |
required |
x_groups
|
ExpertTokenGroups
|
How the tokens are split between groups. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The output tensor. Shape: |
reset_parameters()
Initializes the weights from U(-1 / sqrt(in_features), 1 / sqrt(in_features)).
with_features(in_features, out_features)
Creates a new GroupedLinear like this one, but with other feature sizes.
The new layer has the same number of groups, grouped GEMM backend configuration, device and dtype, and freshly initialized weights. Layers attached to this one, such as LoRA adapters, use it to run on the same backend without knowing which one it is.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
in_features
|
int
|
Input hidden size of the new layer. |
required |
out_features
|
int
|
Output hidden size of the new layer. |
required |
Returns:
| Type | Description |
|---|---|
GroupedLinear
|
The new layer. |
GroupedSwiGLU
Bases: Module, ModuleLateInit
Set of SwiGLU experts that run with grouped GEMM.
Each expert computes down_proj(SiLU(gate_proj(x)) * up_proj(x)). All experts run in one grouped GEMM
per projection, without padding or masking.
__init__(hidden_dim, intermediate_dim, num_experts, grouped_mm_backend=None)
Constructs the GroupedSwiGLU object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_dim
|
int
|
Hidden size of the input and output. |
required |
intermediate_dim
|
int
|
Intermediate size of each expert. |
required |
num_experts
|
int
|
Number of experts held by this module. |
required |
grouped_mm_backend
|
AnyGroupedMmBackendConfig | None
|
Grouped GEMM backend for the projections. If |
None
|
forward(permuted_x, permuted_probs, expert_groups)
Computes expert outputs for sorted input tokens.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
permuted_x
|
Tensor
|
Input tokens sorted by their assigned expert. Shape: |
required |
permuted_probs
|
Tensor
|
Routing probabilities of the sorted tokens. Shape: |
required |
expert_groups
|
ExpertTokenGroups
|
How the tokens are split between consecutive experts. |
required |
Returns:
| Name | Type | Description |
|---|---|---|
Tensor
|
The expert outputs weighted by the routing probabilities, in the sorted order of |
|
Shape |
Tensor
|
|
reset_parameters()
Resets module parameters.
MoELayer
Bases: Module, ModuleLateInit
Mixture-of-Experts (MoE) block with routing, communication and expert computation.
The layer combines:
- Router: Selects experts for each token.
- Communicator: Dispatches tokens to local or remote experts (expert parallelism).
- Experts: Compute the outputs with grouped SwiGLU.
__init__(hidden_dim, intermediate_dim_grouped, num_grouped_experts, top_k, router_renormalize_probabilities, shared_expert=None, grouped_mm_backend=None)
Constructs the MoELayer object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_dim
|
int
|
Hidden size. |
required |
intermediate_dim_grouped
|
int
|
Intermediate size of each expert FFN. |
required |
num_grouped_experts
|
int
|
Total number of experts. |
required |
top_k
|
int
|
Number of experts to route each token to. |
required |
router_renormalize_probabilities
|
bool
|
If |
required |
shared_expert
|
SharedExpertParameters | None
|
Optional configuration for a shared expert. |
None
|
grouped_mm_backend
|
AnyGroupedMmBackendConfig | None
|
Grouped GEMM backend for the experts. If |
None
|
enable_distributed_communicator(group)
Switches from local communication to distributed DeepEP communication.
Call it during model initialization if the model runs with expert parallelism.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
group
|
ProcessGroup
|
Process group that spans the expert-parallel ranks. |
required |
forward(hidden_states)
reset_parameters()
Resets module parameters.
reset_stats()
Resets the expert load balancing counters.
RoutingResult
dataclass
SharedExpertParameters
SharedSwiGLU
Bases: Module, ModuleLateInit
Shared expert: a SwiGLU FFN with an optional sigmoid output gate.
__init__(hidden_size, params)
Constructs the SharedSwiGLU object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_size
|
int
|
Hidden size. |
required |
params
|
SharedExpertParameters
|
Shared expert configuration. |
required |
forward(hidden_states)
reset_parameters()
Resets module parameters.
TopKRouter
Bases: Module, ModuleLateInit
Router that selects the top-k experts for each token with a learned gate.
The router:
- Projects input tokens to one score per expert.
- Applies softmax to get expert probabilities.
- Selects the
top_kexperts with the highest probabilities. The optional expert bias affects only this selection, not the returned probabilities. - Optionally renormalizes the selected probabilities to sum to 1.
__init__(dim, num_experts, top_k, renormalize_probabilities, enable_expert_bias=False)
Constructs the TopKRouter object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dim
|
int
|
Hidden size. |
required |
num_experts
|
int
|
Total number of experts to choose from. |
required |
top_k
|
int
|
Number of experts to select for each token. |
required |
renormalize_probabilities
|
bool
|
If |
required |
enable_expert_bias
|
bool
|
If |
False
|
forward(hidden_states)
Computes routing decisions for the input tokens.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_states
|
Tensor
|
Input tokens. Shape: |
required |
Returns:
| Type | Description |
|---|---|
RoutingResult
|
The indices of the selected experts and their probabilities. |
reset_parameters()
Resets module parameters.
d9d.module.block.moe.communications
Communication strategies that route tokens between MoE experts.
ExpertCommunicationHandler
Bases: ABC
Abstract base class for Mixture-of-Experts communication strategies.
combine(hidden_states)
abstractmethod
Returns processed hidden states to their original order and rank.
Replicated tokens are summed. Must be called after dispatch.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_states
|
Tensor
|
Processed hidden states. Shape: |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The combined hidden states in the original order. Shape: |
dispatch(hidden_states, topk_ids, topk_weights)
abstractmethod
Routes local hidden states to their target experts, which can be on other ranks.
Tokens assigned to several experts are replicated. The received tokens are sorted by expert index, as grouped GEMM requires.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
hidden_states
|
Tensor
|
Input tokens. Shape: |
required |
topk_ids
|
Tensor
|
Indices of the experts selected for each token. Shape: |
required |
topk_weights
|
Tensor
|
Routing weights of the selected experts. Shape: |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
A tuple of: |
Tensor
|
|
ExpertTokenGroups
|
|
tuple[Tensor, Tensor, ExpertTokenGroups]
|
|
NoCommunicationHandler
Bases: ExpertCommunicationHandler
MoE communication handler for the case when all experts are on the local device.
This handler does no communication. It only sorts the tokens by expert index.
__init__(num_experts)
Constructs the NoCommunicationHandler object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
num_experts
|
int
|
Number of local experts. |
required |
d9d.module.block.moe.grouped_mm
Provides pluggable grouped GEMM backends for Mixture-of-Experts layers.
EagerGroupedMmBackendConfig
ExpertTokenGroups
How the permuted tokens are split between consecutive experts.
offsets
property
Token offsets of the experts with a leading zero, on the device. Shape: (num_experts + 1,).
__init__(counts, host_counts=None)
Constructs the ExpertTokenGroups object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
counts
|
Tensor
|
Tokens per expert on the device the permuted tokens live on. Shape: |
required |
host_counts
|
Tensor | None
|
The same counts on the host, if the producer already has them. Shape: |
None
|
host_counts()
Returns the tokens per expert on the host.
If the producer did not provide them, the first call copies them from the device and synchronizes with it. Later calls return the same tensor.
Returns:
| Type | Description |
|---|---|
Tensor
|
The tokens per expert. Shape: |
GroupedGemmBackendConfig
Bases: BaseModel
Configuration for the grouped_gemm library backend.
Needs the per-expert counts on the host; getting them from the device synchronizes with it. Requires the
d9d[backend-grouped-mm-grouped-gemm] extra.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
Literal['grouped_gemm']
|
Discriminator field. Always |
GroupedMmBackend
Bases: Protocol
Protocol for grouped matrix multiplication backends used by GroupedLinear.
__call__(x, weight, groups)
Multiplies the rows of every group by the weight of that group.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
x
|
Tensor
|
Rows of all groups, sorted by group. Shape: |
required |
weight
|
Tensor
|
Per-group weights. Shape: |
required |
groups
|
ExpertTokenGroups
|
How the rows are split between groups. |
required |
Returns:
| Type | Description |
|---|---|
Tensor
|
The result. Shape: |
GroupedMmParameters
dataclass
QuackGroupedMmBackendConfig
Bases: BaseModel
Configuration for the QuACK (CuTe-DSL) variable-length GEMM backend.
Reads the expert offsets on the device, so it does not need the per-expert counts on the host. Kernels are
JIT-compiled on first use. Requires the d9d[backend-grouped-mm-quack] extra.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
Literal['quack']
|
Discriminator field. Always |
TorchGroupedMmBackendConfig
Bases: BaseModel
Configuration for the PyTorch torch.nn.functional.grouped_mm backend.
Reads the expert offsets on the device, so it does not need the per-expert counts on the host. Fast only for bf16 on sm90 and sm100.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
Literal['torch']
|
Discriminator field. Always |
build_grouped_mm_backend(params, backend_config)
Builds the grouped GEMM backend for the given configuration.
If backend_config is None, the backend config is read from the D9D_BACKEND_AUTO_GROUPED_MM
environment variable (a JSON backend config). If the variable is not set, the factory picks QuACK on sm90+ if it
is installed, grouped_gemm on sm80 if it is installed, PyTorch grouped_mm on sm90 and sm100, and the eager
backend otherwise.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
params
|
GroupedMmParameters
|
Structural layer parameters the backend must support. |
required |
backend_config
|
AnyGroupedMmBackendConfig | None
|
Explicit grouped GEMM backend configuration, or |
required |
Returns:
| Type | Description |
|---|---|
GroupedMmBackend
|
The grouped GEMM backend. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the backend configuration type is unknown. |