Skip to content

Commit 6bc5f8a

Browse files
Merge pull request AI-Hypercomputer#4221 from AI-Hypercomputer:gmm_kernel
PiperOrigin-RevId: 936327845
2 parents 48ac2bf + 3db3e05 commit 6bc5f8a

3 files changed

Lines changed: 1045 additions & 20 deletions

File tree

src/maxtext/kernels/megablox/pallas_mosaic_tpu_v2_gmm_kernel.py

Lines changed: 73 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ class GmmConfigs:
204204
rhs_cfgs: InputConfigs
205205
out_dtype: jnp.dtype
206206
acc_dtype: jnp.dtype
207+
has_partial_sum: bool
207208
zero_init: bool
208209
fuse_act: str | None
209210

@@ -219,7 +220,7 @@ def out_size_n(self) -> int:
219220
return self.dims.size_n // 2
220221

221222

222-
TileFn = Callable[[Dimensions, InputConfigs, InputConfigs, int, str | None], TileSizes]
223+
TileFn = Callable[[Dimensions, InputConfigs, InputConfigs, int | None, str | None, bool], TileSizes]
223224

224225

225226
class IndexMaps:
@@ -270,10 +271,20 @@ def out_index_map(self, n_id: jax.Array, gm_id: jax.Array, _: jax.Array):
270271

271272
return (pl.ds(row_start, row_size), 0, n_id)
272273

274+
def ps_index_map(self, n_id: jax.Array, gm_id: jax.Array, _: jax.Array):
275+
m_start = self.metadata_ref.gm_id_to_m_offset[gm_id]
276+
m_end = self.metadata_ref.gm_id_to_m_offset[gm_id + 1]
277+
278+
row_start = m_start // self.cfgs.dims.size_lhs_sublane
279+
row_end = pl.cdiv(m_end, self.cfgs.dims.size_lhs_sublane)
280+
row_size = row_end - row_start
281+
282+
return (pl.ds(row_start, row_size), 0, n_id)
283+
273284

274285
def generate_block_specs(
275286
metadata_ref: MetadataRef, cfgs: GmmConfigs
276-
) -> Tuple[Tuple[pl.BlockSpec, WeightsRef], pl.BlockSpec]:
287+
) -> Tuple[Tuple[pl.BlockSpec, WeightsRef, pl.BlockSpec | None], pl.BlockSpec]:
277288
"""Generates block specs for the given lhs, rhs, and out refs."""
278289

