Skip to content

Commit dd4fbc8

Browse files
committed
Vectorize NVFP4 packed decode
1 parent d17aeea commit dd4fbc8

1 file changed

Lines changed: 81 additions & 14 deletions

File tree

transformer_nuggets/cute/nvfp4_tma.py

Lines changed: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import cutlass
66
import cutlass.cute as cute
7+
from cutlass._mlir import ir
8+
from cutlass._mlir.dialects import llvm, vector
9+
from cutlass.cutlass_dsl import T, dsl_user_op
710
import torch
811

912
from transformer_nuggets.cute.blockscaled_tma import (
@@ -19,6 +22,46 @@
1922
NVFP4_TMA_PROFILE_TAGS = BLOCKSCALED_TMA_PROFILE_TAGS
2023

2124

25+
@dsl_user_op
26+
def decode_e2m1x8(
27+
packed: cutlass.Uint32,
28+
*,
29+
loc: ir.Location | None = None,
30+
ip: ir.InsertionPoint | None = None,
31+
) -> cute.TensorSSA:
32+
"""Decode one packed E2M1 word with the native four-instruction conversion."""
33+
converted = llvm.inline_asm(
34+
llvm.StructType.get_literal([T.i32(), T.i32(), T.i32(), T.i32()]),
35+
[packed.ir_value(loc=loc, ip=ip)],
36+
"""{
37+
.reg .b8 b0, b1, b2, b3;
38+
mov.b32 {b0, b1, b2, b3}, $4;
39+
cvt.rn.f16x2.e2m1x2 $0, b0;
40+
cvt.rn.f16x2.e2m1x2 $1, b1;
41+
cvt.rn.f16x2.e2m1x2 $2, b2;
42+
cvt.rn.f16x2.e2m1x2 $3, b3;
43+
}""",
44+
"=r,=r,=r,=r,r",
45+
has_side_effects=False,
46+
is_align_stack=False,
47+
loc=loc,
48+
ip=ip,
49+
)
50+
converted_words = vector.from_elements(
51+
ir.VectorType.get([4], T.i32(), loc=loc),
52+
[llvm.extractvalue(T.i32(), converted, [word], loc=loc, ip=ip) for word in range(4)],
53+
loc=loc,
54+
ip=ip,
55+
)
56+
converted_values = llvm.bitcast(
57+
ir.VectorType.get([8], cutlass.Float16.mlir_type, loc=loc),
58+
converted_words,
59+
loc=loc,
60+
ip=ip,
61+
)
62+
return cute.TensorSSA(converted_values, (8,), cutlass.Float16, loc=loc, ip=ip)
63+
64+
2265
class Nvfp4TmaGemv(BlockscaledTmaGemv):
2366
"""Compute M=1 NVFP4 GEMV using scaled_mm-compatible storage."""
2467

@@ -59,7 +102,14 @@ def __init__(
59102

60103
@cute.jit
61104
def decode_lane_values(self, raw_values: cute.Tensor):
62-
"""Decode 32 packed E2M1 values into two 16-value groups."""
105+
"""Decode 32 packed E2M1 values into register-resident FP16 groups."""
106+
if cutlass.const_expr(self.k <= 4096):
107+
return (
108+
decode_e2m1x8(raw_values[0, 0]),
109+
decode_e2m1x8(raw_values[0, 1]),
110+
decode_e2m1x8(raw_values[0, 2]),
111+
decode_e2m1x8(raw_values[0, 3]),
112+
)
63113
return (
64114
cute.recast_tensor(raw_values, cutlass.Float4E2M1FN)
65115
.load()
@@ -169,21 +219,38 @@ def accumulate_scaled_products(
169219
weight_scales,
170220
):
171221
"""Accumulate two independently scaled 16-value E2M1 blocks."""
172-
fp16_reduction_width = 8 if self.k <= 4096 else 2
173-
products = (
174-
(x_values * w_values)
175-
.reshape((fp16_reduction_width, 16 // fp16_reduction_width, 2))
176-
.reduce(
222+
if cutlass.const_expr(self.k <= 4096):
223+
partial0 = (x_values[0] * w_values[0]).reduce(
224+
cute.ReductionOp.ADD, cutlass.Float16(0.0), 0
225+
)
226+
partial1 = (x_values[1] * w_values[1]).reduce(
227+
cute.ReductionOp.ADD, cutlass.Float16(0.0), 0
228+
)
229+
partial2 = (x_values[2] * w_values[2]).reduce(
230+
cute.ReductionOp.ADD, cutlass.Float16(0.0), 0
231+
)
232+
partial3 = (x_values[3] * w_values[3]).reduce(
233+
cute.ReductionOp.ADD, cutlass.Float16(0.0), 0
234+
)
235+
products = (
236+
cutlass.Float32(partial0) + cutlass.Float32(partial1),
237+
cutlass.Float32(partial2) + cutlass.Float32(partial3),
238+
)
239+
else:
240+
products = (
241+
(x_values * w_values)
242+
.reshape((2, 8, 2))
243+
.reduce(
244+
cute.ReductionOp.ADD,
245+
cutlass.Float16(0.0),
246+
(1, None, None),
247+
)
248+
)
249+
products = products.to(cutlass.Float32).reduce(
177250
cute.ReductionOp.ADD,
178-
cutlass.Float16(0.0),
179-
(1, None, None),
251+
cutlass.Float32(0.0),
252+
(1, None),
180253
)
181-
)
182-
products = products.to(cutlass.Float32).reduce(
183-
cute.ReductionOp.ADD,
184-
cutlass.Float32(0.0),
185-
(1, None),
186-
)
187254
for scale_idx in cutlass.range_constexpr(2):
188255
accumulator += products[scale_idx] * input_scales[scale_idx] * weight_scales[scale_idx]
189256
return accumulator

0 commit comments

Comments
 (0)