Grouped GEMM (SM100 BF16)#
This is an experimental API and subject to change. It requires an NVIDIA SM100-or-newer GPU and the optional CuTe DSL dependencies:
pip install nvidia-cudnn-frontend[cutedsl]
GroupedGemmSm100 and grouped_gemm_wrapper_sm100 implement the neutral,
unfused BF16 MoE grouped GEMM. They support dense stacked expert weights or a
device pointer per expert, optional bias, optional materialization of the
intermediate C, and static or dynamic tile scheduling.
Operation#
Let padded_offsets[g] be the exclusive end of expert g’s contiguous row
range and let begin_g be zero for the first expert or
padded_offsets[g - 1] otherwise. For rows in [begin_g, padded_offsets[g]):
G_g = alpha[g] * A_g @ B_g.T
C_g = G_g + prob_g * bias[:, g] # when bias is present
D_g = C_g
Without bias, C_g = G_g and D_g = prob_g * G_g. Accumulation is FP32;
C and D may independently use BF16, FP16, or FP32.
Tensors and layouts#
For total padded rows M, inner dimension K, output dimension N, and L
experts:
Tensor |
Shape |
Required stride / dtype |
|---|---|---|
|
|
|
dense |
|
|
discrete |
|
contiguous CUDA int64 pointers to |
|
|
|
|
|
|
|
|
|
optional |
|
|
|
|
|
M and every cumulative offset must be 256-aligned. Dense weights are
K-major. Discrete mode uses b_major="k", n=N, and
b_dtype=torch.bfloat16.
The pointer-array tensor must be contiguous, non-null, eight-byte aligned, and
on the same device as A; each target pointer must satisfy the kernel’s
alignment contract. The API records the pointer-array tensor on the launch
stream. The caller must keep every pointed-to expert allocation alive and must
not modify or free it until that stream completes.
Wrapper API#
Dense mode:
import cudnn
import torch
result = cudnn.grouped_gemm_wrapper_sm100(
a_tensor=a,
padded_offsets=padded_offsets,
alpha_tensor=alpha,
b_tensor=b,
bias_tensor=bias,
prob_tensor=prob,
c_dtype=torch.float32,
d_dtype=torch.bfloat16,
generate_c=True,
use_dynamic_sched=True,
)
d, c = result
assert d is result["d_tensor"]
assert c is result["c_tensor"]
Discrete mode changes only the weight arguments:
result = cudnn.grouped_gemm_wrapper_sm100(
a_tensor=a,
padded_offsets=padded_offsets,
alpha_tensor=alpha,
b_ptrs=b_ptrs,
n=N,
b_dtype=torch.bfloat16,
b_major="k",
bias_tensor=bias,
prob_tensor=prob,
)
The TupleDict order is exactly d_tensor, then c_tensor. c_tensor is
None unless generate_c=True; the kernel still uses an internal C buffer
when it is needed for execution.
Class API#
The class API takes representative sample tensors, then follows
check_support() -> compile() -> execute():
op = cudnn.GroupedGemmSm100(
sample_a=a,
sample_c=c,
sample_d=d,
sample_padded_offsets=padded_offsets,
sample_alpha=alpha,
sample_b=b,
sample_bias=bias,
sample_prob=prob,
acc_dtype=torch.float32,
generate_c=True,
use_dynamic_sched=False,
)
assert op.check_support()
op.compile()
op.execute(
a_tensor=a,
c_tensor=c,
d_tensor=d,
padded_offsets=padded_offsets,
alpha_tensor=alpha,
b_tensor=b,
bias_tensor=bias,
prob_tensor=prob,
)
For a discrete class instance, replace sample_b with
num_experts=L, b_shape=(N, K), b_dtype=torch.bfloat16, and pass
b_ptrs to execute().
Scheduling, caching, and errors#
use_dynamic_sched=Falseuses the static scheduler.use_dynamic_sched=Truecompiles a dynamic-M callable and reuses it for compatible M values; discrete mode also allocates the per-expert tensor-map workspace. Wrapper cache keys retain dtype, layout, expert count, optional features, scheduler choice, output policy, tile/cluster shape, and overlap margin.Dense and discrete weight arguments are mutually exclusive. Invalid shapes, strides, dtypes, devices, alignment, offsets, pointer entries, output descriptors, tiles/clusters, or a target below SM100 raise
ValueErrororRuntimeErrorbefore launch.Fused GLU, dGLU, and WGrad APIs select BF16 from BF16 operands while keeping their existing FP4/FP8 block-scaled backends. For BF16 on those fused APIs, scale-factor controls are
None; see their operation pages for the exact dispatch and return contracts.