refactor context parallel state (#17213)

Co-authored-by: Shunkang <182541032+Shunkangz@users.noreply.github.co>
This commit is contained in:
dongjiyingdjy
2026-02-13 23:18:17 +08:00
committed by GitHub
co-authored by Shunkang
parent 0012d6a4eb
commit 8b4c364960
27 changed files with 847 additions and 118 deletions
@@ -0,0 +1,290 @@
"""
Test file to verify the correctness of parallel group calculations.
This test validates that the parallel group initialization creates the correct
groups for different parallelism configurations including:
- Tensor parallelism (TP)
- Pipeline parallelism (PP)
- Attention context parallelism (attn_cp)
- Attention data parallelism (attn_dp)
- MoE expert parallelism (EP)
- MoE data parallelism (moe_dp)
These tests call the ACTUAL initialize_model_parallel() function with mocked
distributed backend to verify the group construction logic.
## How These Tests Work
initialize_model_parallel() creates ALL groups for ALL ranks in a single call.
For example, when creating TP groups with tp_size=2 and world_size=8:
group_ranks = [[0,1], [2,3], [4,5], [6,7]] # ALL groups created
_TP = init_model_parallel_group(group_ranks, local_rank, ...)
ALL ranks call this function and get the same complete group structure. Each rank
then figures out which specific group(s) it belongs to.
Our tests:
1. Mock the distributed backend (no real GPUs needed)
2. Mock init_model_parallel_group to capture the group_ranks parameter
3. Call the real initialize_model_parallel()
4. Verify group_ranks contains the expected complete group structure
We only need to simulate rank 0 because we're testing the group creation logic,
not the per-rank group membership logic.
"""
from __future__ import annotations
import sys
from unittest.mock import Mock, patch
import pytest
from sglang.test.ci.ci_register import register_cuda_ci
register_cuda_ci(est_time=5, suite="stage-b-test-small-1-gpu")
# Import the actual parallel_state module
parallel_state = pytest.importorskip("sglang.srt.distributed.parallel_state")
def test_parallel_group_construction_tp8_attn_cp2():
"""
Test parallel group construction for 8 GPU configuration with:
- tensor_model_parallel_size = 8
- attention_context_model_parallel_size = 2
Expected groups based on docstring example:
1 tensor model-parallel group:
[g0, g1, g2, g3, g4, g5, g6, g7]
4 attention context-parallel groups:
[g0, g4], [g1, g5], [g2, g6], [g3, g7]
This test calls the ACTUAL initialize_model_parallel() and verifies the groups.
Note: We simulate only rank 0 here, but initialize_model_parallel() creates
ALL groups for ALL ranks in a single call. We capture these groups via mocking
and verify the complete group structure.
"""
world_size = 8
# Mock the distributed backend
# Note: get_rank() returns 0 because we're testing from a single process,
# but initialize_model_parallel() still creates all groups for all ranks
with patch.object(parallel_state, "_WORLD", None), patch.object(
parallel_state, "_TP", None
), patch.object(parallel_state, "_ATTN_CP", None), patch.object(
parallel_state, "_ATTN_TP", None
), patch.object(
parallel_state, "_PP", None
), patch(
"torch.distributed.is_initialized", return_value=True
), patch(
"torch.distributed.get_world_size", return_value=world_size
), patch(
"torch.distributed.get_rank", return_value=0
), patch(
"torch.distributed.get_backend", return_value="nccl"
):
# Mock init_model_parallel_group to capture the groups being created
created_groups = {}
def mock_init_model_parallel_group(group_ranks, local_rank, backend, **kwargs):
group_name = kwargs.get("group_name", "unknown")
created_groups[group_name] = group_ranks
# Create a mock group object
mock_group = Mock()
mock_group.device_group = Mock()
return mock_group
with patch.object(
parallel_state,
"init_model_parallel_group",
side_effect=mock_init_model_parallel_group,
), patch.object(parallel_state, "get_world_group") as mock_world_group:
# Mock world group
mock_world = Mock()
mock_world.device_group = Mock()
mock_world.local_rank = 0
mock_world_group.return_value = mock_world
# Call the actual function
parallel_state.initialize_model_parallel(
tensor_model_parallel_size=8,
pipeline_model_parallel_size=1,
attention_context_model_parallel_size=2,
)
# Verify TP groups
tp_groups = created_groups.get("tp", [])
assert len(tp_groups) == 1, f"Expected 1 TP group, got {len(tp_groups)}"
assert tp_groups[0] == [
0,
1,
2,
3,
4,
5,
6,
7,
], f"Wrong TP group: {tp_groups[0]}"
# Verify ATTN_CP groups
attn_cp_groups = created_groups.get("attn_cp", [])
assert (
len(attn_cp_groups) == 4
), f"Expected 4 ATTN_CP groups, got {len(attn_cp_groups)}"
expected_attn_cp = [
[0, 4],
[1, 5],
[2, 6],
[3, 7],
]
assert (
attn_cp_groups == expected_attn_cp
), f"Wrong ATTN_CP groups: {attn_cp_groups}"
print("TP=8, Attn CP=2 group construction verified")
# Cleanup
parallel_state.destroy_model_parallel()
def test_parallel_group_construction_tp8_moe_ep4_cp2():
"""
Test parallel group construction for 8 GPU configuration with:
- tensor_model_parallel_size = 8
- expert_model_parallel_size = 4
- moe_data_model_parallel_size = 2
Expected groups:
1 tensor model-parallel group:
[g0, g1, g2, g3, g4, g5, g6, g7]
2 MoE expert-parallel groups:
[g0, g1, g2, g3], [g4, g5, g6, g7]
4 MoE data-parallel groups:
[g0, g4], [g1, g5], [g2, g6], [g3, g7]
"""
world_size = 8
# Mock the distributed backend
with patch.object(parallel_state, "_WORLD", None), patch.object(
parallel_state, "_TP", None
), patch.object(parallel_state, "_MOE_EP", None), patch.object(
parallel_state, "_MOE_DP", None
), patch.object(
parallel_state, "_MOE_TP", None
), patch.object(
parallel_state, "_PP", None
), patch(
"torch.distributed.is_initialized", return_value=True
), patch(
"torch.distributed.get_world_size", return_value=world_size
), patch(
"torch.distributed.get_rank", return_value=0
), patch(
"torch.distributed.get_backend", return_value="nccl"
):
# Mock init_model_parallel_group to capture the groups being created
created_groups = {}
def mock_init_model_parallel_group(group_ranks, local_rank, backend, **kwargs):
group_name = kwargs.get("group_name", "unknown")
created_groups[group_name] = group_ranks
# Create a mock group object
mock_group = Mock()
mock_group.device_group = Mock()
return mock_group
with patch.object(
parallel_state,
"init_model_parallel_group",
side_effect=mock_init_model_parallel_group,
), patch.object(parallel_state, "get_world_group") as mock_world_group:
# Mock world group
mock_world = Mock()
mock_world.device_group = Mock()
mock_world.local_rank = 0
mock_world_group.return_value = mock_world
# Call the actual function
parallel_state.initialize_model_parallel(
tensor_model_parallel_size=8,
expert_model_parallel_size=4,
pipeline_model_parallel_size=1,
moe_data_model_parallel_size=2,
)
# Verify TP groups
tp_groups = created_groups.get("tp", [])
assert len(tp_groups) == 1, f"Expected 1 TP group, got {len(tp_groups)}"
assert tp_groups[0] == [
0,
1,
2,
3,
4,
5,
6,
7,
], f"Wrong TP group: {tp_groups[0]}"
# Verify MOE_EP groups
moe_ep_groups = created_groups.get("moe_ep", [])
assert (
len(moe_ep_groups) == 2
), f"Expected 2 MOE_EP groups, got {len(moe_ep_groups)}"
expected_moe_ep = [
[0, 1, 2, 3],
[4, 5, 6, 7],
]
assert (
moe_ep_groups == expected_moe_ep
), f"Wrong MOE_EP groups: {moe_ep_groups}"
# Verify MOE_DP groups
moe_dp_groups = created_groups.get("moe_dp", [])
assert (
len(moe_dp_groups) == 4
), f"Expected 4 MOE_DP groups, got {len(moe_dp_groups)}"
expected_moe_dp = [
[0, 4],
[1, 5],
[2, 6],
[3, 7],
]
assert (
moe_dp_groups == expected_moe_dp
), f"Wrong MOE_DP groups: {moe_dp_groups}"
print("TP=8, MoE EP=4, MoE CP=2 group construction verified")
# Cleanup
parallel_state.destroy_model_parallel()
if __name__ == "__main__":
# Run tests without requiring GPUs
import sys
try:
test_parallel_group_construction_tp8_attn_cp2()
test_parallel_group_construction_tp8_moe_ep4_cp2()
sys.exit(0)
except AssertionError as e:
print(f"\n Test failed: {e}")
sys.exit(1)
except Exception as e:
print(f"\n Unexpected error: {e}")
import traceback
traceback.print_exc()
sys.exit(1)