Model CP scheduler admission with cache-hit pressure
Add an offline benchmark that reuses PrefillAdder to model how L1 cached tokens, L2 HiCache hits, and extend tokens shape CP shared-KV prefill batch admission. The tool makes scheduler stop reasons and fake L2 load-back capacity pressure observable without starting a model. Constraint: The benchmark must stay CPU/offline and avoid depending on CUDA execution or live services. Constraint: L2 cached tokens are modeled as host_hit_length, so successful load-back both increases prefix_len and consumes fake L1 capacity. Rejected: Build an ETE benchmark first | too slow for isolating scheduler admission behavior. Rejected: Reimplement scheduler logic from scratch | would drift from PrefillAdder semantics. Confidence: high Scope-risk: narrow Directive: Treat duration_us as Python admission overhead only; it is not an ETE latency metric. Tested: Remote pytest test/registered/unit/managers/test_prefill_scheduler_admission_bench.py: 4 passed as part of 6 targeted tests. Tested: Remote synthetic benchmark run with --cp-max-total-cached-tokens showed second 4096-token cached request stopped with OTHER. Not-tested: Real traffic trace import from production logs.
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
import importlib.util
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[4]
|
||||
sys.path.insert(0, str(_REPO_ROOT / "python"))
|
||||
|
||||
|
||||
_BENCH_PATH = (
|
||||
_REPO_ROOT
|
||||
/ "benchmark"
|
||||
/ "hicache"
|
||||
/ "bench_prefill_scheduler_admission.py"
|
||||
)
|
||||
|
||||
|
||||
def _load_bench_module():
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
"bench_prefill_scheduler_admission", _BENCH_PATH
|
||||
)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
assert spec.loader is not None
|
||||
sys.modules[spec.name] = module
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
class TestPrefillSchedulerAdmissionBench(unittest.TestCase):
|
||||
def test_l1_l2_and_extend_tokens_are_reported_with_real_prefill_semantics(self):
|
||||
bench = _load_bench_module()
|
||||
cfg = bench.SchedulerBenchConfig(
|
||||
page_size=64,
|
||||
available_tokens=10000,
|
||||
evictable_tokens=0,
|
||||
max_prefill_tokens=1024,
|
||||
enable_cp_shared_kv_prefill_bs_gt1=True,
|
||||
cp_shared_kv_prefill_max_batch_requests=8,
|
||||
cp_shared_kv_prefill_max_total_extend_tokens=4096,
|
||||
)
|
||||
trace = bench.run_scheduler_admission_trace(
|
||||
[
|
||||
bench.RequestSpec(
|
||||
rid="hit-l1-l2",
|
||||
l1_cached_tokens=128,
|
||||
l2_cached_tokens=128,
|
||||
extend_tokens=64,
|
||||
max_new_tokens=1,
|
||||
)
|
||||
],
|
||||
cfg,
|
||||
)
|
||||
|
||||
self.assertEqual(len(trace.ticks), 1)
|
||||
tick = trace.ticks[0]
|
||||
self.assertEqual([req.rid for req in tick.accepted], ["hit-l1-l2"])
|
||||
accepted = tick.accepted[0]
|
||||
self.assertEqual(accepted.initial_extend_tokens, 192)
|
||||
self.assertEqual(accepted.effective_extend_tokens, 64)
|
||||
self.assertEqual(accepted.l1_cached_tokens, 128)
|
||||
self.assertEqual(accepted.l2_cached_tokens, 128)
|
||||
self.assertEqual(accepted.loaded_l2_tokens, 128)
|
||||
self.assertEqual(tick.log_hit_tokens, 256)
|
||||
self.assertEqual(tick.log_input_tokens, 64)
|
||||
|
||||
def test_cp_total_extend_limit_controls_batching_not_generic_max_prefill_tokens(self):
|
||||
bench = _load_bench_module()
|
||||
cfg = bench.SchedulerBenchConfig(
|
||||
page_size=64,
|
||||
available_tokens=10000,
|
||||
evictable_tokens=0,
|
||||
max_prefill_tokens=192,
|
||||
enable_cp_shared_kv_prefill_bs_gt1=True,
|
||||
cp_shared_kv_prefill_max_batch_requests=8,
|
||||
cp_shared_kv_prefill_max_total_extend_tokens=256,
|
||||
)
|
||||
trace = bench.run_scheduler_admission_trace(
|
||||
[
|
||||
bench.RequestSpec("a", 0, 0, 128, max_new_tokens=1),
|
||||
bench.RequestSpec("b", 0, 0, 128, max_new_tokens=1),
|
||||
],
|
||||
cfg,
|
||||
)
|
||||
|
||||
self.assertEqual(len(trace.ticks), 1)
|
||||
self.assertEqual([req.rid for req in trace.ticks[0].accepted], ["a", "b"])
|
||||
self.assertEqual(trace.ticks[0].cp_total_extend_tokens, 256)
|
||||
|
||||
def test_l2_load_back_consumes_l1_capacity_and_can_stop_later_requests(self):
|
||||
bench = _load_bench_module()
|
||||
cfg = bench.SchedulerBenchConfig(
|
||||
page_size=64,
|
||||
available_tokens=320,
|
||||
evictable_tokens=0,
|
||||
max_prefill_tokens=4096,
|
||||
enable_cp_shared_kv_prefill_bs_gt1=True,
|
||||
cp_shared_kv_prefill_max_batch_requests=8,
|
||||
cp_shared_kv_prefill_max_total_extend_tokens=4096,
|
||||
)
|
||||
trace = bench.run_scheduler_admission_trace(
|
||||
[
|
||||
bench.RequestSpec("l2-heavy", 0, 128, 64, max_new_tokens=1),
|
||||
bench.RequestSpec("next", 0, 0, 64, max_new_tokens=1),
|
||||
],
|
||||
cfg,
|
||||
)
|
||||
|
||||
self.assertEqual([req.rid for req in trace.ticks[0].accepted], ["l2-heavy"])
|
||||
self.assertEqual(trace.ticks[0].stopped_on_rid, "next")
|
||||
self.assertEqual(trace.ticks[0].stopped_result, "NO_TOKEN")
|
||||
self.assertEqual(trace.ticks[0].allocator_available_after_tick, 192)
|
||||
self.assertEqual(trace.ticks[0].load_back_events[0].loaded_tokens, 128)
|
||||
|
||||
def test_total_cached_limit_is_observable_in_scheduler_trace(self):
|
||||
bench = _load_bench_module()
|
||||
cfg = bench.SchedulerBenchConfig(
|
||||
page_size=64,
|
||||
available_tokens=10000,
|
||||
evictable_tokens=0,
|
||||
max_prefill_tokens=4096,
|
||||
enable_cp_shared_kv_prefill_bs_gt1=True,
|
||||
cp_shared_kv_prefill_max_batch_requests=8,
|
||||
cp_shared_kv_prefill_max_total_extend_tokens=4096,
|
||||
cp_shared_kv_prefill_max_total_cached_tokens=4096,
|
||||
)
|
||||
trace = bench.run_scheduler_admission_trace(
|
||||
[
|
||||
bench.RequestSpec("a", 4096, 0, 64, max_new_tokens=1),
|
||||
bench.RequestSpec("b", 4096, 0, 64, max_new_tokens=1),
|
||||
],
|
||||
cfg,
|
||||
)
|
||||
|
||||
self.assertEqual([req.rid for req in trace.ticks[0].accepted], ["a"])
|
||||
self.assertEqual(trace.ticks[0].stopped_on_rid, "b")
|
||||
self.assertEqual(trace.ticks[0].stopped_result, "OTHER")
|
||||
self.assertEqual(trace.ticks[0].cp_total_cached_tokens, 4096)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user