Skip to content

Commit 1c51bd1

Browse files
committed
[MoE] Support tokamax GMM backend for TransformerFeedForwardDropFreeMoE
GitOrigin-RevId: c175da2
1 parent 3bd8717 commit 1c51bd1

7 files changed

Lines changed: 638 additions & 8 deletions

File tree

axlearn/common/mixture_of_experts.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import jax
2424
import jax.numpy as jnp
2525
import numpy as np
26+
import tokamax
2627
from absl import logging
2728
from jax import lax
2829
from jax.experimental.pjit import pjit
@@ -80,6 +81,26 @@ class GateNoise(enum.Enum):
8081
GUMBEL = "GUMBEL"
8182

8283

84+
class GMMBackend(enum.Enum):
85+
"""Backend implementations for Grouped Matrix Multiplication (GMM).
86+
87+
Used in TransformerFeedForwardDropFreeMoE to select between different
88+
GMM implementations for experimentation and performance comparison.
89+
"""
90+
91+
# Use Pallas/Triton-based GMM kernel.
92+
# Requires tiling configuration. Default backend.
93+
PALLAS = "PALLAS"
94+
95+
# Use JAX native jax.lax.ragged_dot.
96+
# Ignores tiling configuration. Useful for experimentation.
97+
RAGGED_DOT = "RAGGED_DOT"
98+
99+
# Use tokamax.ragged_dot (third-party implementation).
100+
# Has the same API as jax.lax.ragged_dot. Ignores tiling configuration.
101+
TOKAMAX = "TOKAMAX"
102+
103+
83104
# Type definitions for configurable functions.
84105
TopKFn = Callable[[Tensor, int], Tuple[Tensor, Tensor]]
85106
ScoreFn = Callable[[Tensor, int], Tensor]
@@ -2229,11 +2250,19 @@ class TransformerFeedForwardDropFreeMoE(TransformerFeedForwardMoE):
22292250
class Config(TransformerFeedForwardMoE.Config):
22302251
"""Config for TransformerFeedForwardDropFreeMoE."""
22312252

2253+
# GMM backend selection for experimentation and performance comparison.
2254+
# PALLAS: Uses Pallas/Triton-based GMM kernel - requires tiling config.
2255+
# RAGGED_DOT: Uses JAX native jax.lax.ragged_dot - ignores tiling config.
2256+
# TOKAMAX: Uses Tokamax GMM kernel - ignores tiling config.
2257+
# None: Defaults to PALLAS for backward compatibility.
2258+
gmm_backend: Optional[GMMBackend] = None
2259+
22322260
# Adjustable 3-tuple of ints to use the gmm kernel for the best performance.
22332261
# tiling[0] is the block size for the number of tokens dimension.
22342262
# tiling[1] is the block size for the model_dim.
22352263
# tiling[2] is the block size for the hidden_dim.
22362264
# The tiling blocks have to be multiples of 128.
2265+
# Note: This config is only used when gmm_backend is PALLAS (or None).
22372266
tiling: Required[Union[InstantiableConfig, tuple[int, int, int]]] = REQUIRED
22382267
# How to partition the input batch with the expected keys below.
22392268
input_dim_to_partition_spec: dict[str, Optional[PartitionSpec]] = {
@@ -2259,6 +2288,12 @@ class Config(TransformerFeedForwardMoE.Config):
22592288
# (so called Auxiliary-Loss-Free Load Balancing strategy) in DeepSeek V3.
22602289
# It's an experimental feature, so use it with caution.
22612290
seq_load_balance_loss_weight: Optional[float] = None
2291+
# Implementation backends for tokamax.ragged_dot.
2292+
# When gmm_backend is TOKAMAX, this specifies which implementation(s) to use.
2293+
# If None, tokamax will use its default implementation selection.
2294+
# Example: ["triton", "xla"] to use triton first, falling back to xla.
2295+
# See tokamax documentation for available implementations.
2296+
tokamax_implementation: Optional[Sequence[str]] = None
22622297

22632298
@classmethod
22642299
def default_config(cls):
@@ -2284,6 +2319,96 @@ def __init__(self, cfg: Config, *, parent: Module):
22842319
), "When interpret == True, please set preferred_element_type explicitly"
22852320

