@@ -174,7 +174,7 @@ def _reference_permute_impl(
174174 permuted_probs : jnp.ndarray
175175 Permuted probabilities if probs was provided, None otherwise.
176176 """
177- num_tokens , hidden_size = inp .shape
177+ num_tokens = inp .shape [ 0 ]
178178 num_experts = (row_id_map .shape [1 ] - 1 ) // 2
179179
180180 # Extract destination rows, expert indices, and n_routed from row_id_map
@@ -198,17 +198,16 @@ def _reference_permute_impl(
198198 # This avoids overwriting valid entries at index 0 with zeros
199199 flat_dest_rows_clamped = jnp .where (flat_valid_mask , flat_dest_rows , num_out_tokens )
200200
201- # Gather input tokens and scatter to output
202- output = jnp .zeros ((num_out_tokens , hidden_size ), dtype = inp .dtype )
203- gathered_inp = inp [flat_token_indices ] # [num_tokens * num_experts, hidden_size]
204-
205- # Use segment_sum-like operation via scatter
206- # For each valid (token, expert) pair, write inp[token] to output[dest_row]
207- # Invalid entries target num_out_tokens and get dropped by mode="drop"
208- output = output .at [flat_dest_rows_clamped ].set (
209- gathered_inp ,
210- mode = "drop" ,
201+ # Scatter token indices rather than token rows: gathering a row per (token, expert) slot
202+ # would materialize [num_tokens * num_experts, hidden_size], exhausting GPU memory for large
203+ # configs. Invalid entries target num_out_tokens and get dropped by mode="drop"; output rows
204+ # no token maps to keep the out-of-bounds index num_tokens and are filled with zeros.
205+ src_tokens = (
206+ jnp .full ((num_out_tokens ,), num_tokens , dtype = flat_token_indices .dtype )
207+ .at [flat_dest_rows_clamped ]
208+ .set (flat_token_indices , mode = "drop" )
211209 )
210+ output = inp .at [src_tokens ].get (mode = "fill" , fill_value = 0 ) # [num_out_tokens, hidden_size]
212211
213212 permuted_probs = None
214213 if probs is not None :
0 commit comments