@@ -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
225226class 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
274285def 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 ]
0 commit comments