22862321
def _padded_gmm(self, lhs, rhs, tokens_per_expert):
2322+
cfg = self.config
2323+
# Determine which backend to use (default to PALLAS for backward compatibility)
2324+
backend = cfg.gmm_backend if cfg.gmm_backend is not None else GMMBackend.PALLAS
2325+
2326+
if backend == GMMBackend.RAGGED_DOT:
2327+
return self._ragged_dot_gmm(lhs, rhs, tokens_per_expert)
2328+
elif backend == GMMBackend.TOKAMAX:
2329+
return self._tokamax_gmm(lhs, rhs, tokens_per_expert)
2330+
else:
2331+
return self._pallas_gmm(lhs, rhs, tokens_per_expert)
2332+
2333+
def _ragged_dot_gmm(self, lhs, rhs, tokens_per_expert):
2334+
"""Performs grouped matrix multiplication using jax.lax.ragged_dot.
2335+
2336+
This is an alternative to the Pallas/Triton-based GMM kernel that uses
2337+
JAX's native ragged_dot operation. It ignores tiling configuration and
2338+
is useful for experimentation and performance comparison.
2339+
2340+
Args:
2341+
lhs: Left-hand side tensor of shape [num_tokens, input_dim].
2342+
rhs: Right-hand side tensor of shape [num_experts, input_dim, output_dim].
2343+
tokens_per_expert: Tensor of shape [num_experts] indicating the number
2344+
of tokens assigned to each expert.
2345+
2346+
Returns:
2347+
Output tensor of shape [num_tokens, output_dim].
2348+
"""
2349+
cfg = self.config
2350+
preferred_element_type = cfg.preferred_element_type or jnp.bfloat16
2351+
2352+
# jax.lax.ragged_dot expects:
2353+
# - lhs: [num_tokens, input_dim]
2354+
# - rhs: [num_experts, input_dim, output_dim]
2355+
# - group_sizes: [num_experts] - number of tokens per expert
2356+
# Returns: [num_tokens, output_dim]
2357+
results = jax.lax.ragged_dot(
2358+
lhs,
2359+
rhs,
2360+
tokens_per_expert,
2361+
preferred_element_type=preferred_element_type,
2362+
)
2363+
return results
2364+
2365+
def _tokamax_gmm(self, lhs, rhs, tokens_per_expert):
2366+
"""Performs grouped matrix multiplication using tokamax.ragged_dot.
2367+
2368+
This is an alternative to the Pallas/Triton-based GMM kernel that uses
2369+
the tokamax library's ragged_dot operation. It has the same API as
2370+
jax.lax.ragged_dot and ignores tiling configuration.
2371+
2372+
Args:
2373+
lhs: Left-hand side tensor of shape [num_tokens, input_dim].
2374+
rhs: Right-hand side tensor of shape [num_experts, input_dim, output_dim].
2375+
tokens_per_expert: Tensor of shape [num_experts] indicating the number
2376+
of tokens assigned to each expert.
2377+
2378+
Returns:
2379+
Output tensor of shape [num_tokens, output_dim].
2380+
"""
2381+
2382+
cfg = self.config
2383+
preferred_element_type = cfg.preferred_element_type or jnp.bfloat16
2384+
2385+
# tokamax.ragged_dot has the same API as jax.lax.ragged_dot.
2386+
# The implementation parameter controls which backend(s) to use.
2387+
# If None, tokamax uses its default selection.
2388+
# Example: ["triton", "xla"] uses triton first, falling back to xla
2389+
# if triton raises NotImplementedError (e.g., for non-default dimension
2390+
# numbers in the backward pass on B200/SM100 GPUs).
2391+
results = tokamax.ragged_dot(
2392+
lhs,
2393+
rhs,
2394+
tokens_per_expert,
2395+
preferred_element_type=preferred_element_type,
2396+
implementation=cfg.tokamax_implementation,
2397+
)
2398+
return results
2399+
2400+
def _pallas_gmm(self, lhs, rhs, tokens_per_expert):
2401+
"""Performs grouped matrix multiplication using Pallas/Triton-based GMM kernel.
2402+
2403+
Args:
2404+
lhs: Left-hand side tensor of shape [num_tokens, input_dim].
2405+
rhs: Right-hand side tensor of shape [num_experts, input_dim, output_dim].
2406+
tokens_per_expert: Tensor of shape [num_experts] indicating the number
2407+
of tokens assigned to each expert.
2408+
2409+
Returns:
2410+
Output tensor of shape [num_tokens, output_dim].
2411+
"""
22872412
cfg = self.config
22882413
if isinstance(cfg.tiling, tuple):
22892414
tiling = cfg.tiling

0 commit comments

Comments
 (0)