Skip to content

Commit ea9a31d

Browse files
committed
Update tgmm
1 parent 131fbf5 commit ea9a31d

2 files changed

Lines changed: 52 additions & 11 deletions

File tree

src/maxtext/kernels/megablox/pallas_mosaic_tpu_v2_tgmm_kernel.py

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ class OperandRef:
5454
jnp.dtype,
5555
jnp.dtype,
5656
int,
57+
bool,
5758
],
5859
gmm_v2.TileSizes,
5960
]
@@ -89,6 +90,7 @@ def calculate_tgmm_tiling(
8990
out_dtype: jnp.dtype,
9091
acc_dtype: jnp.dtype,
9192
target_zero_ref_bytes: int,
93+
has_partial_sum: bool = False,
9294
) -> gmm_v2.TileSizes:
9395
"""Calculate optimal tile sizes for TGMM kernel."""
9496
# In tgmm, we calculate lhs.T @ dout which doesn't require quantization.
@@ -121,8 +123,10 @@ def within_vmem_limit(tile_m, tile_k, tile_n):
121123
# XLU's transpose. in order to reduce redundant XLU computation, instead
122124
# of performing XLU's transpose every time lhs is pushed into XLU, it
123125
# caches the transposed value into VMEM. this increases VMEM requirement.
126+
ps_bytes = tile_k * tile_n * num_buffers * out_bytes if has_partial_sum else 0
124127
budget = (
125128
tile_k * tile_n * (acc_bytes + num_buffers * out_bytes)
129+
+ ps_bytes
126130
+ (num_buffers + 1) * (tile_m * tile_k * lhs_bytes)
127131
+ num_buffers * (tile_m * tile_n * rhs_bytes)
128132
# Reserve VMEM for zero_ref. Use the upper bound target_zero_ref_bytes
@@ -179,6 +183,7 @@ def make_tgmm_configs(
179183
lhs: jax.Array, # [m, k]
180184
rhs: jax.Array, # [m, n]
181185
rhs_scale: jax.Array, # [1, 1, n] (per-N scale)
186+
partial_sum: jax.Array | None,
182187
group_sizes: jax.Array,
183188
num_actual_groups: int,
184189
*,
@@ -256,14 +261,15 @@ def make_tgmm_configs(
256261
out_dtype,
257262
acc_dtype,
258263
target_zero_ref_bytes,
264+
partial_sum is not None,
259265
)
260266

261267
return gmm_v2.GmmConfigs(
262268
dims=dims,
263269
tiles=tiles,
264270
lhs_cfgs=lhs_cfgs,
265271
rhs_cfgs=rhs_cfgs,
266-
has_partial_sum=False, # This should always be False until partial sum support is added in bwd pass.
272+
has_partial_sum=(partial_sum is not None),
267273
out_dtype=jnp.dtype(out_dtype),
268274
acc_dtype=jnp.dtype(acc_dtype),
269275
# GMM's 'zero_init' zeros unvisited m-rows via DMA, which doesn't apply to
@@ -280,6 +286,7 @@ def tgmm_inner_kernel(
280286
tiled_rhs_ref: OperandRef,
281287
# .value: [tile_m // size_lhs_sublane, size_lhs_sublane, tile_n]
282288
# .scale: [1, 1, tile_n] or None
289+
tiled_ps_ref: jax.Array | None,
283290
tiled_out_ref: jax.Array,
284291
acc_ref: jax.Array,
285292
metadata_ref: gmm_v2.MetadataRef,
@@ -342,6 +349,8 @@ def _matmul(is_new_group: bool, is_group_changing: bool):
342349
if cfgs.rhs_cfgs.has_scale:
343350
scale_slice = tiled_rhs_scale_ref[0] # pyrefly: ignore[unsupported-operation]
344351
acc *= scale_slice
352+
if cfgs.has_partial_sum:
353+
acc += tiled_ps_ref[...].astype(acc.dtype)
345354
tiled_out_ref[...] = acc.astype(tiled_out_ref.dtype)
346355
else:
347356
acc_ref[...] = acc
@@ -430,7 +439,7 @@ def out_index_map(self, n_id: jax.Array, k_id: jax.Array, gm_id: jax.Array):
430439

431440
def generate_tgmm_block_specs(
432441
metadata_ref: gmm_v2.MetadataRef, cfgs: gmm_v2.GmmConfigs
433-
) -> Tuple[Tuple[pl.BlockSpec, OperandRef], pl.BlockSpec]:
442+
) -> Tuple[Tuple[pl.BlockSpec, OperandRef, pl.BlockSpec | None], pl.BlockSpec]:
434443
"""Generates block specs for the given lhs, rhs, and out refs."""
435444
index_map = TgmmIndexMaps(metadata_ref, cfgs)
436445
# NB: in tgmm, LHS is reshaped from (M, K) to (-1, size_lhs_sublane, K) so
@@ -457,8 +466,14 @@ def generate_tgmm_block_specs(
457466
(None, cfgs.tiles.tile_k, cfgs.tiles.tile_n),
458467
index_map.out_index_map,
459468
)
460-
461-
return (lhs_block_spec, rhs_spec), out_block_spec
469+
ps_block_spec = None
470+
if cfgs.has_partial_sum:
471+
ps_block_spec = pl.BlockSpec(
472+
(None, cfgs.tiles.tile_k, cfgs.tiles.tile_n),
473+
index_map.out_index_map,
474+
)
475+
in_specs = (lhs_block_spec, rhs_spec, ps_block_spec)
476+
return in_specs, out_block_spec
462477

463478

464479
def zero_out_start(
@@ -526,6 +541,7 @@ def tgmm_kernel_main(
526541
group_offset_ref, # int32[1]
527542
lhs_ref, # [m, k]
528543
rhs_ref, # OperandRef: .value [m, n], .scale [1, 1, n] or None
544+
partial_sum_ref, # [num_actual_groups, k, n] or None
529545
out_ref, # [num_actual_groups, k, n]
530546
# Scratch memory
531547
acc_ref: jax.Array, # [tile_k, tile_n]
@@ -578,9 +594,12 @@ def tgmm_kernel_main(
578594
rhs_value = rhs_ref.value
579595
rhs_in = rhs_value.reshape(-1, cfgs.dims.size_lhs_sublane, rhs_value.shape[-1])
580596
rhs_operand = OperandRef(value=rhs_in, scale=rhs_ref.scale)
597+
ps_in = None
598+
if cfgs.has_partial_sum:
599+
ps_in = partial_sum_ref
581600
scratches = [acc_ref, metadata_ref]
582601

583-
pipeline_fn(lhs_in, rhs_operand, out_ref, scratches=scratches)
602+
pipeline_fn(lhs_in, rhs_operand, ps_in, out_ref, scratches=scratches)
584603
zero_out_end(
585604
num_groups_to_zero,
586605
out_ref,
@@ -632,6 +651,7 @@ def tgmm_v2(
632651
group_sizes: jax.Array,
633652
num_actual_groups: int,
634653
rhs_scale: jax.Array | None = None, # [1, 1, size_n] (per-N scale)
654+
partial_sum: jax.Array | None = None,
635655
group_offset: jax.Array | None = None,
636656
*,
637657
tile_info: gmm_v2.TileSizes | TileTgmmFn = calculate_tgmm_tiling,
@@ -683,6 +703,7 @@ def tgmm_v2(
683703
lhs,
684704
rhs,
685705
rhs_scale, # pyrefly: ignore[bad-argument-type]
706+
partial_sum,
686707
group_sizes,
687708
num_actual_groups,
688709
tile_info=tile_info,
@@ -731,14 +752,18 @@ def tgmm_v2(
731752
rhs_scale = jnp.pad(rhs_scale, ((0, 0), (0, 0), (0, pad_n)))
732753
rhs = OperandRef(value=rhs, scale=rhs_scale) # pyrefly: ignore[bad-assignment]
733754
hbm_spec = pl.BlockSpec(memory_space=pltpu.HBM)
755+
partial_sum_spec = None
756+
if partial_sum is not None:
757+
partial_sum_spec = hbm_spec
734758
in_specs = [
735759
hbm_spec, # lhs
736760
# the tree.map build a
737761
# OperandRef(value=hbm_spec, scale=None if scale is None else hbm_spec.
738762
jax.tree.map(lambda _: hbm_spec, rhs), # rhs
763+
partial_sum_spec,
739764
]
740765

741-
return pl.pallas_call(
766+
raw_out = pl.pallas_call(
742767
functools.partial(tgmm_kernel_main, cfgs=cfgs),
743768
out_shape=out_init,
744769
grid_spec=pltpu.PrefetchScalarGridSpec(
@@ -756,4 +781,10 @@ def tgmm_v2(
756781
# the metadata here is for profiling, debugging, and cost modeling.
757782
# It does not affect the kernel's computation.
758783
metadata=gmm_v2.get_metadata(cfgs),
759-
)(group_sizes, group_offset, lhs, rhs)[:, : dims.size_k, : dims.size_n]
784+
)(group_sizes, group_offset, lhs, rhs, partial_sum)[:, : dims.size_k, : dims.size_n]
785+
786+
if partial_sum is not None:
787+
local_group_sizes = lax.dynamic_slice(group_sizes, (group_offset[0],), (num_actual_groups,))
788+
empty_mask = (local_group_sizes == 0).reshape(num_actual_groups, 1, 1)
789+
return jnp.where(empty_mask, partial_sum, raw_out)
790+
return raw_out

tests/unit/pallas_mosaic_tpu_v2_kernel_test.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def reference_tgmm(
162162
# group_offset is obtained from
163163
# jnp.arange(0, num_experts, num_experts_per_shard)
164164
group_offset=None,
165+
partial_sum=None,
165166
): # [num_groups, k, n]
166167
"""Computes reference transposed grouped matrix multiplication."""
167168
# Compute lhs[:, sizes[i-1]:sizes[i]] @ rhs[sizes[i-1]:sizes[i], :]
@@ -179,7 +180,10 @@ def reference_tgmm(
179180
group = global_group - group_offset[0]
180181
end = start + group_size
181182
if 0 <= group < num_actual_groups:
182-
out.append(lhs[:, start:end] @ rhs[start:end, :])
183+
res = lhs[:, start:end].astype(jnp.float32) @ rhs[start:end, :].astype(jnp.float32)
184+
if partial_sum is not None:
185+
res = res + partial_sum[group].astype(jnp.float32)
186+
out.append(res.astype(lhs.dtype))
183187
start = end
184188
return jnp.stack(out)
185189

@@ -260,26 +264,32 @@ def test_gmm_basic(self, batch_size, in_size, out_size, num_groups, has_bias, ha
260264
in_size=[512, 1024],
261265
out_size=[512, 1024],
262266
num_groups=[5, 16, 32],
267+
has_partial_sum=[True, False],
263268
group_offset=[0, 2, 3],
264269
)
265-
def test_tgmm_basic(self, batch_size, in_size, out_size, num_groups, group_offset):
270+
def test_tgmm_basic(self, batch_size, in_size, out_size, num_groups, has_partial_sum, group_offset):
266271
num_local_groups = num_groups - group_offset
267272
key = jax.random.key(0)
268-
key1, key2 = jax.random.split(key, 2)
273+
key1, key2, key3 = jax.random.split(key, 3)
269274
lhs = jax.random.normal(key1, (batch_size, in_size), dtype=jnp.bfloat16) # [m, k]
270275
grad = jax.random.normal(key2, (batch_size, out_size), dtype=jnp.bfloat16) # [m, n]
271276
group_sizes = get_group_sizes(batch_size, num_groups)
272277
# if batch_size=128, num_groups=3, an example group_size is
273278
# group_sizes=Array([14, 14, ..., 7]).
274279
group_offset = jnp.array(group_offset, dtype=jnp.int32)
275280

281+
ps = None
282+
if has_partial_sum:
283+
ps = jax.random.normal(key3, (num_local_groups, in_size, out_size), dtype=jnp.bfloat16)
284+
276285
lhs_t = lhs.swapaxes(0, 1) # [k, m]
277-
expected = reference_tgmm(lhs_t, grad, group_sizes, num_local_groups, group_offset=group_offset)
286+
expected = reference_tgmm(lhs_t, grad, group_sizes, num_local_groups, group_offset=group_offset, partial_sum=ps)
278287
actual = tgmm_backend.tgmm_v2(
279288
lhs,
280289
grad,
281290
group_sizes,
282291
num_local_groups,
292+
partial_sum=ps,
283293
group_offset=group_offset,
284294
preferred_element_type=jnp.bfloat16,
285295
)

0 commit comments

Comments
 (0)