Skip to content

Commit a9963bc

Browse files
Merge pull request AI-Hypercomputer#4230 from huytransformer:htn/ag-packing
PiperOrigin-RevId: 937732026
2 parents 6c2defc + 79394c0 commit a9963bc

2 files changed

Lines changed: 90 additions & 4 deletions

File tree

src/maxtext/utils/train_utils.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -243,25 +243,35 @@ def create_train_state_fn():
243243
data_iterator, eval_data_iterator = create_data_iterator(config, mesh)
244244
rampup_manager = create_rampup_manager(config, checkpoint_manager)
245245
# Validate context parallelism with packing configuration
246+
context_parallel_strategy = config.context_parallel_strategy.lower()
246247
if config.context_parallel_size > 1 and config.packing:
247248
if config.dataset_type == "synthetic":
248249
raise ValueError(
249250
"Context parallelism with sequence packing is not supported with synthetic data. "
250251
"Please disable sequence packing (set packing=False)."
251252
)
252-
if config.context_parallel_strategy != "ring":
253+
if context_parallel_strategy not in ("all_gather", "ring"):
253254
raise ValueError(
254-
"Context parallelism with 'all_gather' strategy cannot be used with sequence packing. "
255-
"Please use 'ring' strategy instead."
255+
"Context parallelism with sequence packing supports context_parallel_strategy='all_gather' or 'ring'."
256256
)
257+
if (
258+
config.hardware in ("gpu", "gpu_multiprocess")
259+
and config.attention == "cudnn_flash_te"
260+
and not (context_parallel_strategy == "ring" and config.context_parallel_load_balance)
261+
):
262+
raise ValueError("Packing is only supported for load balanced ring attention with context parallelism for GPU.")
257263

258264
# Apply reordering wrapper to data iterators if context parallelism is enabled
259265
with jax.set_mesh(mesh):
260266
if config.context_parallel_size > 1 and config.context_parallel_load_balance:
261267

262268
# Determine load balancing reorder strategy based on whether packing is enabled
263269
if config.context_parallel_reorder_strategy == ReorderStrategy.AUTO:
264-
reorder_strategy = ReorderStrategy.STRIPED if config.packing else ReorderStrategy.DUAL_CHUNK_SWAP
270+
reorder_strategy = (
271+
ReorderStrategy.STRIPED
272+
if config.packing and context_parallel_strategy == "ring"
273+
else ReorderStrategy.DUAL_CHUNK_SWAP
274+
)
265275
else:
266276
reorder_strategy = config.context_parallel_reorder_strategy
267277

tests/unit/attention_test.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,82 @@ def test_tpu_flash_attention_context_parallel(
865865
f" ici_expert_parallelism={ici_expert_parallelism}.",
866866
)
867867

868+
@parameterized.named_parameters(
869+
{"testcase_name": "no_load_balance", "context_parallel_load_balance": False},
870+
{"testcase_name": "load_balance", "context_parallel_load_balance": True},
871+
)
872+
@pytest.mark.tpu_only
873+
def test_tpu_flash_attention_packed_all_gather_context_parallel(self, context_parallel_load_balance):
874+
"""Test equivalence between packed dot_product and packed flash attention + all-gather context parallelism."""
875+
lnx = jax.random.normal(
876+
self.rng,
877+
shape=(self.global_batch_size, self.max_target_length, self.embed_dim),
878+
dtype=self.dtype,
879+
)
880+
tokens_per_segment = self.max_target_length // 4
881+
segment_ids = jnp.repeat(jnp.arange(1, 5, dtype=jnp.int32), tokens_per_segment)
882+
positions = jnp.tile(jnp.arange(tokens_per_segment, dtype=jnp.int32), 4)
883+
decoder_segment_ids = jnp.broadcast_to(segment_ids, (self.global_batch_size, self.max_target_length))
884+
decoder_positions = jnp.broadcast_to(positions, (self.global_batch_size, self.max_target_length))
885+
mha_generic_output, _ = self._attention_as_mha_generic(
886+
lnx,
887+
lnx,
888+
decoder_segment_ids=decoder_segment_ids,
889+
inputs_positions=decoder_positions,
890+
deterministic=True,
891+
model_mode=MODEL_MODE_TRAIN,
892+
)
893+
generic_state = nnx.state(self._attention_as_mha_generic)
894+
895+
cfg_cp = pyconfig.initialize(
896+
[sys.argv[0], get_test_config_path()],
897+
**self.config_arguments,
898+
ici_context_parallelism=4,
899+
context_parallel_strategy="all_gather",
900+
context_parallel_load_balance=context_parallel_load_balance,
901+
packing=True,
902+
)
903+
devices_array_cp = maxtext_utils.create_device_mesh(cfg_cp)
904+
mesh_cp = Mesh(devices_array_cp, cfg_cp.mesh_axes)
905+
attention_as_mha_flash_cp = Attention(
906+
config=cfg_cp,
907+
num_query_heads=cfg_cp.num_query_heads,
908+
num_kv_heads=cfg_cp.num_kv_heads,
909+
head_dim=cfg_cp.head_dim,
910+
max_target_length=cfg_cp.max_target_length,
911+
max_prefill_predict_length=cfg_cp.max_prefill_predict_length,
912+
inputs_q_shape=lnx.shape,
913+
inputs_kv_shape=lnx.shape,
914+
mesh=mesh_cp,
915+
attention_kernel="flash",
916+
dtype=self.dtype,
917+
dropout_rate=cfg_cp.dropout_rate,
918+
model_mode=MODEL_MODE_PREFILL,
919+
rngs=self.nnx_rng,
920+
)
921+
nnx.update(attention_as_mha_flash_cp, generic_state)
922+
923+
mha_generic_flash_cp_output = attention_test_util.forward_with_context_expert_parallelism(
924+
cfg_cp,
925+
mesh_cp,
926+
attention_as_mha_flash_cp,
927+
lnx,
928+
decoder_segment_ids,
929+
decoder_positions,
930+
)
931+
932+
self.assertTrue(
933+
jax.numpy.allclose(
934+
jax.device_get(mha_generic_output),
935+
jax.device_get(mha_generic_flash_cp_output),
936+
rtol=1e-01,
937+
atol=1e-01,
938+
equal_nan=False,
939+
),
940+
msg="Logits from packed generic dot product and packed flash attention + all-gather context parallelism "
941+
f"are not close. context_parallel_load_balance={context_parallel_load_balance}.",
942+
)
943+
868944
@pytest.mark.tpu_only
869945
def test_dot_product_cache_axis_order(self):
870946
all_axis_orders = tuple(itertools.permutations(range(4)))

0 commit comments

Comments
 (0)