Skip to content

Commit 5a9aca2

Browse files
committed
add cost estimates for ragged sort kernels
1 parent dd9c267 commit 5a9aca2

6 files changed

Lines changed: 182 additions & 2 deletions

File tree

src/maxtext/configs/base.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ ragged_gather_fallback: false # when true, unconditionally use the JAX reference
226226
# ragged gather SparseCore kernel. When false (default), use the SparseCore kernel.
227227
ragged_gather_reduce_fallback: false # when true, unconditionally use the JAX reference implementation instead of the
228228
# ragged gather reduce SparseCore kernel. When false (default), use the SparseCore kernel.
229+
ragged_gather_cost_estimate_flops: -1 # -1 means auto-compute, any > 0 value overrides the flop cost estimate for the ragged gather kernel
230+
ragged_gather_reduce_cost_estimate_flops: -1 # -1 means auto-compute, any > 0 value overrides the flop cost estimate for the ragged gather reduce kernel
231+
ragged_gather_cost_estimate_bytes_accessed: -1 # -1 means auto-compute, any > 0 value overrides the bytes_accessed cost estimate for the ragged gather kernel
232+
ragged_gather_reduce_cost_estimate_bytes_accessed: -1 # -1 means auto-compute, any > 0 value overrides the bytes_accessed cost estimate for the ragged gather reduce kernel
229233
# tunable tiling dimensions used for mlp gmm
230234
# megablox/jax ragged dot - supports forward pass only (6 configs: `wi_tile_fwd...` and `wo_tile_fwd_...`)
231235
# tokamax ragged dot - supports all 18 configs

