[Weight Cache] Support static DP/EP layouts (#33684)

Co-authored-by: siyu <liusy58@linux.alibaba.com>
This commit is contained in:
Xun Sun
2026-08-23 19:57:52 -07:00
committed by GitHub
co-authored by siyu
parent 514b997e6c
commit a90d770c40
9 changed files with 354 additions and 343 deletions
+8 -4
View File
@@ -54,20 +54,24 @@ def run_single_daemon(
"""Run a single daemon process for one (pp_rank, tp_rank)."""
import traceback
from sglang.srt.server_args import ServerArgs
from sglang.srt.weight_cache.daemon import WeightCacheDaemon
daemon = WeightCacheDaemon(
server_args = ServerArgs(
model_path=model_path,
gpu_id=gpu_id,
tp_size=tp_size,
tp_rank=tp_rank,
pp_size=pp_size,
pp_rank=pp_rank,
dp_size=1,
load_format=load_format,
dtype=dtype,
quantization=quantization,
trust_remote_code=trust_remote_code,
)
daemon = WeightCacheDaemon(
server_args=server_args,
gpu_id=gpu_id,
tp_rank=tp_rank,
pp_rank=pp_rank,
dist_init_method=dist_init_method,
)
daemon.socket_path = socket_path
@@ -7,7 +7,7 @@ These cover the pure-Python logic that the GPU end-to-end test
- length-prefixed socket framing (send_msg/recv_msg) over socketpair()
- CacheConfig fingerprint matching / (de)serialization
- quant-config hashing and method-name extraction
- the daemon rank formula and socket/ready path derivation
- daemon spawn configuration and socket/ready path derivation
- the IPC quantization allowlist (the gate that keeps silently-wrong
quant methods off the zero-copy path)
- stale-vs-live daemon file cleanup
@@ -21,6 +21,7 @@ import os
import socket
import struct
import unittest
from types import SimpleNamespace
import torch
@@ -61,6 +62,14 @@ def _make_cache_config(**overrides) -> CacheConfig:
pp_rank=0,
dp_size=1,
ep_size=1,
moe_dp_size=1,
moe_dp_rank=0,
moe_ep_rank=0,
enable_dp_attention=False,
enable_dp_lm_head=False,
attn_cp_size=1,
moe_dense_tp_size=None,
moe_a2a_backend="none",
quant_method="",
quant_config_hash="",
dtype="torch.float16",
@@ -159,6 +168,11 @@ class TestCacheConfig(CustomTestCase):
base = _make_cache_config()
for field, value in (
("tp_rank", 1),
("moe_dp_rank", 1),
("moe_ep_rank", 1),
("enable_dp_attention", True),
("moe_dense_tp_size", 1),
("moe_a2a_backend", "mooncake"),
("dtype", "torch.bfloat16"),
("quant_method", "fp8"),
("model_path", "/models/other"),
@@ -250,6 +264,68 @@ class TestGlobalRankAndPaths(CustomTestCase):
)
class TestDaemonLaunchConfiguration(CustomTestCase):
def test_spawn_forwards_complete_server_args_without_projection(self):
from sglang.srt.weight_cache import daemon
# The spawn helper receives Engine's already-resolved ServerArgs. A
# minimal namespace keeps this projection test CPU-only and
# model-independent; importantly, no EPLB configuration is involved.
server_args = SimpleNamespace(
model_path="/models/demo",
tp_size=8,
pp_size=1,
dp_size=8,
ep_size=8,
moe_dp_size=2,
enable_dp_attention=True,
enable_dp_lm_head=True,
attn_cp_size=2,
moe_dense_tp_size=1,
moe_a2a_backend="mooncake",
deepep_mode="low_latency",
load_format="safetensors",
dtype="bfloat16",
quantization="fp8",
model_loader_extra_config='{"key": "value"}',
trust_remote_code=True,
revision="test-revision",
)
class FakeProcess:
pid = 1234
def start(self):
pass
class FakeContext:
def Process(self, **kwargs):
self.kwargs = kwargs
return FakeProcess()
fake_context = FakeContext()
from unittest import mock
with mock.patch.object(
daemon.multiprocessing, "get_context", return_value=fake_context
) as get_context:
result = daemon.spawn_weight_cache_daemon(
server_args,
gpu_id=3,
tp_rank=3,
pp_rank=0,
dist_init_method="tcp://127.0.0.1:29500",
)
get_context.assert_called_once_with("spawn")
self.assertIsInstance(result, FakeProcess)
self.assertIs(fake_context.kwargs["target"], daemon.run_weight_cache_daemon)
self.assertEqual(
fake_context.kwargs["args"],
(server_args, 3, 3, 0, "tcp://127.0.0.1:29500"),
)
class TestIpcQuantAllowlist(CustomTestCase):
def test_unquantized_is_supported(self):
self.assertTrue(is_ipc_quant_supported("", None))
@@ -179,6 +179,22 @@ _EXPOSED = {
("utils/common.py", "speculative_num_draft_tokens"),
("utils/common.py", "speculative_num_steps"),
("utils/hf_transformers/processor.py", "image_processor_backend"),
# The daemon command and constructor snapshot the resolved startup layout
# before the daemon's loading lifecycle can apply any runtime overrides.
("weight_cache/daemon.py", "attn_cp_size"),
("weight_cache/daemon.py", "deepep_mode"),
("weight_cache/daemon.py", "dp_size"),
("weight_cache/daemon.py", "dtype"),
("weight_cache/daemon.py", "enable_dp_attention"),
("weight_cache/daemon.py", "enable_dp_lm_head"),
("weight_cache/daemon.py", "ep_size"),
("weight_cache/daemon.py", "load_format"),
("weight_cache/daemon.py", "model_path"),
("weight_cache/daemon.py", "moe_a2a_backend"),
("weight_cache/daemon.py", "moe_dense_tp_size"),
("weight_cache/daemon.py", "moe_dp_size"),
("weight_cache/daemon.py", "pp_size"),
("weight_cache/daemon.py", "quantization"),
}
# Pairs whose resolution write only happens on a CUDA host (capability or
@@ -196,6 +212,11 @@ _OVERRIDDEN_AND_READ = {
("dllm/config.py", "model_path"),
("entrypoints/engine.py", "reasoning_parser"),
("entrypoints/engine.py", "tool_call_parser"),
("weight_cache/daemon.py", "dp_size"),
("weight_cache/daemon.py", "dtype"),
("weight_cache/daemon.py", "ep_size"),
("weight_cache/daemon.py", "load_format"),
("weight_cache/daemon.py", "model_path"),
("configs/model_config.py", "dtype"),
("configs/model_config.py", "model_path"),
("mem_cache/kv_cache_builder.py", "hicache_storage_backend"),