Uh oh!
There was an error while loading. Please reload this page.
[Pytorch] Add B200&B300 BF16 Cutlass GroupGEMM for fine-grained MoE and Varlen-K WGrad Grouped GEMM - #3069
[Pytorch] Add B200&B300 BF16 Cutlass GroupGEMM for fine-grained MoE and Varlen-K WGrad Grouped GEMM#3069alan-hpc wants to merge 3 commits into
Conversation
Greptile SummaryThis PR extends the existing CUTLASS Grouped GEMM path (from #2045) to handle the variable-K (ragged-K) BF16 weight-gradient case that arises in fine-grained MoE training on Hopper (SM90) and now Blackwell (SM100/SM103). It also refactors the host staging buffer from a monolithic 4 MB allocation to a 1024-slot pinned ring buffer (64 KB/slot, 64 MB total) to eliminate the per-call synchronization overhead imposed by pageable host memory.
Confidence Score: 5/5The new varlen-K wgrad dispatch path is well-guarded: shape eligibility is validated before entering the CUTLASS path, K=0 empty groups are correctly excluded with output zero-initialization when not accumulating, and the ring-buffer design properly prevents host-buffer reuse races across concurrent stream launches. The core correctness logic — group filtering, NT-layout dispatch, SM90/SM100 tile selection, and cuBLAS fallback preservation — is sound. All findings are latent guards that are wrong in principle but cannot trigger given the current kMaxGroups=256 bound (~15 KB per slot, well within the 64 KB ring slot). cutlass_grouped_gemm.cuh: the ring-buffer slot-size guard, the strict less-than workspace checks, and the two uninstantiated device-path functions whose null problem_sizes_host pointer could crash CUTLASS's scheduler if they are ever wired up without a matching host estimate. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["nvte_multi_tensor_gemm()"] --> B{is Hopper or Blackwell\nAND use_cutlass?}
B -- No --> C["cublas_path() fallback"]
B -- Yes --> D{all_groups_uniform_k128\nAND no epilogue\nAND BF16/FP16 dtype}
D -- Yes --> E["cutlass_grouped_gemm()\nuniform-K forward SM90+SM100"]
D -- No --> F{is_bf16_wgrad_dtype\nAND NT layout AND grad\nAND is_bf16_wgrad_shape}
F -- Yes --> G["cutlass_grouped_gemm_varlen_k()\nragged-K BF16 wgrad"]
F -- No --> H["warn_fallback then cublas_path()"]
G --> I["collect_bf16_wgrad_nt_groups()\nfilter K=0 groups\nzero-init empty outputs"]
I --> J{A_nz empty?}
J -- Yes --> K["return early all K=0"]
J -- No --> L{SM100?}
L -- No --> M["CutlassGroupedGemmWgrad\nSM90 Cooperative FP32\nor Pingpong BF16"]
L -- Yes --> N{avg_K >= 1536?}
N -- Yes --> O["CutlassGroupedGemmWgrad\nSM100 256x256 kBigN=true"]
N -- No --> P["CutlassGroupedGemmWgrad\nSM100 256x128 kBigN=false"]
M --> Q["getHostWorkspace ring slot\nfill ptrs+shapes cudaMemcpyAsync\nCUTLASS kernel launch"]
O --> Q
P --> Q
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A["nvte_multi_tensor_gemm()"] --> B{is Hopper or Blackwell\nAND use_cutlass?}
B -- No --> C["cublas_path() fallback"]
B -- Yes --> D{all_groups_uniform_k128\nAND no epilogue\nAND BF16/FP16 dtype}
D -- Yes --> E["cutlass_grouped_gemm()\nuniform-K forward SM90+SM100"]
D -- No --> F{is_bf16_wgrad_dtype\nAND NT layout AND grad\nAND is_bf16_wgrad_shape}
F -- Yes --> G["cutlass_grouped_gemm_varlen_k()\nragged-K BF16 wgrad"]
F -- No --> H["warn_fallback then cublas_path()"]
G --> I["collect_bf16_wgrad_nt_groups()\nfilter K=0 groups\nzero-init empty outputs"]
I --> J{A_nz empty?}
J -- Yes --> K["return early all K=0"]
J -- No --> L{SM100?}
L -- No --> M["CutlassGroupedGemmWgrad\nSM90 Cooperative FP32\nor Pingpong BF16"]
L -- Yes --> N{avg_K >= 1536?}
N -- Yes --> O["CutlassGroupedGemmWgrad\nSM100 256x256 kBigN=true"]
N -- No --> P["CutlassGroupedGemmWgrad\nSM100 256x128 kBigN=false"]
M --> Q["getHostWorkspace ring slot\nfill ptrs+shapes cudaMemcpyAsync\nCUTLASS kernel launch"]
O --> Q
P --> Q
Reviews (5): Last reviewed commit: "Merge branch 'NVIDIA:main' into feat/var..." | Re-trigger Greptile |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
d0edc9f to
bda3dc3Comparef7a2b73 to
e7a4db9Compareptrendx
commented
Jun 2, 2026
How does this kernel compare performance-wise with the cuBLASLt grouped gemm? Ideally if cuBLAS is better we would like to move towards that solution instead. |
0db8b00 to
0d190d0Compare…m support Signed-off-by: Min Yang <min.yang@shopee.com>
81c6fd2 to
1453a88Compare
Description
This PR extends the CUTLASS Group GEMM support added in #2045 to cover the variable-K
(K-grouped / ragged-K) BF16 weight-gradient (wgrad) path of fine-grained MoE models on H100 (SM90).
In expert-parallel MoE training the per-expert token counts — the contraction dimension of the
wgrad GEMM
D_i = B_iᵀ @ A_i— are ragged and generally not 128-aligned, which the existinguniform-K CUTLASS grouped-GEMM fast path from #2045 cannot serve. This PR adds a dedicated path
that handles ragged per-expert token counts directly (SM90 TMA/WGMMA), zero-initializes empty
(
K=0) groups, and writes each per-expertD_iin place. Inputs are BF16; output is FP32 (default)or BF16. The standard uniform-K and Multi-Stream cuBLAS paths are unchanged.
Performance on H100 80GB, BF16, wgrad (
D_i = B_iᵀ @ A_i), CUTLASS vs. the Multi-Stream cuBLASbaseline. Shape is
(g, m, n, k[mink, avgk, maxk]):ggroups,m= expert dim,n= hidden dim,k= the per-group routed-token count — the ragged contraction this kernel is built for.run benchmark with
NVTE_USE_CUTLASS_GROUPED_GEMM=1 python benchmarks/gemm/benchmark_grouped_gemm_fwd_bwd.py --use-cutlass --dtype bf16 --num-experts <E> --ep-size 8 --hidden-dim 2048 --expert-dim 512 [--jagged-splits ...]The gain grows as the per-group K shrinks: small, ragged groups are where the Multi-Stream cuBLAS
per-group launch overhead dominates.
Correctness reuses the existing test harness from #2045 (unchanged in this PR): the parametrized
tests/pytorch/test_grouped_linear.py::test_grouped_gemmwithlayout=NT(the wgrad case),use_cutlass=True,dtype=bfloat16over ragged group splits exercises exactly this path and passeson SM90.
This path reuses the
NVTE_USE_CUTLASS_GROUPED_GEMMtoggle introduced in #2045 (default0):export NVTE_USE_CUTLASS_GROUPED_GEMM=1routes the BF16 NT wgrad through CUTLASS,0keeps theMulti-Stream cuBLAS implementation.
NVTE_CUTLASS_GROUPED_GEMM_WARN_FALLBACKstill warns on fallback.Type of change
Changes
cutlass_grouped_gemm.cuh: addCutlassGroupedGemmWgrad<trans_a, trans_b, ElementD>— an SM90grouped-GEMM template specialized for the NT wgrad layout — with explicit instantiations for
FP32 and BF16 output.
cutlass_grouped_gemm.cu: addcutlass_grouped_gemm_varlen_k(...). It validates the BF16 NT wgradcontract, splits groups into the non-empty set (excluding
K=0groups whose null A/B pointerswould crash TMA descriptor construction, zero-initializing their outputs when not accumulating),
and dispatches on output dtype — mirroring the existing
cutlass_grouped_gemmcall path.cublaslt_gemm.cu: wire the path into thenvte_multi_tensor_gemmdispatch(uniform-K fast path → K-grouped wgrad → cuBLAS fallback).
Checklist: