Skip to content

Commit a60f127

Browse files
committed
Only forward, no backward support for now
Signed-off-by: Masaki Kozuki <mkozuki@nvidia.com>
1 parent 57131e7 commit a60f127

2 files changed

Lines changed: 116 additions & 49 deletions

File tree

thunder/executors/cutlass_dsl_ex.py

Lines changed: 102 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,24 @@ def is_device_quack_compat() -> bool:
8383
return torch.cuda.get_device_capability() in ((9, 0), (10, 0))
8484

8585

86+
# NOTE: This constraint comes from https://github.com/Dao-AILab/quack/blob/59631e98/quack/reduction_base.py#L35-L38
87+
def is_last_dim_divisible(dtype: dtypes.dtype, last_dim_size: int) -> bool:
88+
return last_dim_size % (128 // 8 // dtype.bytes) == 0
89+
90+
8691
# Register [`quack`](https://github.com/Dao-AILab/quack) ops
8792
if find_spec("quack") is not None:
8893
# softmax
8994
from quack.softmax import _softmax_fwd, _softmax_backward
9095

9196
def quack_softmax_impl(a: torch.Tensor) -> torch.Tensor:
92-
return _softmax_fwd(a)
97+
original_shape = a.shape
98+
if requires_reshpae := a.ndim > 2:
99+
a = a.view(-1, original_shape[-1])
100+
ret = _softmax_fwd(a)
101+
if requires_reshpae:
102+
ret = ret.view(original_shape)
103+
return ret
93104

94105
def quack_softmax_meta(a: TensorProxy) -> TensorProxy:
95106
return TensorProxy(like=a)
@@ -101,7 +112,14 @@ def quack_softmax_meta(a: TensorProxy) -> TensorProxy:
101112
)
102113

103114
def quack_softmax_backward(g: torch.Tensor, a: torch.Tensor) -> torch.Tensor:
104-
return _softmax_backward(g, a)
115+
original_shape = g.shape
116+
if requires_reshape := g.ndim > 2:
117+
g = g.view(-1, original_shape[-1])
118+
a = a.view(-1, original_shape[-1])
119+
ret = _softmax_backward(g, a)
120+
if requires_reshape:
121+
ret = ret.view(original_shape)
122+
return ret
105123

