Discrete Grouped GEMM + SwiGLU (SM100)
Discrete Grouped GEMM + SwiGLU (SM100)
This is an experimental API and subject to change.
JAX support
Supports JAX arrays in FP8 configurations: b_ptrs/sfb_ptrs as packed-uint8 (or x64 int64) pointer arrays, SFA in the physical C-contiguous atom shape (1, MN', K', 32, 4, 4), SFD outputs allocated the same way (the kernel rebuilds all SF layouts from the GEMM shapes and reads only base pointers). Column-major bias and packed-fp4 inputs are not expressible as JAX arrays and raise clear errors. The wrapper is eager, on the CUDA legacy default stream: block_until_ready inputs, synchronize before reading outputs; keep weight arrays alive until the kernel completes.
For jitted JAX programs use the jax.jit-compatible XLA custom-call entry point discrete_grouped_gemm_swiglu_jax_sm100 (built on cudnn.jax.call; k-major weights only): all outputs (c/d/d_col, SFD tensors, amax) are XLA-managed donated buffers — no manual synchronization. Under tracing the offsets values cannot be host-validated, and the weight/scale buffers behind the pointer arrays must stay alive and unmoved across every execution of the traced computation.
Overview
Discrete Grouped GEMM + SwiGLU fusion: A block-scaled grouped GEMM fused with a SwiGLU/GeGLU epilogue on NVIDIA Blackwell GPUs (SM100+), designed for MoE workloads where each expert weight lives in a separate allocation.
Unlike the contiguous grouped API (single packed B tensor with shape (N, K, L)), this API passes per-expert pointers:
b_ptrs: deviceint64tensor of B pointers (one pointer per expert)sfb_ptrs: deviceint64tensor of SFB pointers (one pointer per expert)
Groups are contiguous in the M dimension and described by padded_offsets (cumulative aligned end offsets).
This kernel performs:
- Block-scaled grouped GEMM: Low-precision GEMM (FP4/FP8) using per-expert B and SFB pointers
- GLU epilogue:
act_func="swiglu"oract_func="geglu"applied to GEMM output - Optional quantized output: Produces row/column scale factors for downstream quantization
Shapes
- Inputs
A: contiguous activation tensor across all groups, shape(valid_m, K, 1)B_g: expert-gweight tensor referenced byb_ptrs[g], logical shape(N, K)(or(N, K, 1))SFA: scale factor tensor for A, shape(32, 4, ceil(valid_m/128), 4, ceil(ceil(K/sf_vec_size)/4), 1)SFB_g: expert-gB scale tensor referenced bysfb_ptrs[g], shape(32, 4, ceil(N/128), 4, ceil(ceil(K/sf_vec_size)/4), 1)padded_offsets: cumulative sum of aligned group M sizes, shape(L,).valid_m = padded_offsets[-1]alpha: per-group scaling factors, shape(L,)prob: per-row gating probabilities, shape(valid_m, 1, 1)(optional input)norm_const: normalization constant for FP8 quantization, shape(1,)
- Outputs
C: intermediate GEMM result, shape(valid_m, N, 1)D: row-quantized GLU output, shape(valid_m, N/2, 1)D_col: column-quantized GLU output, shape(valid_m, N/2, 1)SFD_row: row scale factors (when SFD outputs are enabled; wrapper auto-enables this for FP8-input configs), shape(32, 4, ceil(valid_m/128), 4, ceil(ceil((N/2)/sf_vec_size)/4), 1)SFD_col: column scale factors (when SFD outputs are enabled; wrapper auto-enables this for FP8-input configs), shape(32, 4, ceil((N/2)/128), 4, ceil(ceil(valid_m/sf_vec_size)/4), 1)amax: per-group amax (optional; wrapper provides it whend_dtypeis bf16/fp16), shape(L, 1)
Equations
Step 1: Block-scaled grouped GEMM (per group g with rows m in [padded_offsets[g-1], padded_offsets[g])):
Step 2: GLU epilogue (performed by pairing 32-column blocks along N, equations shown for act_func="swiglu"):
Let block size G = 32. For each pair of consecutive 32-wide column blocks:
- Gate block:
Gate_b = C[:, 2·b·G : 2·b·G + G] - Up block:
Up_b = C[:, 2·b·G + G : 2·b·G + 2·G]
For act_func="geglu", the gate nonlinearity is GeGLU instead of SwiGLU.
Step 3: Optional output quantization (when SFD outputs are generated):
Diagram
API Usage
High-level Wrapper
Class API
Parameters
Input/Output Tensors
-
Input tensor A:
a_tensor(wrapper) orsample_a,a_tensor(class)- Shape:
(valid_m, K, 1) - Stride:
(K, 1, valid_m*K)- must be K-major - Dtype (
ab_dtype):{float4_e2m1fn_x2, uint8, float8_e4m3fn, float8_e5m2}uint8is interpreted as packed FP4 (two FP4 values per byte)
- Shape:
-
Input tensor B pointers:
b_ptrs(wrapper/class execute)- Shape:
(L,)whereL = num_experts - Dtype:
int64, CUDA device tensor - Each pointer must reference one expert B tensor with logical shape
(N, K)(or(N, K, 1)) and dtypeb_dtype - Expert B layout is controlled by
b_major("k"or"n")
- Shape:
-
Input tensor SFB pointers:
sfb_ptrs(wrapper/class execute)- Shape:
(L,)whereL = num_experts - Dtype:
int64, CUDA device tensor - Each pointer must reference one expert SFB tensor with shape
(32, 4, ceil(N/128), 4, ceil(ceil(K/sf_vec_size)/4), 1)
- Shape:
-
Output tensor C:
c_tensor(class) or returned in wrapper dict- Shape:
(valid_m, N, 1) - Stride:
(N, 1, valid_m*N)- must be N-major - Dtype (
c_dtype):- FP4 inputs:
{float16, bfloat16} - FP8 inputs:
{float32, float16, bfloat16, float8_e4m3fn, float8_e5m2, float4_e2m1fn_x2}
- FP4 inputs:
- Shape:
-
Output tensor D:
d_tensor(class) or returned in wrapper dict- Shape:
(valid_m, N/2, 1) - Stride:
(N/2, 1, valid_m*(N/2))- must be N-major - Dtype (
d_dtype):- FP4 inputs:
{float16, bfloat16, float32} - FP8 inputs:
{float16, bfloat16, float8_e4m3fn, float8_e5m2, float4_e2m1fn_x2}
- FP4 inputs:
- Shape:
-
Output tensor D_col:
d_col_tensor(class) or returned in wrapper dict- Shape:
(valid_m, N/2, 1) - Stride:
(N/2, 1, valid_m*(N/2))- must match D (N-major) - Dtype: Must match D
- Shape:
-
Input tensor prob (optional):
prob_tensor(wrapper/class)- Shape:
(valid_m, 1, 1) - Dtype:
float32
- Shape:
-
Scale factor tensors
- SFA (A scale factor):
sfa_tensor(wrapper) orsample_sfa,sfa_tensor(class)- Shape:
(32, 4, ceil(valid_m/128), 4, ceil(ceil(K/sf_vec_size)/4), 1) - Dtype (
sf_dtype):{float8_e8m0fnu, float8_e4m3fn}
- Shape:
- SFD_row (optional):
sfd_row_tensor(wrapper) orsample_sfd_row,sfd_row_tensor(class)- Shape:
(32, 4, ceil(valid_m/128), 4, ceil(ceil((N/2)/sf_vec_size)/4), 1) - Dtype: Must match SFA
- Required when: SFD outputs are enabled
- Shape:
- SFD_col (optional):
sfd_col_tensor(wrapper) orsample_sfd_col,sfd_col_tensor(class)- Shape:
(32, 4, ceil((N/2)/128), 4, ceil(ceil(valid_m/sf_vec_size)/4), 1) - Dtype: Must match SFA
- Required when: SFD outputs are enabled
- Shape:
- SFA (A scale factor):
-
Group offsets
- padded_offsets: Cumulative sum of aligned group M sizes
- Shape:
(L,)whereL = num_experts - Dtype:
int32 padded_offsets[-1]equalsvalid_m; each offset is a multiple ofm_aligned
- Shape:
- padded_offsets: Cumulative sum of aligned group M sizes
-
Scaling tensors
- alpha: Per-group scaling factors
- Shape:
(L,) - Dtype:
float32
- Shape:
- amax (optional): Per-group max absolute values
- Shape:
(L, 1) - Dtype:
float32 - If provided, updated in-place; wrapper auto-allocates it when
d_dtype in {bfloat16, float16}
- Shape:
- norm_const (optional): Normalization constant for FP8 quantization
- Shape:
(1,) - Dtype:
float32 - Required when:
sfd_row_tensor/sfd_col_tensorare provided
- Shape:
- alpha: Per-group scaling factors
Common Parameters
-
acc_dtype: torch.dtype- Accumulator dtype. Must be
torch.float32
- Accumulator dtype. Must be
-
mma_tiler_mn: Tuple[int, int]- Kernel tile size
(TILE_M, TILE_N). Default:(256, 256) TILE_M in {128, 256}TILE_N = 256
- Kernel tile size
-
cluster_shape_mn: Tuple[int, int] | None- Thread block cluster shape
(CLUSTER_M, CLUSTER_N) - Constraints: positive powers of 2, both <= 4,
CLUSTER_M * CLUSTER_N <= 16 - Default:
(2, 1)whenTILE_M=256,(1, 1)otherwise
- Thread block cluster shape
-
sf_vec_size: int- Scale factor vector size
- Allowed values:
{16, 32}. Default:16
-
vector_f32: bool- Enable packed f32 operations
- Default:
False
-
m_aligned: int- Alignment requirement for group M dimension
- Must equal
FIX_PAD_SIZE(256) and be divisible bymma_tiler_mn[0] - Default:
256
-
discrete_col_sfd: bool- If True, generate discrete column scale factors grouped by expert tiles
- Only applies when SFD outputs are enabled
- Default:
False
-
act_func: str- Activation function. Valid values:
"swiglu","geglu" - Default:
"swiglu"
- Activation function. Valid values:
-
b_major: str- Expert B layout. Valid values:
"k","n" - FP4 inputs require
"k" - Default:
"k"
- Expert B layout. Valid values:
-
CUDA stream (
current_streamin class API and wrapper)
Wrapper-specific Parameters: discrete_grouped_gemm_swiglu_wrapper_sm100
n: int: Logical full N dimension for expert B (before GLU halves toN/2)b_dtype: torch.dtype: Dtype of expert B tensors referenced byb_ptrsc_dtype: torch.dtype: Intermediate C tensor dtype. Default:torch.bfloat16d_dtype: torch.dtype: Output D tensor dtype. Default:torch.bfloat16cd_major: str: Major dimension for C and D tensors. Must be"n"
Wrapper Return Values
Returns a TupleDict - a dictionary-like object that also supports tuple unpacking and integer indexing.
Dictionary keys (also tuple unpacking order):
c_tensor: Intermediate GEMM resultd_tensor: Row-quantized GLU outputd_col_tensor: Column-quantized GLU outputamax_tensor: Per-group amax (whend_dtype in {bfloat16, float16})sfd_row_tensor: Row scale factors (when SFD outputs are enabled)sfd_col_tensor: Column scale factors (when SFD outputs are enabled)
Class-specific Parameters
DiscreteGroupedGemmSwigluSm100 (constructor)
sample_a,num_experts,b_shape,b_dtype,sample_c,sample_d,sample_sfa,sample_padded_offsets,sample_alpha,sample_d_col,sample_sfd_row,sample_sfd_col,sample_amax,sample_norm_const,sample_prob- see Input/Output tensors- Note:
sample_sfd_row,sample_sfd_col,sample_norm_constmust be allNoneor all notNone b_shapemust be logical(N, K)for one expert (pass logicalK, not packedK/2, for FP4)
- Note:
DiscreteGroupedGemmSwigluSm100.execute
a_tensor,b_ptrs,c_tensor,d_tensor,sfa_tensor,sfb_ptrs,padded_offsets,alpha_tensor,d_col_tensor,sfd_row_tensor,sfd_col_tensor,amax_tensor,norm_const_tensor,prob_tensor- see Input/Output tensors. Layouts must match constructor sample descriptors.
Support Surface and Constraints
Layouts and Strides
Amust be K-major (contiguous along K dimension)- Expert
Blayout is selected byb_major:b_major="k": K-majorb_major="n": N-major (FP8 configs only)
C,D, andD_colmust be N-major- All tensors must be 16-byte aligned along the contiguous dimension
Data Types
Input/Weight Types (ab_dtype)
Additional Type Constraints
b_dtypemust matchAdtypeSFA,SFD_row, andSFD_colmust share dtypeDandD_colmust have the same dtypeacc_dtypemust befloat32sf_dtype=float8_e4m3fnwithsf_vec_size=32is not supported- FP8
ab_dtypewithsf_vec_size=16is not supported - FP4
ab_dtypewithsf_vec_size=16andd_dtype=float32is not supported - FP4
ab_dtyperequiresc_dtype in {float16, bfloat16} - FP4
ab_dtyperequiresb_major="k"
Scale Factor Output Requirements
-
When
sfd_row_tensor/sfd_col_tensorare provided:sfd_row_tensor,sfd_col_tensor, andnorm_const_tensorare all required- These must be provided together (all
Noneor all notNone)
-
amax_tensoris optional:- If provided, it is updated in-place with per-group maxima
- Wrapper auto-allocates it when
d_dtype in {bfloat16, float16}
Tiling and Cluster
mma_tiler_mn[0] = 256enables 2-CTA instructions (use_2cta_instrs=True)mma_tiler_mn[0] = 128uses the non-2CTA instruction path- When
use_2cta_instrs=True:cluster_shape_mn[0]must be divisible by 2 m_alignedmust be divisible bymma_tiler_mn[0]m_alignedmust equalFIX_PAD_SIZE=256
Shapes and Divisibility
Nis consumed in paired 32-column blocks by the GLU epilogue (useNdivisible by 64)padded_offsetslengthLis expert count and must be<= 1024valid_m = padded_offsets[-1]determines actual M sizeb_ptrsandsfb_ptrsmust be CUDAint64tensors with shape(L,)
Environment
- Requires CUDA with SM100+ compute capability (Blackwell GPUs)
Usage Examples
For runnable examples and validation, see:
test/python/fe_api/grouped_gemm/test_discrete_grouped_gemm_swiglu.pytest/python/fe_api/grouped_gemm/test_discrete_grouped_gemm_swiglu_utils.py