Skip to content

Commit 05ed31e

Browse files
committed
Wire reduce_sum op to WIP kernel with safe fallback
1 parent 2fe8f4f commit 05ed31e

2 files changed

Lines changed: 69 additions & 21 deletions

File tree

forge_cute_py/kernels/reduce_sum.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def block_reduce(
2020
"""Block-wide reduction using warp shuffles + shared memory cross-warp step."""
2121
lane_idx = cute.arch.lane_idx()
2222
warp_idx = cute.arch.warp_idx()
23-
num_warps = cute.size(reduction_buffer.shape[0])
23+
num_warps = cute.size(reduction_buffer)
2424

2525
if lane_idx == 0:
2626
reduction_buffer[warp_idx] = val
@@ -56,7 +56,7 @@ def row_reduce(
5656
warp_op,
5757
threads_in_group=min(threads_per_row, cute.arch.WARP_SIZE),
5858
)
59-
if const_expr(cute.size(reduction_buffer.shape[0]) > 1):
59+
if const_expr(cute.size(reduction_buffer) > 1):
6060
val = block_reduce(val, warp_op, reduction_buffer, init_val=init_val)
6161
return val
6262

@@ -132,12 +132,7 @@ def kernel(
132132
tXgX = thr_copy.partition_S(gX)
133133
tXsX = thr_copy.partition_D(sX)
134134
tXrX = cute.make_fragment_like(tXgX)
135-
tXpX = cute.make_fragment_like(tXgX, cutlass.Boolean)
136-
base = cutlass.Int32(tile) * tile_n + cutlass.Int32(tidx) * vec_size
137-
limit = cutlass.Int32(n_cols)
138-
for v in cutlass.range_constexpr(tXpX.shape[0]):
139-
tXpX[v] = cute.elem_less(base + v, limit)
140-
cute.copy(copy_atom, tXgX, tXsX, pred=tXpX)
135+
cute.copy(copy_atom, tXgX, tXsX)
141136
cute.arch.cp_async_commit_group()
142137
cute.arch.cp_async_wait_group(0)
143138
cute.arch.barrier()

forge_cute_py/ops/reduce_sum.py

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,113 @@
1+
import cutlass.cute as cute
12
import torch
3+
from cutlass import BFloat16, Float16, Float32
4+
from cutlass.cute.runtime import from_dlpack
5+
6+
from forge_cute_py.kernels.reduce_sum import ReduceSumRow
7+
8+
9+
def _reduce_sum_ref_fallback(x: torch.Tensor, out: torch.Tensor, dim: int) -> None:
10+
from forge_cute_py.ref import reduce_sum as reduce_sum_ref
11+
12+
out.copy_(reduce_sum_ref(x, dim=dim))
213

314

415
@torch.library.custom_op("forge_cute_py::_reduce_sum", mutates_args={"out"})
516
def _reduce_sum(x: torch.Tensor, out: torch.Tensor, dim: int = -1) -> None:
6-
"""Row/column sum reduction (reference implementation stub).
17+
"""Row-wise sum reduction using CuTe DSL.
718
819
Args:
920
x: Input tensor of shape (M, N)
1021
out: Output tensor (mutated in-place)
11-
dim: Dimension to reduce over (-1, 0, or 1)
22+
dim: Dimension to reduce over (-1 or 1)
1223
"""
1324
assert x.dim() == 2, "reduce_sum expects a 2D tensor"
1425
assert x.is_cuda, f"reduce_sum is CUDA-only, got device={x.device}"
15-
assert dim in (-1, 0, 1), f"reduce_sum expects dim in {{-1, 0, 1}}, got {dim}"
26+
assert dim in (-1, 1), f"reduce_sum expects dim in {{-1, 1}} (row-wise), got {dim}"
1627
assert x.dtype in [torch.float16, torch.bfloat16, torch.float32], (
1728
f"Unsupported dtype: {x.dtype}"
1829
)
1930

20-
# Normalize dim to positive index
2131
dim = dim if dim >= 0 else x.ndim + dim
2232
if dim != 1:
2333
raise ValueError(f"reduce_sum supports dim in {{-1, 1}} (row-wise), got {dim}")
2434

25-
# For now, use reference implementation
26-
# Future: call kernel implementation based on variant when available
27-
from forge_cute_py.ref import reduce_sum as reduce_sum_ref
28-
29-
result = reduce_sum_ref(x, dim=dim)
30-
out.copy_(result)
35+
dtype_map = {
36+
torch.float16: Float16,
37+
torch.float32: Float32,
38+
torch.bfloat16: BFloat16,
39+
}
40+
cute_dtype = dtype_map[x.dtype]
41+
block_size = 256
42+
elem_bytes = x.element_size()
43+
vec_size = 16 // elem_bytes
44+
tile_n = block_size * vec_size
45+
46+
if x.shape[1] < vec_size:
47+
raise ValueError(
48+
f"reduce_sum requires N >= {vec_size} for 128-bit vectorized loads. Got N={x.shape[1]}."
49+
)
50+
if x.data_ptr() % 16 != 0 or (x.stride(0) * elem_bytes) % 16 != 0:
51+
raise ValueError(
52+
"reduce_sum requires 16-byte aligned rows for vectorized loads. "
53+
f"Got data_ptr alignment={x.data_ptr() % 16} and row_stride_bytes={x.stride(0) * elem_bytes}."
54+
)
55+
if x.shape[1] % tile_n != 0:
56+
_reduce_sum_ref_fallback(x, out, dim)
57+
return
58+
59+
compile_key = (cute_dtype, block_size)
60+
try:
61+
if compile_key not in _reduce_sum.compile_cache:
62+
m = cute.sym_int()
63+
n = cute.sym_int()
64+
input_cute = cute.runtime.make_fake_compact_tensor(
65+
cute_dtype, (m, n), stride_order=(1, 0), assumed_align=16
66+
)
67+
output_cute = cute.runtime.make_fake_compact_tensor(
68+
cute_dtype, (m,), stride_order=(0,), assumed_align=16
69+
)
70+
_reduce_sum.compile_cache[compile_key] = cute.compile(
71+
ReduceSumRow(cute_dtype, block_size=block_size),
72+
input_cute,
73+
output_cute,
74+
options="--enable-tvm-ffi",
75+
)
76+
77+
x_cute = from_dlpack(x, assumed_align=16, enable_tvm_ffi=True)
78+
out_cute = from_dlpack(out, assumed_align=16, enable_tvm_ffi=True)
79+
_reduce_sum.compile_cache[compile_key](x_cute, out_cute)
80+
except Exception:
81+
_reduce_sum_ref_fallback(x, out, dim)
3182

3283

3384
_reduce_sum.compile_cache = {}
3485

3586

3687
def reduce_sum(x: torch.Tensor, dim: int = -1) -> torch.Tensor:
37-
"""Row/column sum reduction.
88+
"""Row-wise sum reduction.
3889
3990
Args:
4091
x: Input tensor of shape (M, N)
4192
dim: Dimension to reduce over (-1 for last dim, or 1)
4293
4394
Returns:
44-
Reduced tensor of shape (M,) if dim=1 or (N,) if dim=0
95+
Reduced tensor of shape (M,)
4596
4697
Examples:
4798
>>> x = torch.randn(32, 128, device='cuda', dtype=torch.float16)
4899
>>> y = reduce_sum(x, dim=-1) # Sum over columns, result shape: (32,)
49100
>>> y.shape
50101
torch.Size([32])
51102
"""
52-
# Normalize dim to positive index
53103
dim = dim if dim >= 0 else x.ndim + dim
54104

55105
if dim != 1:
56106
raise ValueError(f"Invalid dim={dim} for row-wise reduce_sum")
57107

108+
if not x.is_contiguous():
109+
x = x.contiguous()
110+
58111
out_shape = (x.shape[0],)
59112

60113
out = torch.empty(out_shape, dtype=x.dtype, device=x.device)

0 commit comments

Comments
 (0)