279290
index_map = IndexMaps(metadata_ref, cfgs)
@@ -294,7 +305,7 @@ def generate_block_specs(
294305
index_map.rhs_weight_index_map,
295306
pipeline_mode=pl.Buffered(buffer_count=3),
296307
)
297-
rhs_scale_block_spec = rhs_bias_block_spec = None
308+
rhs_scale_block_spec = rhs_bias_block_spec = ps_block_spec = None
298309
if cfgs.rhs_cfgs.has_bias:
299310
rhs_bias_block_spec = pl.BlockSpec(
300311
(None, 1, cfgs.tiles.tile_n),
@@ -306,6 +317,12 @@ def generate_block_specs(
306317
index_map.rhs_scale_index_map,
307318
)
308319

320+
if cfgs.has_partial_sum:
321+
ps_block_spec = pl.BlockSpec(
322+
(bounded_slice_gm, cfgs.dims.size_lhs_sublane, cfgs.tiles.tile_n),
323+
index_map.ps_index_map,
324+
)
325+
309326
rhs_block_spec = WeightsRef(
310327
weight=rhs_weight_spec,
311328
scale=rhs_scale_block_spec,
@@ -317,7 +334,7 @@ def generate_block_specs(
317334
index_map.out_index_map,
318335
)
319336

320-
return (lhs_block_spec, rhs_block_spec), out_block_spec
337+
return (lhs_block_spec, rhs_block_spec, ps_block_spec), out_block_spec
321338

322339

323340
# Define kernels.
@@ -328,6 +345,9 @@ def inner_kernel(
328345
tiled_lhs_ref: jax.Array,
329346
# [tile_m // size_lhs_sublane, size_lhs_sublane, tile_k]
330347
tiled_rhs_ref: RhsRef, # [tile_k, tile_n]
348+
# Partial Sum
349+
tiled_ps_ref: jax.Array | None,
350+
# [tile_m // size_lhs_sublane, size_lhs_sublane, tile_n]
331351
# Out
332352
tiled_out_ref: jax.Array,
333353
# [tile_m // size_lhs_sublane, size_lhs_sublane, tile_n]
@@ -489,6 +509,9 @@ def _matmul(is_first_k_step: bool, is_last_k_step: bool):
489509
if cfgs.rhs_cfgs.has_bias:
490510
tiled_rhs_bias = tiled_rhs_ref.get_bias()
491511
acc += tiled_rhs_bias.astype(acc.dtype)
512+
if cfgs.has_partial_sum:
513+
ps_tile = tiled_ps_ref[...].reshape(acc.shape)
514+
acc += ps_tile.astype(acc.dtype)
492515

493516
acc = apply_act_fn(acc, cfgs.fuse_act)
494517

@@ -746,6 +769,7 @@ def kernel_main(
746769
# In
747770
lhs_ref: jax.Array, # [size_m, size_k]
748771
rhs_ref: WeightsRef, # [size_group, size_k, size_n]
772+
partial_sum_ref: jax.Array, # [size_m, size_n]
749773
# Out
750774
out_ref: jax.Array, # [size_m, size_n]
751775
# Scratch memory
@@ -812,7 +836,7 @@ def kernel_main(
812836
dims=cfgs.dims,
813837
)
814838

815-
(lhs_spec, rhs_spec), out_spec = generate_block_specs(metadata_ref, cfgs)
839+
(lhs_spec, rhs_spec, ps_spec), out_spec = generate_block_specs(metadata_ref, cfgs)
816840

817841
if cfgs.fuse_act is not None:
818842
rhs_up_ref = jax.tree.map(lambda x: x.at[..., cfgs.out_size_n :], rhs_ref)
@@ -827,16 +851,19 @@ def kernel_main(
827851
pipeline_fn = pltpu.emit_pipeline(
828852
functools.partial(inner_kernel, cfgs=cfgs),
829853
grid=(num_n, num_gm, num_k),
830-
in_specs=(lhs_spec, rhs_spec),
854+
in_specs=(lhs_spec, rhs_spec, ps_spec),
831855
out_specs=out_spec,
832856
)
833857

834858
# Bounded slice requires second last dim to be aligned to the sublane size.
835859
# rhs_ref uses static tiling thus reshape is not needed.
836860
lhs_in = lhs_ref.reshape(-1, cfgs.dims.size_lhs_sublane, lhs_ref.shape[-1])
861+
ps_in = None
862+
if cfgs.has_partial_sum:
863+
ps_in = partial_sum_ref.reshape(-1, cfgs.dims.size_lhs_sublane, partial_sum_ref.shape[-1])
837864
out_in = out_ref.reshape(-1, cfgs.dims.size_lhs_sublane, out_ref.shape[-1])
838865
scratches = [partial_out_ref, acc_ref, metadata_ref]
839-
pipeline_fn(lhs_in, rhs_ref, out_in, scratches=scratches)
866+
pipeline_fn(lhs_in, rhs_ref, ps_in, out_in, scratches=scratches)
840867

841868
if cfgs.zero_init:
842869
zero_out_end(out_ref, semaphore_ref, zero_size, dims=cfgs.dims)
@@ -848,6 +875,7 @@ def calculate_tiling(
848875
rhs_cfgs: InputConfigs,
849876
vmem_limit_bytes: int,
850877
fuse_act: str | None = None,
878+
has_partial_sum: bool = False,
851879
) -> TileSizes:
852880
"""Calculate optimal tile sizes for GMM kernel."""
853881

@@ -914,11 +942,13 @@ def _gmm_vmem_estimate(tn: int, tk: int) -> int:
914942
acc_dtype_bytes = 2 if lhs_cfgs.quant_dtype is not None else 4
915943
acc_vmem = tile_m * acc_cols * acc_dtype_bytes
916944

917-
# 4. Output tile (double-buffered)
945+
# 4. Output tile (double-buffered) and partial sum buffer
918946
out_dtype_bytes = jax.dtypes.itemsize_bits(lhs_cfgs.dtype) // 8
919947
out_vmem = 2 * tile_m * tn * out_dtype_bytes
948+
ps_vmem = 2 * tile_m * tn * out_dtype_bytes if has_partial_sum else 0
949+
partial_out_vmem = dims.size_lhs_sublane * tn * out_dtype_bytes
920950

921-
return lhs_vmem + rhs_vmem + acc_vmem + out_vmem
951+
return lhs_vmem + rhs_vmem + acc_vmem + out_vmem + ps_vmem + partial_out_vmem
922952

923953
# Multiple k tiles will introduce accumulation overhead. Thus, we first try
924954
# to fit the tensors into vmem by only adjusting tile_n.
@@ -952,6 +982,7 @@ def validate_inputs(
952982
rhs: jax.Array,
953983
rhs_scale: jax.Array | None,
954984
rhs_bias: jax.Array | None,
985+
partial_sum: jax.Array | None,
955986
group_sizes: jax.Array,
956987
group_offset: jax.Array,
957988
fuse_act: str | None = None,
@@ -967,6 +998,8 @@ def validate_inputs(
967998
assert rhs.shape == (size_group, size_k, size_n)
968999
if rhs_bias is not None:
9691000
assert rhs_bias.shape == (size_group, 1, size_n)
1001+
if partial_sum is not None:
1002+
assert partial_sum.shape == (size_m, size_n)
9701003
if rhs_scale is not None:
9711004
num_quant_blocks = rhs_scale.shape[1]
9721005
assert rhs_scale.shape == (size_group, num_quant_blocks, 1, size_n)
@@ -1043,6 +1076,7 @@ def make_gmm_configs(
10431076
rhs: jax.Array,
10441077
rhs_scale: jax.Array | None,
10451078
rhs_bias: jax.Array | None,
1079+
partial_sum: jax.Array | None,
10461080
group_sizes: jax.Array,
10471081
group_offset: jax.Array,
10481082
*,
@@ -1056,7 +1090,7 @@ def make_gmm_configs(
10561090
):
10571091
"""Fills the GMM config for the GMM kernel."""
10581092

1059-
dims = validate_inputs(lhs, rhs, rhs_scale, rhs_bias, group_sizes, group_offset, fuse_act)
1093+
dims = validate_inputs(lhs, rhs, rhs_scale, rhs_bias, partial_sum, group_sizes, group_offset, fuse_act)
10601094

10611095
if rhs_scale is not None:
10621096
has_scale = True
@@ -1118,7 +1152,7 @@ def make_gmm_configs(
11181152
if isinstance(tile_info, TileSizes):
11191153
tiles = tile_info
11201154
else:
1121-
tiles = tile_info(dims, lhs_cfgs, rhs_cfgs, vmem_limit_bytes, fuse_act)
1155+
tiles = tile_info(dims, lhs_cfgs, rhs_cfgs, vmem_limit_bytes, fuse_act, partial_sum is not None)
11221156

11231157
return GmmConfigs(
11241158
dims=dims,
@@ -1127,6 +1161,7 @@ def make_gmm_configs(
11271161
rhs_cfgs=rhs_cfgs,
11281162
out_dtype=jnp.dtype(out_dtype),
11291163
acc_dtype=jnp.dtype(acc_dtype),
1164+
has_partial_sum=partial_sum is not None,
11301165
zero_init=zero_initialize,
11311166
fuse_act=fuse_act,
11321167
)
@@ -1161,6 +1196,7 @@ def gmm_v2(
11611196
group_sizes: jax.Array, # int32[size_lhs_group]
11621197
rhs_scale: jax.Array | None = None, # [size_group, num_blocks, 1, out_size]
11631198
rhs_bias: jax.Array | None = None, # [size_group, 1, out_size]
1199+
partial_sum: jax.Array | None = None, # [size_m, size_n]
11641200
group_offset: jax.Array | None = None, # int32[1]
11651201
*,
11661202
tile_info: TileSizes | TileFn = calculate_tiling,
@@ -1184,6 +1220,7 @@ def gmm_v2(
11841220
group_sizes: The group sizes of lhs rows of shape [size_lhs_group,].
11851221
rhs_scale: The rhs scale of shape [size_group, num_blocks, 1, out_size].
11861222
rhs_bias: The rhs bias of shape [size_group, 1, out_size].
1223+
partial_sum: Optional. Per-token partial sums of shape [size_m, size_n].
11871224
group_offset: Optional. The group offset of shape [1,].
11881225
tile_info: The tile sizes or tile function to use.
11891226
vmem_limit_bytes: Optional vmem limit in bytes.
@@ -1214,6 +1251,7 @@ def gmm_v2(
12141251
rhs,
12151252
rhs_scale,
12161253
rhs_bias,
1254+
partial_sum,
12171255
group_sizes,
12181256
group_offset,
12191257
tile_info=tile_info,
@@ -1280,20 +1318,35 @@ def gmm_v2(
12801318
aligned_n = align_to(cfgs.out_size_n, num_lanes)
12811319
out_init = jax.ShapeDtypeStruct((dims.size_m, aligned_n), cfgs.out_dtype)
12821320
rhs_weights = WeightsRef(weight=rhs, scale=rhs_scale, bias=rhs_bias)
1321+
in_specs = [
1322+
pl.BlockSpec(memory_space=pltpu.HBM),
1323+
WeightsRef(
1324+
weight=pl.BlockSpec(memory_space=pltpu.HBM),
1325+
scale=rhs_scale_spec,
1326+
bias=rhs_bias_spec,
1327+
),
1328+
]
1329+
1330+
partial_sum_spec = None
1331+
if partial_sum is not None:
1332+
in_specs.append(pl.BlockSpec(memory_space=pltpu.HBM))
1333+
partial_sum_spec = pl.BlockSpec(memory_space=pltpu.HBM)
1334+
in_specs = [
1335+
pl.BlockSpec(memory_space=pltpu.HBM), # lhs
1336+
WeightsRef(
1337+
weight=pl.BlockSpec(memory_space=pltpu.HBM),
1338+
scale=rhs_scale_spec,
1339+
bias=rhs_bias_spec,
1340+
), # rhs_weights
1341+
partial_sum_spec, # partial_sum
1342+
]
12831343

12841344
return pl.pallas_call(
12851345
functools.partial(kernel_main, cfgs=cfgs),
12861346
out_shape=out_init,
12871347
grid_spec=pltpu.PrefetchScalarGridSpec(
12881348
num_scalar_prefetch=2,
1289-
in_specs=[
1290-
pl.BlockSpec(memory_space=pltpu.HBM),
1291-
WeightsRef(
1292-
weight=pl.BlockSpec(memory_space=pltpu.HBM),
1293-
scale=rhs_scale_spec,
1294-
bias=rhs_bias_spec,
1295-
),
1296-
],
1349+
in_specs=in_specs,
12971350
out_specs=pl.BlockSpec(memory_space=pltpu.HBM),
12981351
scratch_shapes=scratch_shapes,
12991352
),
@@ -1304,4 +1357,4 @@ def gmm_v2(
13041357
name=get_scope_name(cfgs),
13051358
cost_estimate=get_cost_estimate(cfgs),
13061359
metadata=get_metadata(cfgs),
1307-
)(group_sizes, group_offset, lhs, rhs_weights)[:, : cfgs.out_size_n]
1360+
)(group_sizes, group_offset, lhs, rhs_weights, partial_sum)[:, : cfgs.out_size_n]

src/maxtext/kernels/megablox/pallas_mosaic_tpu_v2_tgmm_kernel.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,7 @@ def make_tgmm_configs(
263263
tiles=tiles,
264264
lhs_cfgs=lhs_cfgs,
265265
rhs_cfgs=rhs_cfgs,
266+
has_partial_sum=False, # This should always be False until partial sum support is added in bwd pass.
266267
out_dtype=jnp.dtype(out_dtype),
267268
acc_dtype=jnp.dtype(acc_dtype),
268269
# GMM's 'zero_init' zeros unvisited m-rows via DMA, which doesn't apply to

0 commit comments

Comments
 (0)