Add TPU USP context parallelism - #4836
Conversation
|
🤖 Hi @huytransformer, I've received your request, and I'm working on it now! You can track my progress in the logs for more details. |
There was a problem hiding this comment.
Code Review
This pull request introduces support for USP (Ulysses-over-ring) context parallelism on the TPU Tokamax Splash path for training in MaxText. It adds configuration options, layout validation helpers, and integrates the hybrid strategy into the attention operation, accompanied by extensive unit and collective tests. The review feedback focuses on improving robustness by adding defensive null checks in usp_attention.py to prevent potential TypeError exceptions on unsharded tensors, and refactoring device platform checks in attention_op.py to use a more idiomatic numpy flat indexing approach.
There was a problem hiding this comment.
This pull request introduces robust support for TPU USP (Ulysses-over-Ring) context parallelism, allowing MaxText to scale hybrid context parallelism efficiently by combining ring-attention sequence rotation with Ulysses head-exchange all-to-alls. The overall design is exceptionally high quality, extremely clean, highly idiomatic, and very well integrated with the existing MaxText config validation and Tokamax splash-attention paths.
🔍 General Feedback
- Exceptional Testing Quality: The newly added unit tests (
usp_attention_test.pyandusp_collective_test.py) are incredibly thorough. Specifically, forcing an 8-device CPU mesh in a standalone subprocess to verify multi-dimensional collectives, attention parity, and gradient correctness without physical TPU hardware is a masterclass in robust JAX testing. - Robust Layout and Runtime Checks: The USP-specific configurations and constraints are systematically validated at both startup (config types validation) and layer initialization (attention operator layout checks), protecting against unsupported setups.
- Seamless Integration: The extension of logical axis rules in
base.ymland physical axes configuration inmaxtext_utils.pyis elegant, maintaining full backward compatibility with non-USP paths.
| dense_grads = jax.grad(dense_loss, argnums=(0, 1, 2))(query, key, value) | ||
| usp_grads = jax.grad(usp_loss, argnums=(0, 1, 2))(query, key, value) | ||
| for name, dense_grad, usp_grad in zip(("dQ", "dK", "dV"), dense_grads, usp_grads): | ||
| np.testing.assert_allclose(jax.device_get(usp_grad), jax.device_get(dense_grad), atol=1e-5, err_msg=name) |
There was a problem hiding this comment.
🟢 Low - Adding rtol=1e-5 to gradient comparison as well ensures numerical stability across different architectures.
| np.testing.assert_allclose(jax.device_get(usp_grad), jax.device_get(dense_grad), atol=1e-5, err_msg=name) | |
| np.testing.assert_allclose(jax.device_get(usp_grad), jax.device_get(dense_grad), rtol=1e-5, atol=1e-5, err_msg=name) | |
| ```</COMMENT> |
| raise ValueError( | ||
| "TPU USP attention requires max_target_length to be divisible by ici_context_parallelism squared." | ||
| ) | ||
| if self.num_query_heads % usp_ulysses_size != 0: |
There was a problem hiding this comment.
🟡 Medium - Checking that self.num_query_heads is divisible by usp_ulysses_size is a great static sanity check. Note that if tensor model parallelism is enabled, heads are first sharded across tensor_parallelism. The actual constraint is that the local query head count (after tensor sharding) must be divisible by Ulysses size. While validate_head_sharding dynamically catches this during execution, we could consider documenting this interaction or eventually validating it here statically.
|
|
||
| dense_output = _dense_reference_attention(query, key, value, segment_ids) | ||
| usp_output = usp_attention_fn(query, key, value, segment_ids) | ||
| np.testing.assert_allclose(jax.device_get(usp_output), jax.device_get(dense_output), atol=1e-5) |
There was a problem hiding this comment.
🟢 Low - Adding a relative tolerance (rtol) to assert_allclose is recommended to prevent potential flakiness under different CPU architectures or compiler versions, especially when comparing standard dot-product attention with block/gathered attention.
| np.testing.assert_allclose(jax.device_get(usp_output), jax.device_get(dense_output), atol=1e-5) | |
| np.testing.assert_allclose(jax.device_get(usp_output), jax.device_get(dense_output), rtol=1e-5, atol=1e-5) | |
| ```</COMMENT> |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
f9a0d97 to
293acad
Compare
16a60ea to
27a6b5f
Compare
27a6b5f to
b9eea0d
Compare
Description
This PR introduces
context_parallel_strategy=usp(USP, Ulysses over ring). Follow up to #4687.Currently does not support load balancing + sequence packing.
Tests
python3 -m pytest tests/unit/configs_value_test.py tests/unit/usp_attention_test.py tests/unit/usp_collective_test.pyPassed.
python3 -m pytest tests/unit/attention_test.py -k usp3 passed.
Performance
llama3-8b, v5p (64 chips), CP64, bf16, synthetic data, global batch 1, no load-balancing
Reporting median step time (s)
Example repro command:
Checklist
Before submitting this PR, please make sure (put X in square brackets):
gemini-reviewlabel.