Files
vLLM/tests/distributed/test_cpu_all_reduce.py
2026-10-15 15:29:13 -07:00

85 lines
2.9 KiB
Python

# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
"""Tests for cpu_all_reduce_ (gloo, CPU only)."""
import pytest
import torch
import torch.distributed as dist
import torch.multiprocessing as mp
from vllm.distributed.parallel_state import GroupCoordinator
from vllm.distributed.utils import (
cpu_all_reduce_,
stateless_init_torch_distributed_process_group,
)
from vllm.utils.network_utils import get_open_port
pytestmark = pytest.mark.cpu_test
def _make_input(rank: int, world_size: int, step: int) -> torch.Tensor:
# Same shape as the V2 DP metadata tensor; values differ per rank/step.
t = torch.zeros(6, world_size, dtype=torch.int32)
t[:, rank] = torch.arange(6, dtype=torch.int32) * (rank + 1) + step
return t
def _worker(rank: int, world_size: int, port: int, stateless_port: int):
dist.init_process_group(
"gloo",
init_method=f"tcp://127.0.0.1:{port}",
rank=rank,
world_size=world_size,
)
coord = GroupCoordinator(
group_ranks=[list(range(world_size))],
local_rank=0,
torch_distributed_backend="gloo",
use_device_communicator=False,
group_name="cpu_all_reduce_test",
)
# Several steps to exercise tag reuse across consecutive calls.
for step in range(5):
expected = _make_input(rank, world_size, step)
dist.all_reduce(expected, group=coord.cpu_group)
t = _make_input(rank, world_size, step)
out = coord.cpu_all_reduce_(t)
assert out is t
torch.testing.assert_close(t, expected, rtol=0, atol=0)
# Float sums must be bitwise identical on every rank.
t = torch.randn(6, world_size, generator=torch.Generator().manual_seed(rank))
expected = t.clone()
dist.all_reduce(expected, group=coord.cpu_group)
coord.cpu_all_reduce_(t)
torch.testing.assert_close(t, expected)
gathered = [torch.empty_like(t) for _ in range(world_size)]
dist.all_gather(gathered, t, group=coord.cpu_group)
for other in gathered:
assert torch.equal(other, t)
# Unregistered (stateless) process group, as used by the engine-core DP
# group and elastic EP.
stateless_pg = stateless_init_torch_distributed_process_group(
"127.0.0.1", stateless_port, rank, world_size, backend="gloo"
)
expected = _make_input(rank, world_size, 0)
dist.all_reduce(expected)
t = _make_input(rank, world_size, 0)
cpu_all_reduce_(t, stateless_pg)
torch.testing.assert_close(t, expected, rtol=0, atol=0)
dist.destroy_process_group()
# Non-power-of-two sizes exercise the cross-block exchange.
@pytest.mark.parametrize("world_size", [2, 3, 4, 6, 8, 9, 12])
def test_cpu_all_reduce(world_size: int):
mp.spawn(
_worker,
args=(world_size, get_open_port(), get_open_port()),
nprocs=world_size,
join=True,
)