src/maxtext/configs/types.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -760,6 +760,26 @@ class MoEGeneral(BaseModel):
760760
description="When true, unconditionally use the JAX reference implementation instead of the ragged gather "
761761
"reduce SparseCore kernel. When false (default), use the SparseCore kernel.",
762762
)
763+
ragged_gather_cost_estimate_flops: int = Field(
764+
-1,
765+
description="Flop cost estimate override for the ragged gather kernel. "
766+
"-1 means auto-compute, any > 0 value overrides the flop cost estimate.",
767+
)
768+
ragged_gather_reduce_cost_estimate_flops: int = Field(
769+
-1,
770+
description="Flop cost estimate override for the ragged gather reduce kernel. "
771+
"-1 means auto-compute, any > 0 value overrides the flop cost estimate.",
772+
)
773+
ragged_gather_cost_estimate_bytes_accessed: int = Field(
774+
-1,
775+
description="Bytes-accessed cost estimate override for the ragged gather kernel. "
776+
"-1 means auto-compute, any > 0 value overrides the bytes_accessed cost estimate.",
777+
)
778+
ragged_gather_reduce_cost_estimate_bytes_accessed: int = Field(
779+
-1,
780+
description="Bytes-accessed cost estimate override for the ragged gather reduce kernel. "
781+
"-1 means auto-compute, any > 0 value overrides the bytes_accessed cost estimate.",
782+
)
763783
use_random_routing: bool = Field(False, description="Whether to use random routing for debugging.")
764784
interleave_moe_layer_step: int = Field(1, description="Frequency of MoE layers, e.g., 2 means every 2nd layer is MoE.")
765785
moe_fsdp_use_two_stage_all_gather: bool = Field(

src/maxtext/kernels/ragged/ragged_gather.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,59 @@ def dma_write_loop(col_vmem_start):
271271
inner_kernel()
272272

273273

274+
def get_cost_estimate(
275+
out_size: int,
276+
hidden_size: int,
277+
dtype_bytes: int,
278+
has_weights: bool,
279+
flops_override: int = -1,
280+
bytes_accessed_override: int = -1,
281+
) -> pl.CostEstimate:
282+
"""Returns a cost estimate for the ragged gather kernel.
283+
284+
The ragged gather is primarily a data-movement kernel: it gathers rows from
285+
an input table according to an index array. When ``has_weights`` is True an
286+
additional element-wise multiply is performed.
287+
288+
Args:
289+
out_size: Number of output rows (after padding).
290+
hidden_size: Number of columns in the input / output.
291+
dtype_bytes: Size of one element in bytes (e.g. 2 for bf16, 4 for f32).
292+
has_weights: Whether per-row weighting is applied.
293+
flops_override: If > 0, use this value as the flop count instead of
294+
auto-computing. -1 (default) means auto-compute.
295+
bytes_accessed_override: If > 0, use this value as bytes_accessed instead
296+
of auto-computing. -1 (default) means auto-compute.
297+
298+
Returns:
299+
A ``pl.CostEstimate`` suitable for XLA scheduling.
300+
"""
301+
# Flops: one multiply per element when weighting is enabled.
302+
if flops_override > 0:
303+
flops = flops_override
304+
else:
305+
flops = out_size * hidden_size if has_weights else 0
306+
307+
if bytes_accessed_override > 0:
308+
bytes_accessed = bytes_accessed_override
309+
else:
310+
# Bytes accessed:
311+
# read – gathered input rows + indices (int32) + optional weights (f32)
312+
# write – output rows
313+
bytes_in = out_size * hidden_size * dtype_bytes # input rows read
314+
bytes_in += out_size * 4 # indices (int32)
315+
if has_weights:
316+
bytes_in += out_size * 4 # weights (float32)
317+
bytes_out = out_size * hidden_size * dtype_bytes # output rows written
318+
bytes_accessed = bytes_in + bytes_out
319+
320+
return pl.CostEstimate(
321+
flops=flops,
322+
bytes_accessed=bytes_accessed,
323+
transcendentals=0,
324+
)
325+
326+
274327
def _fallback_implementation(
275328
x: jax.Array,
276329
indices: jax.Array,
@@ -308,7 +361,9 @@ def calculate_col_size(hidden_size: int) -> int:
308361
return pl.cdiv(hidden_size, (num_cols * num_lanes)) * num_lanes
309362

310363

311-
@functools.partial(jax.jit, static_argnames=("has_weights", "enforce_fallback"))
364+
@functools.partial(
365+
jax.jit, static_argnames=("has_weights", "enforce_fallback", "flops_override", "bytes_accessed_override")
366+
)
312367
def ragged_gather(
313368
x: jax.Array,
314369
indices: jax.Array,
@@ -317,6 +372,8 @@ def ragged_gather(
317372
weights: jax.Array | None = None,
318373
has_weights: bool = False,
319374
enforce_fallback: bool = False,
375+
flops_override: int = -1,
376+
bytes_accessed_override: int = -1,
320377
) -> jax.Array:
321378
"""Perform gather on indices within dynamic array start and end.
322379
@@ -333,6 +390,10 @@ def ragged_gather(
333390
enforce_fallback: Static bool flag. When ``True``, unconditionally use the
334391
JAX reference implementation instead of the SparseCore kernel.
335392
When ``False`` (default), use the SparseCore kernel and raise any error.
393+
flops_override: If > 0, use this value as the flop count instead of
394+
auto-computing. -1 (default) means auto-compute.
395+
bytes_accessed_override: If > 0, use this value as bytes_accessed instead
396+
of auto-computing. -1 (default) means auto-compute.
336397
337398
Returns:
338399
Gathered output of shape ``(indices_size, hidden_size)``.
@@ -395,6 +456,14 @@ def ragged_gather(
395456
compiler_params=pltpu.CompilerParams( # pytype: disable=wrong-keyword-args
396457
**_COMPILER_PARAMS,
397458
),
459+
cost_estimate=get_cost_estimate(
460+
out_size=out_size + out_pad_size,
461+
hidden_size=aligned_hidden_size,
462+
dtype_bytes=jax.dtypes.itemsize_bits(dtype) // 8,
463+
has_weights=has_weights,
464+
flops_override=flops_override,
465+
bytes_accessed_override=bytes_accessed_override,
466+
),
398467
mesh=vector_mesh,
399468
name="sc_ragged_gather",
400469
**{

src/maxtext/kernels/ragged/ragged_gather_reduce.py

Lines changed: 68 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,61 @@ def _align_to(a, b):
4949
return ((a + b - 1) // b) * b
5050

5151

52+
def get_cost_estimate(
53+
padded_input_size: int,
54+
aligned_hidden_size: int,
55+
reduce_group_size: int,
56+
input_dtype_bytes: int,
57+
bytes_accessed_override: int = -1,
58+
flops_override: int = -1,
59+
) -> pl.CostEstimate:
60+
"""Returns a cost estimate for the ragged gather-reduce kernel.
61+
62+
The kernel gathers rows, multiplies each by a scalar weight, and reduces
63+
(sums) every ``reduce_group_size`` rows into one output row.
64+
65+
Args:
66+
padded_input_size: Total number of source rows (after padding).
67+
aligned_hidden_size: Number of columns (after alignment).
68+
reduce_group_size: Number of source rows reduced into each output row.
69+
input_dtype_bytes: Size of one input element in bytes.
70+
bytes_accessed_override: If > 0, use this value as bytes_accessed instead
71+
of auto-computing. -1 (default) means auto-compute.
72+
flops_override: If > 0, use this value as the flop count instead of
73+
auto-computing. -1 (default) means auto-compute.
74+
75+
Returns:
76+
A ``pl.CostEstimate`` suitable for XLA scheduling.
77+
"""
78+
# Flops:
79+
# - one multiply per element for weighting: padded_input_size * aligned_hidden_size
80+
# - one add per element for reduction: padded_input_size * aligned_hidden_size
81+
if flops_override > 0:
82+
flops = flops_override
83+
else:
84+
flops = 2 * padded_input_size * aligned_hidden_size
85+
86+
if bytes_accessed_override > 0:
87+
bytes_accessed = bytes_accessed_override
88+
else:
89+
# Bytes accessed:
90+
# read – input rows + src_indices (int32) + dst_indices (int32) + topk_weights (f32)
91+
# write – output rows (float32)
92+
bytes_in = padded_input_size * aligned_hidden_size * input_dtype_bytes # input rows
93+
bytes_in += padded_input_size * 4 # src_indices (int32)
94+
bytes_in += padded_input_size * 4 # dst_indices (int32)
95+
bytes_in += padded_input_size * 4 # topk_weights (float32)
96+
output_rows = padded_input_size // reduce_group_size
97+
bytes_out = output_rows * aligned_hidden_size * 4 # output rows (float32)
98+
bytes_accessed = bytes_in + bytes_out
99+
100+
return pl.CostEstimate(
101+
flops=flops,
102+
bytes_accessed=bytes_accessed,
103+
transcendentals=0,
104+
)
105+
106+
52107
def _fallback_implementation(
53108
x: jax.Array,
54109
indices: jax.Array,
@@ -370,14 +425,18 @@ def _preprocess(
370425
)
371426

372427

373-
@functools.partial(jax.jit, static_argnames=("reduce_group_size", "enforce_fallback"))
428+
@functools.partial(
429+
jax.jit, static_argnames=("reduce_group_size", "enforce_fallback", "flops_override", "bytes_accessed_override")
430+
)
374431
def ragged_gather_reduce(
375432
x: jax.Array,
376433
indices: jax.Array,
377434
topk_weights: jax.Array,
378435
valid_rows_mask: jax.Array,
379436
reduce_group_size: int,
380437
enforce_fallback: bool = False,
438+
flops_override: int = -1,
439+
bytes_accessed_override: int = -1,
381440
) -> jax.Array:
382441
"""Gathers `x` according to `indices`, applies weights and masks, and reduces.
383442
@@ -502,6 +561,14 @@ def ragged_gather_reduce(
502561
compiler_params=pltpu.CompilerParams( # pytype: disable=wrong-keyword-args
503562
**_COMPILER_PARAMS,
504563
),
564+
cost_estimate=get_cost_estimate(
565+
padded_input_size=padded_input_size,
566+
aligned_hidden_size=aligned_hidden_size,
567+
reduce_group_size=reduce_group_size,
568+
input_dtype_bytes=dtype_bytes,
569+
flops_override=flops_override,
570+
bytes_accessed_override=bytes_accessed_override,
571+
),
505572
mesh=vector_mesh,
506573
name="sc_ragged_gather_reduce",
507574
**{

src/maxtext/kernels/ragged/ragged_sort.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ def ring_ragged_sort(
3030
buffer_size=None,
3131
enforce_gather_fallback=False,
3232
enforce_gather_reduce_fallback=False,
33+
gather_flops_override=-1,
34+
gather_reduce_flops_override=-1,
35+
gather_bytes_accessed_override=-1,
36+
gather_reduce_bytes_accessed_override=-1,
3337
):
3438
"""Ragged-gather variant for AG-RS Expert Parallelism token routing.
3539
@@ -105,6 +109,8 @@ def _ring_ragged_sort_fwd(hidden_states_local, topk_indices_local):
105109
shard_output_start[None],
106110
shard_output_end[None],
107111
enforce_fallback=enforce_gather_fallback,
112+
flops_override=gather_flops_override,
113+
bytes_accessed_override=gather_bytes_accessed_override,
108114
)
109115
else:
110116
local_buffer_size = buffer_size
@@ -126,6 +132,8 @@ def _ring_ragged_sort_fwd(hidden_states_local, topk_indices_local):
126132
jnp.int32(0)[None],
127133
gather_end[None],
128134
enforce_fallback=enforce_gather_fallback,
135+
flops_override=gather_flops_override,
136+
bytes_accessed_override=gather_bytes_accessed_override,
129137
)
130138

131139
out = (x, group_sizes_local, topk_argsort_revert_indices)
@@ -219,6 +227,10 @@ def ring_ragged_unsort(
219227
topk_weights,
220228
enforce_gather_fallback=False,
221229
enforce_gather_reduce_fallback=False,
230+
gather_flops_override=-1,
231+
gather_reduce_flops_override=-1,
232+
gather_bytes_accessed_override=-1,
233+
gather_reduce_bytes_accessed_override=-1,
222234
):
223235
"""Dual of :func:`ring_ragged_sort`.
224236

src/maxtext/layers/moe.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,10 @@ def permute(
864864
buffer_size=buffer_size,
865865
enforce_gather_fallback=self.config.ragged_gather_fallback,
866866
enforce_gather_reduce_fallback=self.config.ragged_gather_reduce_fallback,
867+
gather_flops_override=self.config.ragged_gather_cost_estimate_flops,
868+
gather_reduce_flops_override=self.config.ragged_gather_reduce_cost_estimate_flops,
869+
gather_bytes_accessed_override=self.config.ragged_gather_cost_estimate_bytes_accessed,
870+
gather_reduce_bytes_accessed_override=self.config.ragged_gather_reduce_cost_estimate_bytes_accessed,
867871
)
868872
else:
869873
flatten_selected_experts = jnp.ravel(selected_experts)
@@ -945,6 +949,10 @@ def unpermute(
945949
topk_weights=flat_weights,
946950
enforce_gather_fallback=self.config.ragged_gather_fallback,
947951
enforce_gather_reduce_fallback=self.config.ragged_gather_reduce_fallback,
952+
gather_flops_override=self.config.ragged_gather_cost_estimate_flops,
953+
gather_reduce_flops_override=self.config.ragged_gather_reduce_cost_estimate_flops,
954+
gather_bytes_accessed_override=self.config.ragged_gather_cost_estimate_bytes_accessed,
955+
gather_reduce_bytes_accessed_override=self.config.ragged_gather_reduce_cost_estimate_bytes_accessed,
948956
)
949957
else:
950958
unsort_intermediate = _sort_activations(

0 commit comments

Comments
 (0)