Repository navigation
[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 |
d0edc9f to
bda3dc3
Compare
f7a2b73 to
e7a4db9
Compare
|
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
0d190d0
Compare
…m support Signed-off-by: Min Yang <min.yang@shopee.com>
81c6fd2 to
1453a88
Compare
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: