config: the alias form of the runner-side instance read
The previous batch counted `self.server_args.X` and called the runner surface done. It was not: the same read spelled through a local alias -- `server_args = model_runner.server_args` (or `sa = kvc.server_args`, `args = ...`) followed by `server_args.leaf` -- is the same process-global read wearing a local name, and the AST census counts **57 of them** across eleven files that the grep never saw. Census per function, following the alias. 52 were leaves and go to their bag (`spec` 11, `schedule` 9, `memory` 7, `exec.graph` 5, `exec.moe` 5, `parallel` 4, `disagg` 4, `model` 3, `exec.mamba` 2, `exec.overlap` 2). Five were not leaves: three derived members on the eager runner -- `max_speculative_num_draft_tokens` and `enable_mamba_extra_buffer` already had accessors, and `max_prefill_buffer_tokens` gets one (all its inputs are `schedule` leaves plus the configured PP size, so it derives from the bags and follows a post-publish override; `TestDerivedPredicatesAgreeAcrossTiers` pins it against the member over a 48-case matrix) -- plus `get_attention_backends()`, which the same commit routes through `attention_backends()`, and a dict that merely shares the name (`server_args_dict.items`). That dict is the one read left behind. `build_attention_backends` also stops resolving the pair from the record: it runs after publish, so it asks `attention_backends()` like every other consumer. The draft override on the runner still wins first. `dispatch_event_loop`'s three PP checks read the *configured* PP size, not the live topology: the MLX runner stub never initializes torch.distributed, so the live property asserts before the MLX event loop can start (a Codex catch). The configured leaf answers the same value wherever the live groups exist. `flashinfer_gdn_prefill_default`'s guard is the one read here that asks what the *operator* named rather than what the config resolved to, and the bag leaf now answers exactly that: the per-runner auto-default is stamped on the runner and deliberately never recorded process-wide, so nothing writes that leaf after launch and reading it back cannot mistake another runner's default for a flag. Three test doubles injected a `SimpleNamespace`/`MagicMock` record for exactly these reads and now publish instead (pool configurator, cache registry, GDN prefill policy) -- the fixture publishes what the case configures and hands the published instance to the whole-object contracts that still take one. The functions this sweep partially converted stop mixing sources (review catches): the flash-attention constructor's remaining seed reads (`speculative_eagle_topk`, `speculative_algorithm`, both deterministic gates) read their bags next to the leaves already converted; `_should_disable_scheduler_metadata_precompute` reads the parallel config leaves itself instead of taking the record (its alias binding was the last use); and the autotune gates (`disable_flashinfer_autotune`, deterministic, `flashinfer_autotune_skip_ops`) join the moe leaves the same function already reads from the bags. The pool-configurator fixture drops a parameter nothing published or read.
This commit is contained in:
@@ -11,7 +11,7 @@ from types import SimpleNamespace
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from sglang.srt.distributed.parallel_state_wrapper import ParallelState
|
||||
from sglang.srt.runtime_context import get_parallel
|
||||
from sglang.srt.runtime_context import get_parallel, get_server_args
|
||||
from sglang.test.ci.ci_register import register_cpu_ci
|
||||
|
||||
register_cpu_ci(est_time=10, suite="base-a-test-cpu")
|
||||
@@ -35,7 +35,21 @@ def mock_cpu_env(kv_size=2, tp_size=1, swa_eviction_interval=4):
|
||||
yield
|
||||
|
||||
|
||||
def _publish_config(testcase, **fields):
|
||||
"""Publish the configuration a runner double describes.
|
||||
|
||||
Installed per call and restored on the calling case's cleanup, so a failed
|
||||
case cannot leave a partial publish for a later file in a monolithic run.
|
||||
"""
|
||||
from sglang.srt.runtime_context import get_context
|
||||
|
||||
override = get_context().override_server_args(**fields)
|
||||
override.install()
|
||||
testcase.addCleanup(override.restore)
|
||||
|
||||
|
||||
def _make_model_runner(
|
||||
testcase,
|
||||
*,
|
||||
num_kv_heads=4,
|
||||
head_dim=64,
|
||||
@@ -56,7 +70,6 @@ def _make_model_runner(
|
||||
disable_overlap_schedule=False,
|
||||
sliding_window_size=None,
|
||||
speculative_num_draft_tokens=None,
|
||||
max_speculative_num_draft_tokens=None,
|
||||
speculative_algorithm=None,
|
||||
speculative_num_steps=None,
|
||||
speculative_eagle_topk=None,
|
||||
@@ -105,27 +118,29 @@ def _make_model_runner(
|
||||
mr.model_config = mc
|
||||
mr.kv_cache_dtype = "fake_bf16"
|
||||
|
||||
sa = SimpleNamespace()
|
||||
sa.max_total_tokens = None
|
||||
sa.swa_full_tokens_ratio = swa_full_tokens_ratio
|
||||
sa.page_size = page_size
|
||||
sa.disable_radix_cache = disable_radix_cache
|
||||
sa.chunked_prefill_size = chunked_prefill_size
|
||||
sa.disable_overlap_schedule = disable_overlap_schedule
|
||||
sa.speculative_num_draft_tokens = speculative_num_draft_tokens
|
||||
sa.max_speculative_num_draft_tokens = (
|
||||
max_speculative_num_draft_tokens or speculative_num_draft_tokens
|
||||
# The configurator reads the published bags, so the fixture publishes the
|
||||
# configuration it describes. The instance stays for the whole-object
|
||||
# hand-offs the configurator still does.
|
||||
_publish_config(
|
||||
testcase,
|
||||
max_total_tokens=None,
|
||||
swa_full_tokens_ratio=swa_full_tokens_ratio,
|
||||
page_size=page_size,
|
||||
disable_radix_cache=disable_radix_cache,
|
||||
chunked_prefill_size=chunked_prefill_size,
|
||||
disable_overlap_schedule=disable_overlap_schedule,
|
||||
speculative_num_draft_tokens=speculative_num_draft_tokens,
|
||||
speculative_algorithm=speculative_algorithm,
|
||||
speculative_num_steps=speculative_num_steps,
|
||||
speculative_eagle_topk=speculative_eagle_topk,
|
||||
disaggregation_mode=disaggregation_mode,
|
||||
max_running_requests=max_running_requests,
|
||||
disaggregation_decode_extra_slots=disaggregation_decode_extra_slots,
|
||||
enable_hisparse=False,
|
||||
enable_dsa_cache_layer_split=False,
|
||||
kv_cache_dtype="auto",
|
||||
)
|
||||
sa.speculative_algorithm = speculative_algorithm
|
||||
sa.speculative_num_steps = speculative_num_steps
|
||||
sa.speculative_eagle_topk = speculative_eagle_topk
|
||||
sa.disaggregation_mode = disaggregation_mode
|
||||
sa.max_running_requests = max_running_requests
|
||||
sa.disaggregation_decode_extra_slots = disaggregation_decode_extra_slots
|
||||
sa.enable_hisparse = False
|
||||
sa.enable_dsa_cache_layer_split = False
|
||||
sa.kv_cache_dtype = "auto"
|
||||
mr.server_args = sa
|
||||
mr.server_args = get_server_args()
|
||||
|
||||
spec = MagicMock()
|
||||
spec.is_eagle.return_value = False
|
||||
@@ -180,7 +195,7 @@ class TestDefaultConfigurator(unittest.TestCase):
|
||||
"""Default (MHA): available_bytes -> tokens, memory invariant holds."""
|
||||
|
||||
def _run(self, available_bytes, page_size=1, **kwargs):
|
||||
mr = _make_model_runner(page_size=page_size, **kwargs)
|
||||
mr = _make_model_runner(self, page_size=page_size, **kwargs)
|
||||
with mock_cpu_env():
|
||||
from sglang.srt.model_executor.pool_configurator import (
|
||||
create_memory_pool_configurator,
|
||||
@@ -238,10 +253,12 @@ class TestDefaultConfigurator(unittest.TestCase):
|
||||
):
|
||||
num_layers = 2
|
||||
raw = _make_model_runner(
|
||||
self,
|
||||
num_layers=num_layers,
|
||||
use_mla_backend=True,
|
||||
)
|
||||
packed = _make_model_runner(
|
||||
self,
|
||||
num_layers=num_layers,
|
||||
use_mla_backend=True,
|
||||
)
|
||||
@@ -265,6 +282,7 @@ class TestHybridSWAConfigurator(unittest.TestCase):
|
||||
|
||||
def _make_swa_runner(self, full_layers=16, swa_layers=16, ratio=0.5, page_size=1):
|
||||
return _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=list(range(full_layers)),
|
||||
swa_attention_layer_ids=list(range(full_layers, full_layers + swa_layers)),
|
||||
@@ -353,6 +371,7 @@ class TestHybridSWAConfigurator(unittest.TestCase):
|
||||
def test_chunk_cache_cap_accounts_for_spec_topk_page_rounding(self):
|
||||
available = 1_000_000
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=[0],
|
||||
swa_attention_layer_ids=[1],
|
||||
@@ -392,6 +411,7 @@ class TestHybridSWAConfigurator(unittest.TestCase):
|
||||
# 2*chunk(4) + page(1) = 9; cap = 41 * 2 + 9 = 91.
|
||||
available = 1_000_000
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=[0],
|
||||
swa_attention_layer_ids=[1],
|
||||
@@ -422,6 +442,7 @@ class TestHybridSWAConfigurator(unittest.TestCase):
|
||||
def test_chunk_cache_cap_drops_prefill_for_disagg_decode(self):
|
||||
available = 1_000_000
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=[0],
|
||||
swa_attention_layer_ids=[1],
|
||||
@@ -452,6 +473,7 @@ class TestHybridSWAConfigurator(unittest.TestCase):
|
||||
# under overlap.
|
||||
available = 1_000_000
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=[0],
|
||||
swa_attention_layer_ids=[1],
|
||||
@@ -484,6 +506,7 @@ class TestHybridSWAConfigurator(unittest.TestCase):
|
||||
# request count (num_reserved_decode_tokens is a full-pool concern, not SWA).
|
||||
available = 2_000_000
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=[0],
|
||||
swa_attention_layer_ids=[1],
|
||||
@@ -517,6 +540,7 @@ class TestAllSWAConfigurator(unittest.TestCase):
|
||||
|
||||
def _run(self, available_bytes, ratio=0.5, page_size=1, **kwargs):
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=[],
|
||||
swa_attention_layer_ids=list(range(32)),
|
||||
@@ -569,7 +593,7 @@ class TestEagleConfigurator(unittest.TestCase):
|
||||
num_layers = 32
|
||||
eagle_draft_num_layers = 4
|
||||
|
||||
mr = _make_model_runner(num_layers=num_layers)
|
||||
mr = _make_model_runner(self, num_layers=num_layers)
|
||||
mr.spec_algorithm.is_eagle.return_value = True
|
||||
mr.spec_algorithm.is_standalone.return_value = False
|
||||
mr.spec_algorithm.is_none.return_value = False
|
||||
@@ -591,7 +615,7 @@ class TestEagleConfigurator(unittest.TestCase):
|
||||
|
||||
class TestFactory(unittest.TestCase):
|
||||
def test_default_for_non_swa(self):
|
||||
mr = _make_model_runner(is_hybrid_swa=False)
|
||||
mr = _make_model_runner(self, is_hybrid_swa=False)
|
||||
with mock_cpu_env():
|
||||
from sglang.srt.model_executor.pool_configurator import (
|
||||
DefaultPoolConfigurator,
|
||||
@@ -603,6 +627,7 @@ class TestFactory(unittest.TestCase):
|
||||
|
||||
def test_swa_for_hybrid(self):
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=list(range(16)),
|
||||
swa_attention_layer_ids=list(range(16, 32)),
|
||||
@@ -621,6 +646,7 @@ class TestFactory(unittest.TestCase):
|
||||
# SWAChunkCapPoolConfigurator is selected only when max_running_requests is set.
|
||||
def _cfg(max_running_requests):
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=[0],
|
||||
swa_attention_layer_ids=[1],
|
||||
@@ -681,7 +707,7 @@ class TestDflashDraftKvBudget(unittest.TestCase):
|
||||
def test_dcp_replication_scales_draft_budget(self):
|
||||
"""The replicated draft pool spans every DCP virtual location."""
|
||||
draft_kv_per_token = 10_240
|
||||
mr = _make_model_runner()
|
||||
mr = _make_model_runner(self)
|
||||
mr.spec_algorithm.is_dflash_family.return_value = True
|
||||
mr.spec_aux_config = SimpleNamespace(
|
||||
eagle_draft_num_layers=None,
|
||||
@@ -715,6 +741,7 @@ class TestDflashDraftKvBudget(unittest.TestCase):
|
||||
|
||||
def _tokens(draft_kv_per_token):
|
||||
mr = _make_model_runner(
|
||||
self,
|
||||
is_hybrid_swa=True,
|
||||
full_attention_layer_ids=list(range(16)),
|
||||
swa_attention_layer_ids=list(range(16, 32)),
|
||||
|
||||
Reference in New Issue
Block a user