Skip to content

Commit 79394c0

Browse files
Allow packed all-gather context parallelism
1 parent 0b9f604 commit 79394c0

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
@@ -241,25 +241,35 @@ def create_train_state_fn():
241241
data_iterator, eval_data_iterator = create_data_iterator(config, mesh)
242242
rampup_manager = create_rampup_manager(config, checkpoint_manager)
243243
# Validate context parallelism with packing configuration
244+
context_parallel_strategy = config.context_parallel_strategy.lower()
244245
if config.context_parallel_size > 1 and config.packing:
245246
if config.dataset_type == "synthetic":
246247
raise ValueError(
247248
"Context parallelism with sequence packing is not supported with synthetic data. "
248249
"Please disable sequence packing (set packing=False)."
249250
)
250-
if config.context_parallel_strategy != "ring":
251+
if context_parallel_strategy not in ("all_gather", "ring"):
251252
raise ValueError(
252-
"Context parallelism with 'all_gather' strategy cannot be used with sequence packing. "
253-
"Please use 'ring' strategy instead."
253+
"Context parallelism with sequence packing supports context_parallel_strategy='all_gather' or 'ring'."
254254
)
255+
if (
256+
config.hardware in ("gpu", "gpu_multiprocess")
257+
and config.attention == "cudnn_flash_te"
258+
and not (context_parallel_strategy == "ring" and config.context_parallel_load_balance)
259+
):
260+
raise ValueError("Packing is only supported for load balanced ring attention with context parallelism for GPU.")
255261

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

260266
# Determine load balancing reorder strategy based on whether packing is enabled
261267
if config.context_parallel_reorder_strategy == ReorderStrategy.AUTO:
262-
reorder_strategy = ReorderStrategy.STRIPED if config.packing else ReorderStrategy.DUAL_CHUNK_SWAP
268+
reorder_strategy = (
269+
ReorderStrategy.STRIPED
270+
if config.packing and context_parallel_strategy == "ring"
271+
else ReorderStrategy.DUAL_CHUNK_SWAP
272+
)
263273
else:
264274
reorder_strategy = config.context_parallel_reorder_strategy
265275

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)