[BugFix] Eliminate distributed test port race by switching to file:// rendezvous (#6468)
Signed-off-by: Nick Cao <ncao@redhat.com>
This commit is contained in:
1 parent
3ad64c6a12
commit
b2c9376539
11 files changed
+117
-237
No files matched your search
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
|
||||
# SPDX-FileCopyrightText: Copyright contributors to the vLLM-Omni project
|
||||
"""Tests for PipelineParallelMixin and related PP communication helpers.
|
||||
|
||||
CPU unit tests exercise mixin logic without a process group. Distributed CPU
|
||||
@@ -9,8 +9,6 @@ real multi-GPU NCCL collectives.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import socket
|
||||
from typing import Literal
|
||||
|
||||
import pytest
|
||||
@@ -21,6 +19,7 @@ from vllm.v1.worker.gpu_worker import AsyncIntermediateTensors
|
||||
|
||||
import vllm_omni.diffusion.distributed.pipeline_parallel as pp_module
|
||||
from tests.helpers.mark import hardware_marks
|
||||
from tests.helpers.runtime import get_distributed_init_method
|
||||
from vllm_omni.diffusion.distributed.cfg_parallel import CFGParallelMixin
|
||||
from vllm_omni.diffusion.distributed.parallel_state import (
|
||||
destroy_distributed_env,
|
||||
@@ -40,31 +39,18 @@ DeviceKind = Literal["cpu", "cuda"]
|
||||
_UNIT_MARKS = [pytest.mark.core_model, pytest.mark.diffusion, pytest.mark.cpu]
|
||||
|
||||
|
||||
def _find_free_port() -> str:
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
s.bind(("127.0.0.1", 0))
|
||||
return str(s.getsockname()[1])
|
||||
|
||||
|
||||
def update_environment_variables(envs_dict: dict[str, str]) -> None:
|
||||
for k, v in envs_dict.items():
|
||||
os.environ[k] = v
|
||||
|
||||
|
||||
def _cleanup_distributed() -> None:
|
||||
"""Destroy omni distributed state and clear test-process leftovers.
|
||||
"""Destroy omni distributed state left dangling after a test.
|
||||
|
||||
``destroy_distributed_env`` leaves ``vllm.distributed.parallel_state._PP``
|
||||
aliased to the destroyed group. Parent-process baselines must clear that
|
||||
dangling reference (and RANK/MASTER_* env) so later unit tests that call
|
||||
dangling reference so later unit tests that call
|
||||
``vllm.distributed.initialize_model_parallel`` do not see stale state.
|
||||
"""
|
||||
destroy_distributed_env()
|
||||
import vllm.distributed.parallel_state as vllm_parallel_state
|
||||
|
||||
vllm_parallel_state._PP = None
|
||||
for key in ("MASTER_ADDR", "MASTER_PORT", "RANK", "WORLD_SIZE", "LOCAL_RANK"):
|
||||
os.environ.pop(key, None)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -416,24 +402,20 @@ def _worker_device(local_rank: int, device_kind: DeviceKind) -> torch.device:
|
||||
def init_dist(
|
||||
local_rank: int,
|
||||
world_size: int,
|
||||
master_port: str,
|
||||
init_method: str,
|
||||
device_kind: DeviceKind,
|
||||
) -> torch.device:
|
||||
"""Initialise the distributed environment for a spawned worker."""
|
||||
device = _worker_device(local_rank, device_kind)
|
||||
if device_kind == "cuda":
|
||||
current_omni_platform.set_device(device)
|
||||
update_environment_variables(
|
||||
{
|
||||
"RANK": str(local_rank),
|
||||
"LOCAL_RANK": str(local_rank),
|
||||
"WORLD_SIZE": str(world_size),
|
||||
"MASTER_ADDR": "localhost",
|
||||
"MASTER_PORT": master_port,
|
||||
}
|
||||
)
|
||||
backend = "gloo" if device_kind == "cpu" else None
|
||||
init_distributed_environment(backend=backend)
|
||||
init_distributed_environment(
|
||||
world_size=world_size,
|
||||
rank=local_rank,
|
||||
distributed_init_method=init_method,
|
||||
backend=backend,
|
||||
)
|
||||
return device
|
||||
|
||||
|
||||
@@ -493,11 +475,11 @@ def make_pipeline_and_inputs(
|
||||
def isend_irecv_worker(
|
||||
local_rank: int,
|
||||
world_size: int,
|
||||
master_port: str,
|
||||
init_method: str,
|
||||
device_kind: DeviceKind,
|
||||
result_queue,
|
||||
):
|
||||
device = init_dist(local_rank, world_size, master_port, device_kind)
|
||||
device = init_dist(local_rank, world_size, init_method, device_kind)
|
||||
initialize_model_parallel(pipeline_parallel_size=world_size, backend=_mp_backend(device_kind))
|
||||
pp_group = get_pp_group()
|
||||
|
||||
@@ -517,13 +499,13 @@ def isend_irecv_worker(
|
||||
_cleanup_distributed()
|
||||
|
||||
|
||||
def _run_isend_irecv(pp_size: int, device_kind: DeviceKind, master_port: str) -> None:
|
||||
def _run_isend_irecv(pp_size: int, device_kind: DeviceKind, init_method: str) -> None:
|
||||
mp_context = torch.multiprocessing.get_context("spawn")
|
||||
manager = mp_context.Manager()
|
||||
q = manager.Queue()
|
||||
torch.multiprocessing.spawn(
|
||||
isend_irecv_worker,
|
||||
args=(pp_size, master_port, device_kind, q),
|
||||
args=(pp_size, init_method, device_kind, q),
|
||||
nprocs=pp_size,
|
||||
)
|
||||
results = {label: tensor for label, tensor in [q.get(), q.get()]}
|
||||
@@ -538,7 +520,7 @@ def _run_isend_irecv(pp_size: int, device_kind: DeviceKind, master_port: str) ->
|
||||
@pytest.mark.parametrize("pp_size", [2])
|
||||
def test_isend_irecv_tensor_dict(pp_size: int):
|
||||
"""isend_tensor_dict / irecv_tensor_dict transfer a tensor dict without loss."""
|
||||
_run_isend_irecv(pp_size, device_kind="cpu", master_port=_find_free_port())
|
||||
_run_isend_irecv(pp_size, device_kind="cpu", init_method=get_distributed_init_method())
|
||||
|
||||
|
||||
@pytest.mark.full_model
|
||||
@@ -547,7 +529,7 @@ def test_isend_irecv_tensor_dict(pp_size: int):
|
||||
@pytest.mark.parametrize("pp_size", [pytest.param(2, marks=_L4_TWO_GPU)])
|
||||
def test_isend_irecv_tensor_dict_parity(pp_size: int):
|
||||
"""Nightly: isend/irecv on real multi-GPU NCCL."""
|
||||
_run_isend_irecv(pp_size, device_kind="cuda", master_port=_find_free_port())
|
||||
_run_isend_irecv(pp_size, device_kind="cuda", init_method=get_distributed_init_method())
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -578,7 +560,7 @@ def compute_single_gpu_baseline(
|
||||
if key in _baseline_cache:
|
||||
return _baseline_cache[key]
|
||||
|
||||
device = init_dist(0, 1, _find_free_port(), device_kind)
|
||||
device = init_dist(0, 1, get_distributed_init_method(), device_kind)
|
||||
try:
|
||||
initialize_model_parallel(pipeline_parallel_size=1, backend=_mp_backend(device_kind))
|
||||
|
||||
@@ -604,7 +586,7 @@ def compute_single_gpu_baseline(
|
||||
def predict_noise_worker(
|
||||
local_rank: int,
|
||||
world_size: int,
|
||||
master_port: str,
|
||||
init_method: str,
|
||||
pp_size: int,
|
||||
cfg_size: int,
|
||||
do_true_cfg: bool,
|
||||
@@ -614,7 +596,7 @@ def predict_noise_worker(
|
||||
result_queue,
|
||||
):
|
||||
"""Generic predict-noise worker parameterized by PP and CFG topology."""
|
||||
device = init_dist(local_rank, world_size, master_port, device_kind)
|
||||
device = init_dist(local_rank, world_size, init_method, device_kind)
|
||||
initialize_model_parallel(
|
||||
pipeline_parallel_size=pp_size,
|
||||
cfg_parallel_size=cfg_size,
|
||||
@@ -661,7 +643,7 @@ def _run_predict_noise(
|
||||
rtol: float,
|
||||
atol: float,
|
||||
device_kind: DeviceKind,
|
||||
master_port: str,
|
||||
init_method: str,
|
||||
) -> None:
|
||||
test_config = {
|
||||
"num_layers": num_layers,
|
||||
@@ -681,7 +663,7 @@ def _run_predict_noise(
|
||||
world_size = pp_size * cfg_size
|
||||
torch.multiprocessing.spawn(
|
||||
predict_noise_worker,
|
||||
args=(world_size, master_port, pp_size, cfg_size, do_true_cfg, dtype, test_config, device_kind, pp_q),
|
||||
args=(world_size, init_method, pp_size, cfg_size, do_true_cfg, dtype, test_config, device_kind, pp_q),
|
||||
nprocs=world_size,
|
||||
)
|
||||
|
||||
@@ -786,7 +768,7 @@ def test_predict_noise(pp_size, cfg_size, do_true_cfg, num_layers, input_seed, r
|
||||
rtol=rtol,
|
||||
atol=atol,
|
||||
device_kind="cpu",
|
||||
master_port=_find_free_port(),
|
||||
init_method=get_distributed_init_method(),
|
||||
)
|
||||
|
||||
|
||||
@@ -809,7 +791,7 @@ def test_predict_noise_parity(pp_size, cfg_size, do_true_cfg, dtype, num_layers,
|
||||
rtol=rtol,
|
||||
atol=atol,
|
||||
device_kind="cuda",
|
||||
master_port=_find_free_port(),
|
||||
init_method=get_distributed_init_method(),
|
||||
)
|
||||
|
||||
|
||||
@@ -824,7 +806,7 @@ def compute_scheduler_step_baseline(
|
||||
device_kind: DeviceKind,
|
||||
) -> torch.Tensor:
|
||||
"""Single-process reference: predict_noise + scheduler_step."""
|
||||
device = init_dist(0, 1, _find_free_port(), device_kind)
|
||||
device = init_dist(0, 1, get_distributed_init_method(), device_kind)
|
||||
try:
|
||||
initialize_model_parallel(pipeline_parallel_size=1, backend=_mp_backend(device_kind))
|
||||
|
||||
@@ -853,7 +835,7 @@ def compute_scheduler_step_baseline(
|
||||
def scheduler_step_worker(
|
||||
local_rank: int,
|
||||
world_size: int,
|
||||
master_port: str,
|
||||
init_method: str,
|
||||
pp_size: int,
|
||||
cfg_size: int,
|
||||
do_true_cfg: bool,
|
||||
@@ -861,7 +843,7 @@ def scheduler_step_worker(
|
||||
device_kind: DeviceKind,
|
||||
result_queue,
|
||||
):
|
||||
device = init_dist(local_rank, world_size, master_port, device_kind)
|
||||
device = init_dist(local_rank, world_size, init_method, device_kind)
|
||||
initialize_model_parallel(
|
||||
pipeline_parallel_size=pp_size,
|
||||
cfg_parallel_size=cfg_size,
|
||||
@@ -905,7 +887,7 @@ def _run_scheduler_step(
|
||||
do_true_cfg: bool,
|
||||
input_seed: int,
|
||||
device_kind: DeviceKind,
|
||||
master_port: str,
|
||||
init_method: str,
|
||||
) -> None:
|
||||
test_config = {
|
||||
"num_layers": 4,
|
||||
@@ -925,7 +907,7 @@ def _run_scheduler_step(
|
||||
world_size = pp_size * cfg_size
|
||||
torch.multiprocessing.spawn(
|
||||
scheduler_step_worker,
|
||||
args=(world_size, master_port, pp_size, cfg_size, do_true_cfg, test_config, device_kind, q),
|
||||
args=(world_size, init_method, pp_size, cfg_size, do_true_cfg, test_config, device_kind, q),
|
||||
nprocs=world_size,
|
||||
)
|
||||
|
||||
@@ -957,7 +939,7 @@ def test_scheduler_step(pp_size, cfg_size, do_true_cfg, input_seed):
|
||||
do_true_cfg=do_true_cfg,
|
||||
input_seed=input_seed,
|
||||
device_kind="cpu",
|
||||
master_port=_find_free_port(),
|
||||
init_method=get_distributed_init_method(),
|
||||
)
|
||||
|
||||
|
||||
@@ -979,5 +961,5 @@ def test_scheduler_step_parity(pp_size, cfg_size, do_true_cfg, input_seed):
|
||||
do_true_cfg=do_true_cfg,
|
||||
input_seed=input_seed,
|
||||
device_kind="cuda",
|
||||
master_port=_find_free_port(),
|
||||
init_method=get_distributed_init_method(),
|
||||
)
|
||||
Reference in new issue
Block a user