[Refactor] Clean up parallel runtime comments (#40632)
This commit is contained in:
@@ -131,8 +131,6 @@ def init_distributed():
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=coord)
|
||||
|
||||
cpu_group = coord.cpu_group
|
||||
|
||||
@@ -62,8 +62,6 @@ def _init_cpu_group() -> dist.ProcessGroup:
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=coord)
|
||||
atexit.register(dist.destroy_process_group)
|
||||
torch.cuda.set_stream(torch.cuda.Stream())
|
||||
|
||||
@@ -78,8 +78,6 @@ def _init_cpu_group() -> dist.ProcessGroup:
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=ps._WORLD)
|
||||
atexit.register(dist.destroy_process_group)
|
||||
logging.disable(logging.INFO)
|
||||
|
||||
@@ -109,8 +109,6 @@ def _init_cpu_group() -> dist.ProcessGroup:
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=coord)
|
||||
atexit.register(dist.destroy_process_group)
|
||||
logging.disable(logging.INFO)
|
||||
|
||||
@@ -130,8 +130,6 @@ def _init_cpu_group_once() -> dist.ProcessGroup:
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=coord)
|
||||
atexit.register(dist.destroy_process_group)
|
||||
cpu_group = coord.cpu_group
|
||||
|
||||
@@ -71,8 +71,6 @@ def _init_cpu_group_once() -> dist.ProcessGroup:
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=ps._WORLD)
|
||||
atexit.register(dist.destroy_process_group)
|
||||
logging.disable(logging.INFO)
|
||||
|
||||
@@ -93,8 +93,6 @@ def _init_cpu_group_once() -> dist.ProcessGroup:
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=coord)
|
||||
atexit.register(dist.destroy_process_group)
|
||||
cpu_group = coord.cpu_group
|
||||
|
||||
@@ -66,8 +66,6 @@ def _init_world():
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=coord)
|
||||
atexit.register(dist.destroy_process_group)
|
||||
logging.disable(logging.INFO)
|
||||
|
||||
@@ -54,8 +54,6 @@ def _init_world():
|
||||
local_rank=local_rank,
|
||||
backend="nccl",
|
||||
)
|
||||
# The context answers a handle from what was stated on it, not from
|
||||
# this module global, so a rank stood up by hand says so itself.
|
||||
get_parallel().override_permanently(world_group=coord)
|
||||
atexit.register(dist.destroy_process_group)
|
||||
cpu_group = coord.cpu_group
|
||||
|
||||
@@ -13,12 +13,7 @@ register_cuda_ci(est_time=10, stage="base-b", runner_config="1-gpu-large")
|
||||
|
||||
@pytest.fixture
|
||||
def stated_tp_group():
|
||||
"""A TP group for a test that runs in a process without one.
|
||||
|
||||
The production call passes the group *into* `use_symmetric_memory`, so
|
||||
stubbing that context manager does not stop the read -- the argument is
|
||||
evaluated first. Stating it on the context answers every spelling.
|
||||
"""
|
||||
"""Provide a TP-group placeholder for kernels with mocked symmetric memory."""
|
||||
from sglang.srt.runtime_context import get_parallel
|
||||
|
||||
with get_parallel().override(tp_group=None):
|
||||
@@ -35,10 +30,7 @@ def test_mhc_fused_post_pre_matches_unfused(
|
||||
pytest.skip("CUDA is required for TileLang mHC kernels")
|
||||
|
||||
monkeypatch.setattr(mhc, "is_dsa_prefill_cp_interleave", lambda: False)
|
||||
# This is a single-process kernel unit test with no TP group initialized.
|
||||
# mhc_pre / mhc_fused_post_pre allocate the MoE input in the symmetric-memory
|
||||
# pool, which asks for the TP group; bypassing the allocation is enough, and
|
||||
# then nothing asks. Mirrors the workaround in test_mxfp4_sm90_cutlass.py.
|
||||
# Disable symmetric-memory allocation for this single-process kernel test.
|
||||
monkeypatch.setattr(mhc, "use_symmetric_memory", lambda *a, **kw: nullcontext())
|
||||
monkeypatch.setattr(mhc, "is_allocation_symmetric", lambda: False)
|
||||
torch.manual_seed(0)
|
||||
|
||||
@@ -37,12 +37,7 @@ dev = "cuda"
|
||||
|
||||
@pytest.fixture
|
||||
def stated_tp_group():
|
||||
"""A TP group for a test that runs in a process without one.
|
||||
|
||||
The production call passes the group *into* `use_symmetric_memory`, so
|
||||
stubbing that context manager does not stop the read -- the argument is
|
||||
evaluated first. Stating it on the context answers every spelling.
|
||||
"""
|
||||
"""Provide a TP-group placeholder for kernels with mocked symmetric memory."""
|
||||
from sglang.srt.runtime_context import get_parallel
|
||||
|
||||
with get_parallel().override(tp_group=None):
|
||||
|
||||
@@ -129,9 +129,6 @@ def mixer2_gated_norm_tensor_parallel(
|
||||
)
|
||||
mixer.weight.weight_loader(mixer.weight, weight)
|
||||
|
||||
# m2 reads tp via get_parallel().tp_size/rank — state a single-rank topology
|
||||
# through the context. Every width that follows from `tp_size` is named:
|
||||
# narrowing one leaf and leaving the quotients behind describes no layout.
|
||||
with get_parallel().override(
|
||||
tp_size=1,
|
||||
tp_rank=0,
|
||||
|
||||
@@ -37,10 +37,7 @@ def _mock_global_server_args(backend="pytorch"):
|
||||
class _DummyTPGroup:
|
||||
device_group = None
|
||||
|
||||
# `Sampler.__init__` asks the context for the group; state one for the rest
|
||||
# of the process, since this process has no distributed init. Not the scoped
|
||||
# `override()`: its context manager would be collected here and take the
|
||||
# value back down with it.
|
||||
# Provide a TP group for sampler initialization without distributed setup.
|
||||
get_parallel().override_permanently(tp_group=_DummyTPGroup())
|
||||
from sglang.srt.runtime_context import get_flags
|
||||
|
||||
|
||||
@@ -47,12 +47,9 @@ register_cpu_ci(est_time=5, suite="stage-b-test-cpu-intel")
|
||||
|
||||
|
||||
def _make_scheduler(grammar_backend_name="none", skip_tokenizer=False):
|
||||
"""Create a mock scheduler with necessary attributes.
|
||||
"""Create a mock scheduler and publish its configuration and placement.
|
||||
|
||||
The grammar manager reads its config and its place in the pipeline from
|
||||
the context, so the settings that used to be hung off the mock are
|
||||
published instead. The caller resets the context; every test here goes
|
||||
through `_GrammarFixture`.
|
||||
The caller must reset the context during teardown.
|
||||
"""
|
||||
reset_context()
|
||||
server_args = ServerArgs(
|
||||
@@ -778,9 +775,7 @@ class TestGrammarManagerPPSync(unittest.TestCase):
|
||||
enter_override(
|
||||
self, get_context().override_server_args(skip_tokenizer_init=True)
|
||||
)
|
||||
# After that override, not before: installing a server-args override
|
||||
# re-resolves the parallel bag from defaults, which puts `pp_size`
|
||||
# back to 1 whatever was published.
|
||||
# Override ranks after the server-args override rebuilds the config bags.
|
||||
enter_scope(self, get_parallel().override(pp_size=pp_size, pp_rank=pp_rank))
|
||||
scheduler.pp_group = pp_group
|
||||
mgr = GrammarManager(scheduler)
|
||||
|
||||
@@ -204,8 +204,6 @@ class TestRegisterToBootstrap(CustomTestCase):
|
||||
def test_rust_attention_dp_replicates_complete_topology_across_hosts(
|
||||
self, mock_put
|
||||
):
|
||||
# The consumer reads the group through `get_parallel()`, so the
|
||||
# stub is stated there rather than in the module the build writes.
|
||||
mock_world_group = MagicMock()
|
||||
success_resp = MagicMock()
|
||||
success_resp.status_code = 200
|
||||
|
||||
@@ -45,9 +45,6 @@ def test_dp_leaders_reuse_node_local_ports(
|
||||
server_args=SimpleNamespace(),
|
||||
model_config=SimpleNamespace(is_multimodal=False),
|
||||
)
|
||||
# Where this rank sits, stated whole: the attention rank follows
|
||||
# from the TP rank and the attention-TP width, and the identities
|
||||
# refuse the combination if it describes no real layout.
|
||||
with parallel.override(
|
||||
tp_rank=tp_rank,
|
||||
attn_dp_rank=dp_rank,
|
||||
|
||||
@@ -93,8 +93,6 @@ def _make_prefill_aware_swa_runner(
|
||||
page_size=1,
|
||||
attn_cp_size=1,
|
||||
tp_size=1,
|
||||
# The backend still reads the runner's frozen record for these two;
|
||||
# same single-rank placement, stated where it looks for it.
|
||||
ps=SimpleNamespace(attn_cp_size=1, tp_size=1),
|
||||
is_draft_worker=False,
|
||||
server_args=server_args,
|
||||
|
||||
@@ -33,12 +33,7 @@ GROUP_SIZE = 32 # MXFP4 block size
|
||||
|
||||
@pytest.fixture
|
||||
def stated_tp_group():
|
||||
"""A TP group for a test that runs in a process without one.
|
||||
|
||||
The production call passes the group *into* `use_symmetric_memory`, so
|
||||
stubbing that context manager does not stop the read -- the argument is
|
||||
evaluated first. Stating it on the context answers every spelling.
|
||||
"""
|
||||
"""Provide a TP-group placeholder for kernels with mocked symmetric memory."""
|
||||
from sglang.srt.runtime_context import get_parallel
|
||||
|
||||
with get_parallel().override(tp_group=None):
|
||||
|
||||
@@ -19,12 +19,7 @@ register_cuda_ci(est_time=14, stage="base-b", runner_config="1-gpu-small")
|
||||
|
||||
@pytest.fixture
|
||||
def stated_tp_group():
|
||||
"""A TP group for a test that runs in a process without one.
|
||||
|
||||
The production call passes the group *into* `use_symmetric_memory`, so
|
||||
stubbing that context manager does not stop the read -- the argument is
|
||||
evaluated first. Stating it on the context answers every spelling.
|
||||
"""
|
||||
"""Provide a TP-group placeholder for kernels with mocked symmetric memory."""
|
||||
from sglang.srt.runtime_context import get_parallel
|
||||
|
||||
with get_parallel().override(tp_group=None):
|
||||
|
||||
@@ -65,12 +65,7 @@ GROUP_SIZE = 32 # MXFP4 block size
|
||||
|
||||
@pytest.fixture
|
||||
def stated_tp_group():
|
||||
"""A TP group for a test that runs in a process without one.
|
||||
|
||||
The production call passes the group *into* `use_symmetric_memory`, so
|
||||
stubbing that context manager does not stop the read -- the argument is
|
||||
evaluated first. Stating it on the context answers every spelling.
|
||||
"""
|
||||
"""Provide a TP-group placeholder for kernels with mocked symmetric memory."""
|
||||
from sglang.srt.runtime_context import get_parallel
|
||||
|
||||
with get_parallel().override(tp_group=None):
|
||||
|
||||
@@ -64,8 +64,6 @@ def load_mlx_scheduler_module():
|
||||
class TestSchedulerIdleStepCounters(CustomTestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
# The loop asks the context where this process sits; nothing here
|
||||
# builds a process group, so the placement arrives by publishing one.
|
||||
enter_scope(self, published_topology(role="scheduler"))
|
||||
|
||||
@parameterized.expand(
|
||||
@@ -184,11 +182,7 @@ class TestSchedulerIdleStepCounters(CustomTestCase):
|
||||
f"{PDMUX_MODULE}.torch.cuda.stream",
|
||||
side_effect=lambda stream: nullcontext(),
|
||||
),
|
||||
# The prefill section runs under the duplicate communicator
|
||||
# `--enable-pdmux` builds, in place of the module flag this
|
||||
# replaces. The loop has no process groups at all, so stand
|
||||
# one in: the scope refuses to open without it rather than
|
||||
# letting prefill quietly share the decode communicator.
|
||||
# PD multiplexing requires a separate prefill communicator.
|
||||
patch.object(
|
||||
parallel_state,
|
||||
"_PDMUX_PREFILL_TP_GROUP",
|
||||
|
||||
@@ -97,12 +97,10 @@ class TestLoadPublisherGating(CustomTestCase):
|
||||
"""
|
||||
|
||||
def _build(self, *, config=ZMQ_ENDPOINT, explicit="auto", ranks=None, **topology):
|
||||
"""Construct a publisher with the socket bind stubbed out, returning
|
||||
(publisher, captured _open_pub_socket mock). Opts in via explicit="auto"
|
||||
by default (the feature is off without it). The topology is published
|
||||
rather than overridden, so the ranks the publisher reads are the ones a
|
||||
layout of that shape actually produces; every read happens in the
|
||||
constructor."""
|
||||
"""Return a publisher and its mocked socket factory under a published topology.
|
||||
|
||||
``explicit="auto"`` enables load publication by default.
|
||||
"""
|
||||
with (
|
||||
published_topology(ranks=ranks, **topology),
|
||||
patch(
|
||||
|
||||
@@ -103,7 +103,6 @@ class TestOutputStreamerCustomizedInfo(unittest.TestCase):
|
||||
)
|
||||
serving_patch.start()
|
||||
observability_patch.start()
|
||||
# The streamer asks the context which rank it is streaming from.
|
||||
enter_scope(self, published_topology(ranks={"dp_rank": 0}))
|
||||
self.addCleanup(serving_patch.stop)
|
||||
self.addCleanup(observability_patch.stop)
|
||||
|
||||
@@ -23,11 +23,9 @@ register_cpu_ci(est_time=11, suite="base-a-test-cpu")
|
||||
|
||||
|
||||
def _published_topology():
|
||||
"""The topology these tests run in.
|
||||
"""Publish WORLD rank 12 with TP=8 and PP=2.
|
||||
|
||||
World rank 12 of a `tp=8, pp=2` world is `tp_rank=4` on the second stage,
|
||||
which puts this process at `attn_dp_rank=1` with `attn_tp_rank=0`: the
|
||||
context derives all of them from that one number and the widths.
|
||||
This gives TP rank 4, PP rank 1, attention-DP rank 1, and attention-TP rank 0.
|
||||
"""
|
||||
return published_topology(
|
||||
role="scheduler",
|
||||
|
||||
@@ -50,7 +50,6 @@ def _make_scheduler(pending_req, *, chunked_req, running_reqs) -> Scheduler:
|
||||
|
||||
class TestPendingChunkedAbortRace(CustomTestCase):
|
||||
def setUp(self):
|
||||
# The abort path asks the context for the pipeline width.
|
||||
enter_scope(self, published_topology())
|
||||
|
||||
def test_req_left_chunked_slot_is_aborted(self):
|
||||
|
||||
@@ -131,7 +131,6 @@ class TestWaitingTimeout(CustomTestCase):
|
||||
|
||||
class TestRunningTimeout(CustomTestCase):
|
||||
def setUp(self):
|
||||
# The poll asks the context for the pipeline width.
|
||||
enter_scope(self, published_topology())
|
||||
|
||||
def test_emits_only_stale_unfinished_reqs_without_marking(self):
|
||||
|
||||
@@ -297,7 +297,6 @@ class TestPPMambaPoolSizing(unittest.TestCase):
|
||||
server_args=SimpleNamespace(),
|
||||
spec_algorithm=SimpleNamespace(is_none=lambda: True),
|
||||
layer_info=SimpleNamespace(start_layer=start, end_layer=end),
|
||||
# The runner carries its placement as plain attributes.
|
||||
attn_dp_size=1,
|
||||
pp_size=pp_size,
|
||||
hybrid_gdn_config=None,
|
||||
|
||||
@@ -302,10 +302,6 @@ class TestHostMemoryBudget(CustomTestCase):
|
||||
)
|
||||
|
||||
def test_ranks_per_host_divides_world_size_by_nodes(self):
|
||||
# The launcher slices ranks uniformly across nodes, so the co-located
|
||||
# rank count is world_size // nnodes — no hostname collective.
|
||||
# tp_size=16 states the launch width the count divides -- the
|
||||
# published configuration is where ranks_per_host reads it from.
|
||||
with (
|
||||
get_context().override_server_args(nnodes=2, tp_size=16),
|
||||
unittest.mock.patch.object(
|
||||
|
||||
@@ -35,8 +35,6 @@ def mock_cpu_env(kv_size=2, tp_size=1, swa_eviction_interval=4):
|
||||
|
||||
with (
|
||||
patch("torch._utils._element_size", return_value=kv_size),
|
||||
# The whole attention triple, not just one leaf: a width that does not
|
||||
# factor describes no layout.
|
||||
get_parallel().override(
|
||||
tp_size=tp_size,
|
||||
attn_tp_size=tp_size,
|
||||
|
||||
@@ -55,8 +55,6 @@ class TestTransformersFallbackSkipSubstrs(CustomTestCase):
|
||||
pass
|
||||
|
||||
with (
|
||||
# `__init__` only stashes the pipeline group, so an empty
|
||||
# stand-in carries it past the read.
|
||||
get_parallel().override(pp_group=SimpleNamespace()),
|
||||
patch(
|
||||
"sglang.srt.models.transformers.get_hf_text_config",
|
||||
|
||||
@@ -83,9 +83,6 @@ class TestGlm5NextBfgFusion(unittest.TestCase):
|
||||
for attn_tp, rank in ((1, 0), (2, 0), (2, 1)):
|
||||
with (
|
||||
self.subTest(route=expected_route, attn_tp=attn_tp, rank=rank),
|
||||
# A width is a whole topology: the attention triple has
|
||||
# to factor `tp_size`, and this process has to sit where
|
||||
# the triple puts it.
|
||||
get_parallel().override(
|
||||
tp_size=4,
|
||||
tp_rank=rank,
|
||||
|
||||
@@ -64,12 +64,7 @@ class _DummyPublisherThread:
|
||||
|
||||
|
||||
def _publish_server_args(test, **fields):
|
||||
"""Publish a config for the reporter under test and return the instance.
|
||||
|
||||
The collector asks the context where this process sits, so the ranks are
|
||||
stated too: without them a rank read falls through to a process group that
|
||||
a unit test has not built.
|
||||
"""
|
||||
"""Install reporter configuration and rank overrides, with test cleanup."""
|
||||
fields.setdefault("decode_log_interval", 40)
|
||||
override = get_context().override_server_args(**fields)
|
||||
server_args = override.install()
|
||||
@@ -297,8 +292,6 @@ class TestForwardPassMetrics(unittest.TestCase):
|
||||
forward_pass_metrics_ipc_name=None,
|
||||
kv_events_config=None,
|
||||
)
|
||||
# The reporter asks the context whether this is the last stage, and
|
||||
# which replica it is reporting for.
|
||||
enter_scope(self, get_parallel().override(pp_rank=0, pp_size=1, dp_rank=2))
|
||||
scheduler.enable_kv_cache_events = False
|
||||
|
||||
@@ -336,7 +329,6 @@ class TestForwardPassMetrics(unittest.TestCase):
|
||||
forward_pass_metrics_ipc_name=None,
|
||||
kv_events_config=None,
|
||||
)
|
||||
# The reporter asks the context whether this is the last stage.
|
||||
enter_scope(self, get_parallel().override(pp_rank=0, pp_size=2))
|
||||
scheduler.enable_kv_cache_events = False
|
||||
|
||||
|
||||
@@ -132,15 +132,7 @@ def _stash_overlay(server_args):
|
||||
|
||||
|
||||
def _live_topology_leaves():
|
||||
"""Names `ParallelContext` answers from a runtime write, not the config.
|
||||
|
||||
Read off the declarations that carry no `fn`, which is what those are.
|
||||
Inferring them from "did the read raise" is wrong -- it raises only while
|
||||
nothing has written the name, so in a process where an earlier test stated
|
||||
one the property answers that value and a leaf check reads it as a config
|
||||
mismatch (`parallel.tp_size: bag=1 resolution=2`). Whether a name is
|
||||
shadowed is a property of the declaration, not of the process.
|
||||
"""
|
||||
"""Return runtime-only parallel fields, identified by declarations without ``fn``."""
|
||||
from sglang.srt.runtime_context import _derived_widths
|
||||
|
||||
return frozenset(n for n, d in _derived_widths().items() if not d.fn)
|
||||
|
||||
@@ -249,7 +249,6 @@ def test_worker_folds_a_gate_admitted_quantized_selector_head(monkeypatch):
|
||||
block_size=8,
|
||||
selector=object(),
|
||||
model_runner=SimpleNamespace(tp_rank=0),
|
||||
# The worker rank-gates its logging on its own frozen record.
|
||||
ps=SimpleNamespace(tp_rank=0),
|
||||
draft_model=SimpleNamespace(lm_head=None),
|
||||
device="cpu",
|
||||
@@ -285,7 +284,6 @@ def test_worker_warns_once_when_selector_sampling_is_disabled(monkeypatch):
|
||||
_selector_sampling_enabled=False,
|
||||
_warned_sampling_fallback=False,
|
||||
model_runner=SimpleNamespace(tp_rank=0),
|
||||
# The worker rank-gates its logging on its own frozen record.
|
||||
ps=SimpleNamespace(tp_rank=0),
|
||||
)
|
||||
batch = SimpleNamespace(sampling_info=SimpleNamespace(is_all_greedy=False))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -316,7 +316,6 @@ class _SchedulerStub:
|
||||
|
||||
class TestSchedulerRecordWeightVersionChange(CustomTestCase):
|
||||
def _scheduler(self, *args, pp_size=1, **kwargs):
|
||||
# The recording path asks the context for the pipeline width.
|
||||
enter_scope(self, published_topology(pp_size=pp_size))
|
||||
scheduler = _SchedulerStub(*args, **kwargs)
|
||||
for name, value in (
|
||||
|
||||
Reference in New Issue
Block a user