106124
def quack_softmax_backward_meta(g: TensorProxy, a: TensorProxy) -> TensorProxy:
107125
return TensorProxy(like=g)
@@ -123,11 +141,11 @@ def quack_softmax_checker(
123141
last_dims = {-1, a.ndim - 1}
124142
allowed_dtypes = {None, a.dtype}
125143
return (
126-
a.ndim == 2
127-
and dim in last_dims
144+
dim in last_dims
128145
and dtype in allowed_dtypes
129146
and a.dtype in {dtypes.float16, dtypes.bfloat16, dtypes.float32}
130147
and is_device_quack_compat()
148+
and is_last_dim_divisible(a.dtype, a.shape[-1])
131149
)
132150

133151
def quack_softmax_transform(
@@ -139,26 +157,42 @@ def quack_softmax_transform(
139157
) -> TensorProxy:
140158
return quack_softmax(a)
141159

142-
def quack_softmax_grad(
143-
a: TensorProxy,
144-
/,
145-
dim: int,
146-
*,
147-
dtype: thunder_dtype | None = None,
148-
) -> TensorProxy:
149-
fwd = quack_softmax(a)
150-
g = get_grad(fwd)
151-
a_grad = quack_softmax_backward(g, fwd)
152-
put_grad(a, a_grad)
153-
154-
return fwd
160+
# NOTE: Softmax backward doesn't look functioning as follows:
161+
# def _engine_run_backward(
162+
# t_outputs: Sequence[Union[torch.Tensor, GradientEdge]],
163+
# *args: Any,
164+
# **kwargs: Any,
165+
# ) -> tuple[torch.Tensor, ...]:
166+
# attach_logging_hooks = log.getEffectiveLevel() <= logging.DEBUG
167+
# if attach_logging_hooks:
168+
# unregister_hooks = _register_logging_hooks_on_whole_graph(t_outputs)
169+
# try:
170+
# > return Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass
171+
# t_outputs, *args, **kwargs
172+
# ) # Calls into the C++ engine to run the backward pass
173+
# E RuntimeError: element 0 of tensors does not require grad and does not have a grad_fn
174+
#
175+
# /pytorch/torch/autograd/graph.py:829: RuntimeError
176+
# def quack_softmax_grad(
177+
# a: TensorProxy,
178+
# /,
179+
# dim: int,
180+
# *,
181+
# dtype: thunder_dtype | None = None,
182+
# ) -> TensorProxy:
183+
# fwd = quack_softmax(a)
184+
# g = get_grad(fwd)
185+
# a_grad = quack_softmax_backward(g, fwd)
186+
# put_grad(a, a_grad)
187+
188+
# return fwd
155189

156190
for ltorch_softmax in (ltorch._softmax, ltorch.softmax):
157191
cutlass_dsl_ex.register_implementation(
158192
ltorch_softmax,
159193
checker=quack_softmax_checker,
160194
execution_transform=quack_softmax_transform,
161-
grad_transform=quack_softmax_grad,
195+
# grad_transform=quack_softmax_grad,
162196
)
163197

164198
# crossentropy
@@ -305,7 +339,13 @@ def quack_layer_norm_forward_impl(
305339
return_rstd: bool,
306340
return_mean: bool,
307341
) -> torch.Tensor:
308-
return layernorm(x, weight, eps, return_rstd=return_rstd, return_mean=return_mean)
342+
original_shape = x.shape
343+
if requires_reshape := x.ndim > 2:
344+
x = x.view(-1, original_shape[-1])
345+
ret = layernorm(x, weight, eps, return_rstd=return_rstd, return_mean=return_mean)
346+
if requires_reshape:
347+
ret = ret.view(original_shape)
348+
return ret
309349

310350
def quack_layer_norm_forward_meta(
311351
x: TensorProxy,
@@ -332,8 +372,7 @@ def quack_layer_norm_checker(
332372
eps: Number = 1e-5,
333373
) -> bool:
334374
if (
335-
a.ndim != 2
336-
or a.dtype not in {dtypes.float16, dtypes.bfloat16, dtypes.float32}
375+
a.dtype not in {dtypes.float16, dtypes.bfloat16, dtypes.float32}
337376
or weight.ndim != 1
338377
or a.shape[-1] != weight.shape[0]
339378
or weight.dtype not in {dtypes.float32}
@@ -365,7 +404,13 @@ def quack_rms_norm_forward_impl(
365404
weight: torch.Tensor,
366405
eps: float = 1e-6,
367406
) -> torch.Tensor:
368-
return _rmsnorm_fwd(x, weight, eps, return_rstd=False)
407+
original_shape = x.shape
408+
if requires_reshape := x.ndim > 2:
409+
x = x.view(-1, original_shape[-1])
410+
ret = _rmsnorm_fwd(x, weight, eps, return_rstd=False)
411+
if requires_reshape:
412+
ret = ret.view(original_shape)
413+
return ret
369414

370415
def quack_rms_norm_forward_meta(
371416
x: TensorProxy,
@@ -386,7 +431,14 @@ def quack_rms_norm_backward_impl(
386431
weight: torch.Tensor,
387432
rstd: torch.Tensor,
388433
) -> torch.Tensor:
389-
return _rmsnorm_backward(x, weight, grad, rstd)
434+
original_shape = grad.shape
435+
if requires_reshape := grad.ndim > 2:
436+
grad = grad.view(-1, original_shape[-1])
437+
x = x.view(-1, original_shape[-1])
438+
ret = _rmsnorm_backward(x, weight, grad, rstd)
439+
if requires_reshape:
440+
ret = ret.view(original_shape)
441+
return ret
390442

391443
def quack_rms_norm_backward_meta(
392444
grad: TensorProxy,
@@ -411,21 +463,26 @@ def quack_rms_norm_checker(
411463
eps: float | None = None,
412464
) -> bool:
413465
if (
414-
a.ndim != 2
415-
or weight.ndim != 1
466+
weight.ndim != 1
416467
or a.shape[-1] != weight.shape[0]
417468
or a.dtype not in {dtypes.float16, dtypes.bfloat16, dtypes.float32}
418469
or weight.dtype not in {dtypes.float16, dtypes.bfloat16, dtypes.float32}
419470
):
420471
return False
421-
return weight is not None and is_device_quack_compat()
472+
return weight is not None and is_device_quack_compat() and is_last_dim_divisible(a.dtype, a.shape[-1])
422473

423474
def quack_rms_norm_aug_forward_impl(
424475
x: torch.Tensor,
425476
weight: torch.Tensor,
426477
eps: float = 1e-6,
427478
) -> tuple[torch.Tensor, torch.Tensor]:
428-
return _rmsnorm_fwd(x, weight, eps, return_rstd=True)
479+
original_shape = x.shape
480+
if requires_reshape := x.ndim > 2:
481+
x = x.view(-1, original_shape[-1])
482+
fwd, rstd = _rmsnorm_fwd(x, weight, eps, return_rstd=True)
483+
if requires_reshape:
484+
fwd = fwd.view(original_shape)
485+
return fwd, rstd
429486

430487
def quack_rms_norm_aug_forward_meta(
431488
x: TensorProxy,
@@ -451,6 +508,23 @@ def quack_rms_norm_transform(
451508
eps = 1e-6
452509
return quack_rms_norm_aug_forward(a, weight, eps)[0]
453510

511+
# NOTE: The backward looks not functioning:
512+
# def _engine_run_backward(
513+
# t_outputs: Sequence[Union[torch.Tensor, GradientEdge]],
514+
# *args: Any,
515+
# **kwargs: Any,
516+
# ) -> tuple[torch.Tensor, ...]:
517+
# attach_logging_hooks = log.getEffectiveLevel() <= logging.DEBUG
518+
# if attach_logging_hooks:
519+
# unregister_hooks = _register_logging_hooks_on_whole_graph(t_outputs)
520+
# try:
521+
# > return Variable._execution_engine.run_backward( # Calls into the C++ engine to run the backward pass
522+
# t_outputs, *args, **kwargs
523+
# ) # Calls into the C++ engine to run the backward pass
524+
# E RuntimeError: One of the differentiated Tensors does not require grad
525+
#
526+
# /pytorch/torch/autograd/graph.py:829: RuntimeError
527+
454528
def quack_rms_norm_grad(
455529
a: TensorProxy,
456530
/,
@@ -471,5 +545,5 @@ def quack_rms_norm_grad(
471545
ltorch.rms_norm,
472546
checker=quack_rms_norm_checker,
473547
execution_transform=quack_rms_norm_transform,
474-
grad_transform=quack_rms_norm_grad,
548+
# grad_transform=quack_rms_norm_grad,
475549
)

thunder/tests/test_cutlass_dsl_ex.py

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
)
2424
_DTYPES = (torch.float16, torch.bfloat16, torch.float32)
2525
_DTYPE_IDS = tuple(str(a) for a in _DTYPES)
26+
_SHAPES = ((128, 1024), (3, 139, 641), (3, 3, 128, 1024))
27+
_SHAPE_IDS = ("2d", "incompat_3d", "3d")
2628

2729

2830
@pytest.fixture(autouse=True, scope="module")
@@ -39,7 +41,7 @@ def set_cuda_as_default_device():
3941

4042

4143
def jit_with_cutlass_dsl_ex(fn: Callable[[Any], Any]) -> Callable[[Any], Any]:
42-
return thunder.jit(fn, executors=[cutlass_dsl_ex])
44+
return thunder.jit(fn, executors=[cutlass_dsl_ex], disable_torch_autograd=True)
4345

4446

4547
@requiresCUDA
@@ -58,16 +60,13 @@ def test_quack_cross_entropy(dtype: torch.dtype):
5860
actual = jitted(x, targets, reduction="none")
5961
torch.testing.assert_close(expected, actual)
6062

61-
# expected_grad = torch.autograd.grad((expected,), (ref_x, targets), )
62-
# actual_grad = torch.autograd.grad((actual,), (x, targets))
63-
# torch.testing.assert_close(expected_grad, actual_grad)
64-
6563

6664
@requiresCUDA
6765
@quack_available
66+
@pytest.mark.parametrize("shape", _SHAPES, ids=_SHAPE_IDS)
6867
@pytest.mark.parametrize("dtype", _DTYPES, ids=_DTYPE_IDS)
69-
def test_quack_softmax(dtype: torch.dtype):
70-
x = torch.randn((128, 1024), dtype=dtype, requires_grad=True)
68+
def test_quack_softmax(dtype: torch.dtype, shape: tuple[int, ...]):
69+
x = torch.randn(shape, dtype=dtype, requires_grad=True)
7170
ref_x = x.clone().detach()
7271

7372
jitted = jit_with_cutlass_dsl_ex(F.softmax)
@@ -76,19 +75,16 @@ def test_quack_softmax(dtype: torch.dtype):
7675
actual = jitted(x, dim=-1)
7776
torch.testing.assert_close(expected, actual)
7877

79-
# expected_grad = torch.autograd.grad((expected,), (ref_x,))
80-
# actual_grad = torch.autograd.grad((actual,), (x,))
81-
# torch.testing.assert_close(expected_grad, actual_grad)
82-
8378

8479
@requiresCUDA
8580
@quack_available
81+
@pytest.mark.parametrize("shape", _SHAPES, ids=_SHAPE_IDS)
8682
@pytest.mark.parametrize("dtype", _DTYPES, ids=_DTYPE_IDS)
87-
def test_quack_layernorm(dtype: torch.dtype):
88-
x = torch.randn((128, 1024), dtype=dtype, requires_grad=True)
83+
def test_quack_layernorm(dtype: torch.dtype, shape: tuple[int, ...]):
84+
x = torch.randn(shape, dtype=dtype, requires_grad=True)
8985
ref_x = x.clone().detach().to(torch.float32)
9086

91-
module = nn.LayerNorm(1024).cuda()
87+
module = nn.LayerNorm(shape[-1]).cuda()
9288
jitted = jit_with_cutlass_dsl_ex(module)
9389

9490
expected = module(ref_x).to(dtype)
@@ -98,18 +94,15 @@ def test_quack_layernorm(dtype: torch.dtype):
9894

9995
@requiresCUDA
10096
@quack_available
97+
@pytest.mark.parametrize("shape", _SHAPES, ids=_SHAPE_IDS)
10198
@pytest.mark.parametrize("dtype", _DTYPES, ids=_DTYPE_IDS)
102-
def test_quack_rmsnorm(dtype: torch.dtype):
103-
x = torch.randn((128, 1024), dtype=dtype, requires_grad=True)
99+
def test_quack_rmsnorm(dtype: torch.dtype, shape: tuple[int, ...]):
100+
x = torch.randn(shape, dtype=dtype, requires_grad=True)
104101
ref_x = x.clone().detach()
105102

106-
module = nn.RMSNorm(1024).cuda()
103+
module = nn.RMSNorm(shape[-1]).cuda()
107104
jitted = jit_with_cutlass_dsl_ex(module)
108105

109106
expected = module(ref_x)
110107
actual = jitted(x)
111108
torch.testing.assert_close(expected, actual)
112-
113-
# expected_grad = torch.autograd.grad((expected,), (ref_x,))
114-
# actual_grad = torch.autograd.grad((actual,), (x,))
115-
# torch.testing.assert_close(expected_grad, actual_grad)

0 commit comments

Comments
 (0)