[HiCache] Fix the compatibility between PP and HiCache (L2). (#27285)

Co-authored-by: ybyang <ybyang7@iflytek.com>
Co-authored-by: hzh0425 <hzh0425@apache.org>
Co-authored-by: shangmingc <csmthu@gmail.com>
Co-authored-by: 晟海 <huangtingwei.htw@antgroup.com>
This commit is contained in:
Chao Shi
2026-06-06 16:57:23 +08:00
committed by GitHub
co-authored by ybyang hzh0425 shangmingc 晟海
parent aa5213abb1
commit 42fe025280
10 changed files with 313 additions and 76 deletions
@@ -0,0 +1,107 @@
import unittest
from types import SimpleNamespace
from urllib.parse import urlparse
from sglang.srt.utils import kill_process_tree
from sglang.test.ci.ci_register import register_cuda_ci
from sglang.test.kits.unified_radix_cache_kit import UnifiedRadixTreeTestMixin
from sglang.test.kl_multiturn_utils import get_input_ids
from sglang.test.test_utils import (
DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
DEFAULT_URL_FOR_TEST,
CustomTestCase,
popen_launch_server,
)
register_cuda_ci(est_time=400, stage="base-c", runner_config="4-gpu-h100")
QWEN3_30B_MODEL = "Qwen/Qwen3-30B-A3B-FP8"
def _assert_pp_decode_cached_tokens(result, history_len, output_len, label):
expected = history_len + output_len
actual = result["meta_info"]["cached_tokens"]
lower = max(0, expected - 1)
assert (
lower <= actual <= expected
), f"{label}: expected cached_tokens in [{lower}, {expected}], got {actual}"
class TestUnifiedQwen3HiCachePP(UnifiedRadixTreeTestMixin, CustomTestCase):
"""Qwen3-30B-A3B-FP8 + HiCache + PP + UnifiedRadixCache."""
hicache_io_backend = "direct"
hicache_mem_layout = "page_first_direct"
max_running_requests = 4
kl_threshold = 0.012
gsm8k_threshold = 0.7
num_gsm8k_questions = 50
mmlu_threshold = 0.7
decode_cache_assert = staticmethod(_assert_pp_decode_cached_tokens)
def test_gsm8k(self):
from sglang.test.few_shot_gsm8k import run_eval as run_few_shot_gsm8k
url = urlparse(self.base_url)
args = SimpleNamespace(
num_shots=10,
data_path=None,
num_questions=self.num_gsm8k_questions,
max_new_tokens=2048,
parallel=self.max_running_requests,
host=f"http://{url.hostname}",
port=int(url.port),
)
metrics = run_few_shot_gsm8k(args)
print(
f"[{self.__class__.__name__}] GSM8K accuracy: {metrics['accuracy']:.3f} "
f"(threshold: {self.gsm8k_threshold})"
)
self.assertGreaterEqual(metrics["accuracy"], self.gsm8k_threshold)
@classmethod
def setUpClass(cls):
cls.model = QWEN3_30B_MODEL
cls.base_url = DEFAULT_URL_FOR_TEST
cls.process = popen_launch_server(
cls.model,
cls.base_url,
timeout=DEFAULT_TIMEOUT_FOR_SERVER_LAUNCH,
other_args=[
"--trust-remote-code",
"--tp-size",
"2",
"--pp-size",
"2",
"--mem-fraction-static",
"0.8",
"--cuda-graph-max-bs",
"32",
"--max-running-requests",
str(cls.max_running_requests),
"--max-total-tokens",
"14000",
"--disable-piecewise-cuda-graph",
"--model-loader-extra-config",
'{"enable_multithread_load": true, "num_threads": 64}',
"--enable-hierarchical-cache",
"--hicache-ratio",
"4",
"--hicache-write-policy",
"write_through",
"--hicache-io-backend",
cls.hicache_io_backend,
"--hicache-mem-layout",
cls.hicache_mem_layout,
],
env={"SGLANG_ENABLE_UNIFIED_RADIX_TREE": "1"},
)
cls.input_ids = get_input_ids(cls.model, num_samples=18)
@classmethod
def tearDownClass(cls):
kill_process_tree(cls.process.pid)
if __name__ == "__main__":
unittest.main()
@@ -2205,7 +2205,7 @@ class UnifiedRadixCacheSuite:
if storage_backend == "file":
import sglang.srt.managers.cache_controller as cache_controller
# The file-backend storage config records TP rank/size. These unit
# The file-backend storage config records TP/PP rank/size. These unit
# fixtures run without initializing distributed parallel state, so
# provide the local single-rank values that the fixture represents.
tp_rank_patcher = mock.patch.object(
@@ -2214,10 +2214,22 @@ class UnifiedRadixCacheSuite:
tp_size_patcher = mock.patch.object(
cache_controller, "get_tensor_model_parallel_world_size", return_value=1
)
pp_rank_patcher = mock.patch.object(
cache_controller, "get_pipeline_model_parallel_rank", return_value=0
)
pp_size_patcher = mock.patch.object(
cache_controller,
"get_pipeline_model_parallel_world_size",
return_value=1,
)
tp_rank_patcher.start()
tp_size_patcher.start()
pp_rank_patcher.start()
pp_size_patcher.start()
self.addCleanup(tp_rank_patcher.stop)
self.addCleanup(tp_size_patcher.stop)
self.addCleanup(pp_rank_patcher.stop)
self.addCleanup(pp_size_patcher.stop)
assert storage_dir is not None, "file backend needs a storage_dir"
# HiCacheFile reads the directory from this env var.