[Feature] Add AllGather-KV sequence-parallel attention str… (#4968)

Signed-off-by: zhuhh97 <zhuhonghan@huawei.com>
Signed-off-by: dph97 <nenbaying@163.com>
Signed-off-by: zhuhh97 <zhh@unknown>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: dengyunyang <584797741@qq.com>
This commit is contained in:
authored and GitHub committed 2026-07-24 14:45:26 +00:00
1 parent 8927ec057c
commit 84bbd57e4d
33 files changed
+1181 -135

No files matched your search

@@ -303,6 +303,14 @@ def get_ring_parallel_rank():
return get_sp_group().ring_rank
def get_allgather_parallel_world_size():
return get_sp_group().allgather_world_size
def get_allgather_parallel_rank():
return get_sp_group().allgather_rank
def get_expert_parallel_group_ranks() -> list[list[int]]:
assert vllm_parallel_state._EP is not None, "expert parallel group is not initialized"
assert _EXPERT_PARALLEL_GROUP_RANKS is not None, "expert parallel group ranks are not initialized"
@@ -548,13 +556,12 @@ def set_seq_parallel_pg(
world_size: int,
use_ulysses_low: bool = True,
sp_group_ranks: list[list[int]] | None = None,
) -> tuple[torch.distributed.ProcessGroup, torch.distributed.ProcessGroup]:
sp_allgather_degree: int = 1,
) -> tuple[torch.distributed.ProcessGroup, torch.distributed.ProcessGroup, torch.distributed.ProcessGroup]:
"""
Initialize sequence-parallel Ulysses and Ring process groups.
Initialize Ulysses, Ring, and AllGather-KV process groups.
This builds sequence-parallel (SP) subgroups inside each data-parallel (DP)
slice. The SP group size is sp_ulysses_degree * sp_ring_degree, and
world_size must be divisible by that size.
AllGather-KV is mutually exclusive with Ulysses and Ring.
Args:
sp_ulysses_degree: Size of each Ulysses subgroup.
@@ -569,7 +576,7 @@ def set_seq_parallel_pg(
ranges.
Returns:
ulyssess_pg (torch.distributed.ProcessGroup): The Ulysses process group
ulysses_pg (torch.distributed.ProcessGroup): The Ulysses process group
for this rank.
ring_pg (torch.distributed.ProcessGroup): The Ring process group for
this rank.
@@ -586,6 +593,31 @@ def set_seq_parallel_pg(
- If sp_group_ranks is None, groups are auto-generated within each DP
slice using offsets of size sp_size.
"""
if sp_allgather_degree > 1:
if sp_ulysses_degree > 1 or sp_ring_degree > 1:
raise ValueError("AllGather-KV is mutually exclusive with Ulysses and Ring")
sp_size = sp_allgather_degree
if sp_group_ranks is None:
sp_group_ranks = [list(range(offset, offset + sp_size)) for offset in range(0, world_size, sp_size)]
if len(sp_group_ranks) * sp_size != world_size or any(len(ranks) != sp_size for ranks in sp_group_ranks):
raise ValueError(f"Invalid sp_group_ranks: expected {world_size // sp_size} groups of size {sp_size}.")
ulysses_pg = ring_pg = allgather_pg = None
for group_ranks in sp_group_ranks:
group = torch.distributed.new_group(group_ranks)
if rank in group_ranks:
allgather_pg = group
for singleton_rank in range(world_size):
group = torch.distributed.new_group([singleton_rank])
if rank == singleton_rank:
ulysses_pg = group
for singleton_rank in range(world_size):
group = torch.distributed.new_group([singleton_rank])
if rank == singleton_rank:
ring_pg = group
assert ulysses_pg is not None and ring_pg is not None and allgather_pg is not None
return ulysses_pg, ring_pg, allgather_pg
sp_size = sp_ring_degree * sp_ulysses_degree
dp_size = world_size // sp_size
@@ -619,7 +651,7 @@ def set_seq_parallel_pg(
ulysses_ranks = group_ranks[i * sp_ulysses_degree : (i + 1) * sp_ulysses_degree]
group = torch.distributed.new_group(ulysses_ranks)
if rank in ulysses_ranks:
ulyssess_pg = group
ulysses_pg = group
local_ulysses = list(ulysses_ranks)
for i in range(num_ring_pgs):
ring_ranks = group_ranks[i::num_ring_pgs]
@@ -639,7 +671,7 @@ def set_seq_parallel_pg(
ulysses_ranks = group_ranks[i::num_ulysses_pgs]
group = torch.distributed.new_group(ulysses_ranks)
if rank in ulysses_ranks:
ulyssess_pg = group
ulysses_pg = group
local_ulysses = list(ulysses_ranks)
if local_sp_group is not None:
logger.info(
@@ -662,7 +694,7 @@ def set_seq_parallel_pg(
)
group = torch.distributed.new_group(ulysses_ranks)
if rank in ulysses_ranks:
ulyssess_pg = group
ulysses_pg = group
for i in range(num_ring_pgs):
ring_ranks = list(range(i + offset, sp_size + offset, num_ring_pgs))
@@ -683,9 +715,15 @@ def set_seq_parallel_pg(
ulysses_ranks = list(range(i + offset, sp_size + offset, num_ulysses_pgs))
group = torch.distributed.new_group(ulysses_ranks)
if rank in ulysses_ranks:
ulyssess_pg = group
ulysses_pg = group
return ulyssess_pg, ring_pg
allgather_pg = None
for singleton_rank in range(world_size):
group = torch.distributed.new_group([singleton_rank])
if rank == singleton_rank:
allgather_pg = group
assert allgather_pg is not None
return ulysses_pg, ring_pg, allgather_pg
def initialize_model_parallel(
@@ -694,6 +732,7 @@ def initialize_model_parallel(
sequence_parallel_size: int | None = None,
ulysses_degree: int = 1,
ring_degree: int = 1,
allgather_degree: int = 1,
tensor_parallel_size: int = 1,
pipeline_parallel_size: int = 1,
fully_shard_degree: int = 1,
@@ -710,9 +749,12 @@ def initialize_model_parallel(
data_parallel_size: number of data parallelism groups.
cfg_parallel_size: number of GPUs used for Classifier Free Guidance (CFG) parallelism.
sequence_parallel_size: number of GPUs used for sequence parallelism.
sequence_parallel_size = ulysses_degree * ring_degree
Uses allgather_degree when AllGather-KV is enabled, otherwise
ulysses_degree * ring_degree.
ulysses_degree: number of GPUs used for ulysses sequence parallelism.
ring_degree: number of GPUs used for ring sequence parallelism.
allgather_degree: number of GPUs used for AllGather-KV sequence parallelism
(causal=False only). Mutually exclusive with ulysses/ring in v1.
tensor_parallel_size: number of GPUs used for tensor parallelism.
pipeline_parallel_size: number of GPUs used for pipeline parallelism.
fully_shard_degree: number of GPUs used for fully sharded data parallelism (HSDP shard dimension).
@@ -749,16 +791,22 @@ def initialize_model_parallel(
world_size: int = torch.distributed.get_world_size()
backend = backend or torch.distributed.get_backend(get_world_group().device_group)
if sequence_parallel_size is None:
sequence_parallel_size = ring_degree * ulysses_degree
logger.info(
f"sequence_parallel_size is not provided, using ring_degree * ulysses_degree = {sequence_parallel_size}"
if allgather_degree > 1:
assert ulysses_degree == 1 and ring_degree == 1, (
"AllGather-KV (allgather_degree>1) is mutually exclusive with Ulysses/Ring in v1. "
f"Got ulysses_degree={ulysses_degree}, ring_degree={ring_degree}, "
f"allgather_degree={allgather_degree}."
)
if sequence_parallel_size != ring_degree * ulysses_degree:
expected_sequence_parallel_size = allgather_degree if allgather_degree > 1 else ring_degree * ulysses_degree
if sequence_parallel_size is None:
sequence_parallel_size = expected_sequence_parallel_size
logger.info("sequence_parallel_size is not provided, using %d", sequence_parallel_size)
if sequence_parallel_size != expected_sequence_parallel_size:
raise ValueError(
"sequence_parallel_size is not equal to ring_degree * ulysses_degree,"
f" but got {sequence_parallel_size} != {ring_degree} * {ulysses_degree}"
f"sequence_parallel_size must be {expected_sequence_parallel_size} for the configured SP mode, "
f"but got {sequence_parallel_size}"
)
dit_parallel_size = (
@@ -835,9 +883,10 @@ def initialize_model_parallel(
global _SP
assert _SP is None, "sequence parallel group is already initialized"
ulysses_pg, ring_pg = set_seq_parallel_pg(
ulysses_pg, ring_pg, allgather_pg = set_seq_parallel_pg(
sp_ulysses_degree=ulysses_degree,
sp_ring_degree=ring_degree,
sp_allgather_degree=allgather_degree,
rank=get_world_group().rank_in_group,
world_size=dit_parallel_size,
sp_group_ranks=sp_group_ranks,
@@ -849,6 +898,7 @@ def initialize_model_parallel(
parallel_mode="sequence",
ulysses_group=ulysses_pg,
ring_group=ring_pg,
allgather_group=allgather_pg,
)
if use_moe_parallel_mapping:
# Diffusion normally uses its own SP group. Map it to vLLM PCP only for