Skip to content

Commit 72328b3

Browse files
Cute Dsl kernel for Wgrad for Fused MOE Layer (NVIDIA#2869)
* integrate cudnn wgrad kernel Signed-off-by: Varun Thumbe <vthumbe@nvidia.com> * have only cute dsl for wgrad Signed-off-by: Varun Thumbe <vthumbe@nvidia.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * revert the change for cudnn Signed-off-by: Varun Thumbe <vthumbe@nvidia.com> * remove dtype Signed-off-by: Varun Thumbe <vthumbe@nvidia.com> * fix comment: Signed-off-by: Varun Thumbe <vthumbe@nvidia.com> * go to cublas if needed Signed-off-by: Varun Thumbe <vthumbe@nvidia.com> * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Signed-off-by: Varun Thumbe <vthumbe@nvidia.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent dc92b39 commit 72328b3

2 files changed

Lines changed: 125 additions & 10 deletions

File tree

transformer_engine/pytorch/ops/_common.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ def _nvidia_cudnn_frontend_supports_scaled_clamped_qgeglu() -> bool:
2929
return False
3030

3131

32+
@functools.lru_cache(maxsize=1)
33+
def _nvidia_cudnn_frontend_supports_wgrad() -> bool:
34+
"""Check cuDNN FE min version for grouped GEMM wgrad kernel."""
35+
try:
36+
return PkgVersion(get_pkg_version("nvidia-cudnn-frontend")) >= PkgVersion("1.23.0")
37+
except PackageNotFoundError:
38+
return False
39+
40+
3241
def is_quantized_tensor(tensor: torch.Tensor | QuantizedTensorStorage) -> bool:
3342
"""Check if tensor is a quantized tensor"""
3443
return isinstance(tensor, QuantizedTensorStorage)

transformer_engine/pytorch/ops/fused/backward_grouped_mlp.py

Lines changed: 116 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515
import torch
1616

1717
import transformer_engine_torch as tex
18-
from ...cpp_extensions import (
19-
general_grouped_gemm_for_grouped_tensor,
20-
)
2118
from ...module.base import get_dummy_wgrad
2219
from ...quantization import Recipe
2320
from ...tensor.grouped_tensor import GroupedTensor
@@ -28,13 +25,88 @@
2825
from ..fuser import register_backward_fusion
2926
from ..op import FusedOperation, FusibleOperation, OperationContext
3027
from .._common import (
28+
_nvidia_cudnn_frontend_supports_wgrad,
3129
fuse_grouped_mlp_ops,
3230
maybe_dequantize,
3331
validate_grouped_mlp_dims,
3432
)
33+
from ...cpp_extensions import general_grouped_gemm_for_grouped_tensor
34+
from ...module.base import _2X_ACC_WGRAD
3535
from ...triton.grouped_dbias_dscales import _compute_grouped_dbias_dscales
3636

3737

38+
def _cudnn_compute_wgrad(
39+
grouped_x: GroupedTensor,
40+
grouped_dy: GroupedTensor,
41+
wgrad_output,
42+
weight_shape: tuple,
43+
offsets: torch.Tensor,
44+
accumulate: bool,
45+
wgrad_kernel_fn,
46+
single_grouped_weight: bool,
47+
):
48+
"""Compute wgrad using the cuDNN CuTe DSL grouped GEMM wgrad kernel.
49+
50+
The cuDNN wgrad kernel computes:
51+
wgrad[e] = a[:, tok_start:tok_end] @ b[tok_start:tok_end, :]
52+
where a = DY^T = (out_features, total_tokens) row-major and
53+
b = X = (total_tokens, in_features) column-major.
54+
"""
55+
out_features, in_features = weight_shape
56+
total_tokens = grouped_dy.logical_shape[0]
57+
58+
fp8_dtype = torch.float8_e4m3fn
59+
60+
# a_tensor = DY^T = (out_features, total_tokens) row-major
61+
a_tensor = grouped_dy.columnwise_data.view(dtype=fp8_dtype).view(total_tokens, out_features).T
62+
# b_tensor = X = (total_tokens, in_features) column-major
63+
b_tensor = grouped_x.columnwise_data.view(dtype=fp8_dtype).view(total_tokens, in_features)
64+
65+
sfa_tensor = grouped_dy.columnwise_scale_inv.view(out_features, -1).view(
66+
dtype=torch.float8_e8m0fnu
67+
)
68+
sfb_tensor = grouped_x.columnwise_scale_inv.view(in_features, -1).view(
69+
dtype=torch.float8_e8m0fnu
70+
)
71+
offsets_tensor = offsets.to(dtype=torch.int32)
72+
73+
# Prepare wgrad output
74+
if single_grouped_weight:
75+
# Dense mode: single (num_groups, out_features, in_features) tensor
76+
wgrad_tensor = wgrad_output.rowwise_data.view(
77+
offsets_tensor.shape[0], out_features, in_features
78+
)
79+
wgrad_kernel_fn(
80+
a_tensor=a_tensor,
81+
b_tensor=b_tensor,
82+
sfa_tensor=sfa_tensor,
83+
sfb_tensor=sfb_tensor,
84+
offsets_tensor=offsets_tensor,
85+
output_mode="dense",
86+
wgrad_tensor=wgrad_tensor,
87+
acc_dtype=torch.float32,
88+
wgrad_dtype=wgrad_tensor.dtype,
89+
sf_vec_size=MXFP8_BLOCK_SCALING_SIZE,
90+
accumulate_on_output=accumulate,
91+
)
92+
else:
93+
# Discrete mode: per-expert wgrad device pointers
94+
(wgrad_ptrs,) = tex.convert_host_pointers_to_tensor([wgrad_output])
95+
wgrad_kernel_fn(
96+
a_tensor=a_tensor,
97+
b_tensor=b_tensor,
98+
sfa_tensor=sfa_tensor,
99+
sfb_tensor=sfb_tensor,
100+
offsets_tensor=offsets_tensor,
101+
output_mode="discrete",
102+
wgrad_ptrs=wgrad_ptrs,
103+
acc_dtype=torch.float32,
104+
wgrad_dtype=wgrad_output[0].dtype,
105+
sf_vec_size=MXFP8_BLOCK_SCALING_SIZE,
106+
accumulate_on_output=accumulate,
107+
)
108+
109+
38110
@functools.lru_cache(maxsize=1)
39111
def _dglu_wrapper_has_generate_dbias_arg() -> bool:
40112
"""True if cudnn-frontend SM100 dGLU wrapper accepts ``generate_dbias``."""
@@ -61,6 +133,9 @@ def _compute_grad_params(
61133
bias_grads,
62134
bias_grad_packed,
63135
label="",
136+
*,
137+
cudnn_wgrad_kernel_fn,
138+
offsets,
64139
):
65140
"""Compute weight gradients and build grad_params for a GroupedLinear layer.
66141
Returns the grad_params list in parameter registration order.
@@ -131,11 +206,23 @@ def _compute_grad_params(
131206
if ctx.weight_requires_grad:
132207
# Launch or defer the GEMM
133208
delay_wgrad = fc_op.wgrad_store is not None and fc_op.wgrad_store.delay_wgrad_compute()
134-
gemm_fn = functools.partial(
135-
general_grouped_gemm_for_grouped_tensor,
136-
layout="NT",
137-
accumulate=accumulate_into_main_grad,
138-
)
209+
if cudnn_wgrad_kernel_fn is not None:
210+
gemm_fn = functools.partial(
211+
_cudnn_compute_wgrad,
212+
weight_shape=weight_shape,
213+
offsets=offsets,
214+
accumulate=accumulate_into_main_grad,
215+
wgrad_kernel_fn=cudnn_wgrad_kernel_fn,
216+
single_grouped_weight=fc_op.single_grouped_weight,
217+
)
218+
else:
219+
gemm_fn = functools.partial(
220+
general_grouped_gemm_for_grouped_tensor,
221+
layout="NT",
222+
accumulate=accumulate_into_main_grad,
223+
use_split_accumulator=_2X_ACC_WGRAD,
224+
)
225+
139226
if delay_wgrad:
140227
fc_op.wgrad_store.put([grouped_x, grouped_dy, wgrad_output], gemm_fn)
141228
else:
@@ -204,6 +291,19 @@ def grouped_gemm_quant_kernel(cls) -> Callable:
204291

205292
return grouped_gemm_quant_wrapper_sm100
206293

294+
@classmethod
295+
@functools.lru_cache(maxsize=None)
296+
def grouped_gemm_wgrad_kernel(cls) -> Optional[Callable]:
297+
"""CuTe DSL kernel for grouped GEMM wgrad on SM100+.
298+
Returns ``None`` when the cuDNN front-end package is older than
299+
1.23.0.
300+
"""
301+
if not _nvidia_cudnn_frontend_supports_wgrad():
302+
return None
303+
from cudnn import grouped_gemm_wgrad_wrapper_sm100 # pylint: disable=no-name-in-module
304+
305+
return grouped_gemm_wgrad_wrapper_sm100
306+
207307
@classmethod
208308
@functools.lru_cache(maxsize=None)
209309
def is_supported(cls) -> bool:
@@ -477,10 +577,12 @@ def fuser_backward(
477577

478578
fc1_dy_row_data = fc2_dgrad_kernel_out["d_row_tensor"]
479579
fc1_dy_row_data = fc1_dy_row_data.view(out_shape[0], fc1_weight_shape[0])
480-
fc1_dy_row_scale = fc2_dgrad_kernel_out["sfd_row_tensor"]
580+
# View scale in their actual swizzled shape
581+
fc1_dy_row_scale = fc2_dgrad_kernel_out["sfd_row_tensor"].permute(5, 2, 4, 0, 1, 3).view(-1)
481582
fc1_dy_col_data = fc2_dgrad_kernel_out["d_col_tensor"]
482583
fc1_dy_col_data = fc1_dy_col_data.view(out_shape[0], fc1_weight_shape[0])
483-
fc1_dy_col_scale = fc2_dgrad_kernel_out["sfd_col_tensor"]
584+
# View scale in their actual swizzled shape
585+
fc1_dy_col_scale = fc2_dgrad_kernel_out["sfd_col_tensor"].permute(5, 2, 4, 0, 1, 3).view(-1)
484586
grad_scales = fc2_dgrad_kernel_out["dprob_tensor"].view(-1)
485587

486588
fc2_bias_grads: Optional[list[Optional[torch.Tensor]]] = None
@@ -553,6 +655,8 @@ def fuser_backward(
553655
bias_grads=fc2_bias_grads,
554656
bias_grad_packed=fc2_bias_grad_packed,
555657
label="FC2",
658+
cudnn_wgrad_kernel_fn=self.grouped_gemm_wgrad_kernel(),
659+
offsets=split_points,
556660
)
557661

558662
# Clear FC2 input tensor if possible
@@ -648,6 +752,8 @@ def fuser_backward(
648752
bias_grads=fc1_bias_grads,
649753
bias_grad_packed=fc1_bias_grad_packed,
650754
label="FC1",
755+
cudnn_wgrad_kernel_fn=self.grouped_gemm_wgrad_kernel(),
756+
offsets=split_points,
651757
)
652758

653759
# Clear FC1 input tensor if possible

0 commit comments

Comments
 (0)