Stabilize CP HiCache residency under L1/L2 pressure
CP shared KV now keeps explicit L1 and host free-room targets so pressure is handled by planned eviction instead of repeated capacity-edge retries. The host allocator gains contiguous-preferred page reservation, L1 owner-lane allocation prefers contiguous physical pages, and CP HiCache metadata preserves pending backup safety for page-granular radix updates. Mooncake transfer stats and allocator microbenchmarks are included to make the remaining transfer bottlenecks measurable rather than inferred. Constraint: CP shared KV uses decode CP size 1 with all prefill CP ranks participating in transfer, so L1/L2 cache residency must remain page-granular and avoid extra collectives.\nConstraint: Production HiCache can be hundreds of GB, so allocator metadata overhead must be visible before enabling aggressive contiguous allocation broadly.\nRejected: Evict only the exact deficit | this keeps the cache at the cliff and causes repeated evict/allocate pressure.\nRejected: Rely on allocator scans alone for contiguity | remote microbenchmarks show fragmented 220GB-equivalent host metadata can make contiguous-preferred scans multi-ms.\nConfidence: medium\nScope-risk: moderate\nDirective: Do not increase L1/L2 free-room defaults or add new CP collectives without ETE evidence and transfer/allocator measurements.\nTested: python -m py_compile on touched runtime/test/benchmark files.\nTested: PYTHONPATH=. python -m pytest -q test/registered/unit/benchmark/test_cp_hicache_allocator_bench.py => 4 passed, 1 warning.\nTested: Remote g0034 log /mnt/beegfs/cjy/log/sglang_cp_hicache_20260601_233723.log shows active prefill process with L1/L2 free-room args, 702 HTTP 200 chat completions, 6272 prefill batches, and no fatal scheduler traceback in latest scan.\nTested: User-reported L1/L2 cache ETE validation passed on remote run.\nNot-tested: Full local pytest suite; local environment is missing several runtime dependencies.\nNot-tested: CUDA allocator microbenchmark during active production prefill process.\nNot-tested: Mooncake straggler fix; stats show transfer tail latency remains a separate bottleneck.
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
import torch
|
||||
|
||||
from benchmark.hicache.bench_cp_hicache_allocator_overhead import (
|
||||
StandaloneHostAllocator,
|
||||
_host_pages_from_gb,
|
||||
_make_host_free_slots,
|
||||
_parse_int_list,
|
||||
)
|
||||
|
||||
|
||||
def test_parse_int_list_accepts_commas_and_spaces():
|
||||
assert _parse_int_list("1, 2,8") == [1, 2, 8]
|
||||
|
||||
|
||||
def test_host_pages_from_gb_rounds_to_page_capacity():
|
||||
assert _host_pages_from_gb(220.0, bytes_per_token=100_000, page_size=64) == 34375
|
||||
|
||||
|
||||
def test_host_contiguous_preferred_skips_fragmented_prefix():
|
||||
page_size = 4
|
||||
free_slots = _make_host_free_slots(
|
||||
total_pages=12,
|
||||
request_pages=2,
|
||||
page_size=page_size,
|
||||
pattern="fragmented_prefix_later_run",
|
||||
seed=0,
|
||||
)
|
||||
allocator = StandaloneHostAllocator(page_size=page_size, free_slots=free_slots)
|
||||
|
||||
selected = allocator.alloc_contiguous_preferred(2 * page_size)
|
||||
|
||||
selected_pages = (selected.view(-1, page_size)[:, 0] // page_size).tolist()
|
||||
assert selected_pages[1] == selected_pages[0] + 1
|
||||
prefix_pages = (free_slots[: 2 * page_size].view(-1, page_size)[:, 0] // page_size)
|
||||
assert selected_pages != prefix_pages.tolist()
|
||||
|
||||
|
||||
def test_host_random_fragmented_has_requested_size():
|
||||
free_slots = _make_host_free_slots(
|
||||
total_pages=64,
|
||||
request_pages=8,
|
||||
page_size=16,
|
||||
pattern="random_fragmented",
|
||||
seed=123,
|
||||
)
|
||||
assert free_slots.numel() == 64 * 16
|
||||
assert torch.unique(free_slots).numel() == free_slots.numel()
|
||||
@@ -0,0 +1,63 @@
|
||||
import unittest
|
||||
from importlib import util
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
|
||||
from sglang.test.ci.ci_register import register_cpu_ci
|
||||
|
||||
|
||||
register_cpu_ci(est_time=1, suite="stage-a-test-cpu")
|
||||
|
||||
_UTILS_PATH = (
|
||||
Path(__file__).resolve().parents[4]
|
||||
/ "python"
|
||||
/ "sglang"
|
||||
/ "srt"
|
||||
/ "disaggregation"
|
||||
/ "common"
|
||||
/ "utils.py"
|
||||
)
|
||||
_spec = util.spec_from_file_location("disaggregation_common_utils_under_test", _UTILS_PATH)
|
||||
_utils = util.module_from_spec(_spec)
|
||||
assert _spec.loader is not None
|
||||
_spec.loader.exec_module(_utils)
|
||||
contiguous_group_stats = _utils.contiguous_group_stats
|
||||
|
||||
|
||||
class TestDisaggregationCommonUtils(unittest.TestCase):
|
||||
def test_contiguous_group_stats_reports_fragmentation_shape(self):
|
||||
src_indices = np.array([1, 2, 3, 10, 18, 19], dtype=np.int32)
|
||||
dst_indices = np.array([101, 102, 103, 110, 118, 119], dtype=np.int32)
|
||||
src_groups = [[1, 2, 3], [10], [18, 19]]
|
||||
dst_groups = [[101, 102, 103], [110], [118, 119]]
|
||||
|
||||
stats = contiguous_group_stats(
|
||||
src_indices, dst_indices, src_groups, dst_groups
|
||||
)
|
||||
|
||||
self.assertEqual(stats["pages"], 6)
|
||||
self.assertEqual(stats["groups"], 3)
|
||||
self.assertEqual(stats["min_group_pages"], 1)
|
||||
self.assertEqual(stats["max_group_pages"], 3)
|
||||
self.assertAlmostEqual(stats["avg_group_pages"], 2.0)
|
||||
self.assertEqual(stats["src_diff_head"], [1, 1, 7, 8, 1])
|
||||
self.assertEqual(stats["dst_diff_head"], [1, 1, 7, 8, 1])
|
||||
|
||||
def test_contiguous_group_stats_handles_empty_transfer(self):
|
||||
stats = contiguous_group_stats(
|
||||
np.array([], dtype=np.int32),
|
||||
np.array([], dtype=np.int32),
|
||||
[],
|
||||
[],
|
||||
)
|
||||
|
||||
self.assertEqual(stats["pages"], 0)
|
||||
self.assertEqual(stats["groups"], 0)
|
||||
self.assertEqual(stats["avg_group_pages"], 0.0)
|
||||
self.assertEqual(stats["src_diff_head"], [])
|
||||
self.assertEqual(stats["dst_diff_head"], [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -109,6 +109,7 @@ from sglang.srt.managers.cache_controller import HiCacheController
|
||||
from sglang.srt.mem_cache.cp_shared_kv_layout import CpSharedKVLayout
|
||||
from sglang.srt.mem_cache.hiradix_cache import CpHiCacheNodeMetadata
|
||||
from sglang.srt.mem_cache.memory_pool_host import (
|
||||
HostKVCache,
|
||||
MHATokenToKVPoolHost,
|
||||
MLATokenToKVPoolHost,
|
||||
NSATokenToKVPoolHost,
|
||||
@@ -161,6 +162,50 @@ class FakeHostPool:
|
||||
return len(indices)
|
||||
|
||||
|
||||
class ContiguousPreferredHostPool(FakeHostPool):
|
||||
def __init__(self, alloc_result):
|
||||
super().__init__(alloc_result)
|
||||
self.contiguous_alloc_calls = []
|
||||
|
||||
def alloc_contiguous_preferred(self, need_size):
|
||||
self.contiguous_alloc_calls.append(need_size)
|
||||
if self.alloc_result is None:
|
||||
return None
|
||||
return self.alloc_result[:need_size].clone()
|
||||
|
||||
|
||||
class DummyHostKVCacheForAlloc(HostKVCache):
|
||||
def get_size_per_token(self):
|
||||
return 1
|
||||
|
||||
def init_kv_buffer(self):
|
||||
return None
|
||||
|
||||
def load_to_device_per_layer(
|
||||
self, device_pool, host_indices, device_indices, layer_id, io_backend
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def backup_from_device_per_layer(
|
||||
self, device_pool, host_indices, device_indices, layer_id, io_backend
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def backup_from_device_all_layer(
|
||||
self, device_pool, host_indices, device_indices, io_backend
|
||||
) -> None:
|
||||
pass
|
||||
|
||||
def get_data_page(self, index, flat: bool = True) -> torch.Tensor:
|
||||
return torch.empty((0,), dtype=torch.uint8)
|
||||
|
||||
def get_dummy_flat_data_page(self) -> torch.Tensor:
|
||||
return torch.empty((0,), dtype=torch.uint8)
|
||||
|
||||
def set_from_flat_data_page(self, index: int, data_page: torch.Tensor) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class FakeDevicePool:
|
||||
device = "cpu"
|
||||
layer_num = 1
|
||||
@@ -931,6 +976,46 @@ class TestHiCacheControllerCPWrite(CustomTestCase):
|
||||
self.assertEqual(controller.ack_write_queue[0].node_ids, [79])
|
||||
self.assertIn("all-layer backup fallback", "\n".join(logs.output))
|
||||
|
||||
def test_cp_reserve_write_uses_contiguous_preferred_host_alloc(self):
|
||||
host_pool = ContiguousPreferredHostPool(
|
||||
torch.tensor([100, 101, 102, 103], dtype=torch.int64)
|
||||
)
|
||||
draft_host_pool = ContiguousPreferredHostPool(
|
||||
torch.tensor([200, 201, 202, 203], dtype=torch.int64)
|
||||
)
|
||||
controller = self.make_controller(
|
||||
host_pool,
|
||||
cp_rank=1,
|
||||
draft_host_pool=draft_host_pool,
|
||||
draft_mem_pool_device=FakeDevicePool("draft"),
|
||||
)
|
||||
logical_locs = torch.arange(4, 20, dtype=torch.int64)
|
||||
|
||||
reservation = controller.reserve_write_cp(logical_locs, node_id=179)
|
||||
|
||||
self.assertEqual(reservation.metadata.host_indices.tolist(), [100, 101, 102, 103])
|
||||
self.assertEqual(
|
||||
reservation.metadata.draft_host_indices.tolist(), [200, 201, 202, 203]
|
||||
)
|
||||
self.assertEqual(host_pool.contiguous_alloc_calls, [4])
|
||||
self.assertEqual(draft_host_pool.contiguous_alloc_calls, [4])
|
||||
self.assertEqual(host_pool.alloc_calls, [])
|
||||
self.assertEqual(draft_host_pool.alloc_calls, [])
|
||||
|
||||
def test_host_alloc_contiguous_preferred_skips_fragmented_fifo_prefix(self):
|
||||
host_pool = DummyHostKVCacheForAlloc.__new__(DummyHostKVCacheForAlloc)
|
||||
host_pool.page_size = 4
|
||||
host_pool.lock = __import__("threading").RLock()
|
||||
host_pool.free_slots = torch.tensor(
|
||||
[100, 101, 102, 103, 8, 9, 10, 11, 12, 13, 14, 15],
|
||||
dtype=torch.int64,
|
||||
)
|
||||
|
||||
selected = host_pool.alloc_contiguous_preferred(8)
|
||||
|
||||
self.assertEqual(selected.tolist(), [8, 9, 10, 11, 12, 13, 14, 15])
|
||||
self.assertEqual(host_pool.free_slots.tolist(), [100, 101, 102, 103])
|
||||
|
||||
def test_cp_reserve_zero_owned_queues_no_ack_until_submit(self):
|
||||
host_pool = FakeHostPool(torch.tensor([], dtype=torch.int64))
|
||||
controller = self.make_controller(host_pool, cp_rank=3)
|
||||
|
||||
@@ -107,6 +107,8 @@ from sglang.srt.mem_cache.base_prefix_cache import (
|
||||
MatchPrefixParams,
|
||||
)
|
||||
from sglang.srt.mem_cache.hiradix_cache import (
|
||||
CpHiCacheCapacitySnapshot,
|
||||
CpHiCacheEvictionPlan,
|
||||
CpHiCacheNodeMetadata,
|
||||
HiRadixCache,
|
||||
PendingHiCacheBackup,
|
||||
@@ -605,6 +607,132 @@ class TestCpHiCacheNodeMetadata(CustomTestCase):
|
||||
self.assertEqual(child.page_owners.numel(), 0)
|
||||
|
||||
|
||||
class TestCpHiCacheFreeRoom(CustomTestCase):
|
||||
def test_free_room_deficit_does_not_evict_when_required_fits_trigger(self):
|
||||
deficit = hiradix_cache._free_room_deficit(
|
||||
required=64,
|
||||
available=96,
|
||||
capacity=256,
|
||||
page_size=64,
|
||||
target_ratio=0.5,
|
||||
trigger_ratio=0.0,
|
||||
)
|
||||
|
||||
self.assertEqual(deficit, 0)
|
||||
|
||||
def test_free_room_deficit_evicts_to_target_after_trigger(self):
|
||||
deficit = hiradix_cache._free_room_deficit(
|
||||
required=64,
|
||||
available=96,
|
||||
capacity=256,
|
||||
page_size=64,
|
||||
target_ratio=0.5,
|
||||
trigger_ratio=0.25,
|
||||
)
|
||||
|
||||
self.assertEqual(deficit, 96)
|
||||
|
||||
def test_free_room_deficit_rounds_room_to_page(self):
|
||||
deficit = hiradix_cache._free_room_deficit(
|
||||
required=64,
|
||||
available=0,
|
||||
capacity=100,
|
||||
page_size=64,
|
||||
target_ratio=0.01,
|
||||
trigger_ratio=0.0,
|
||||
)
|
||||
|
||||
self.assertEqual(deficit, 128)
|
||||
|
||||
def test_cp_host_write_admission_uses_trigger_target_room(self):
|
||||
cache = HiRadixCache.__new__(HiRadixCache)
|
||||
cache.page_size = 64
|
||||
cache.hicache_host_free_room_ratio = 0.5
|
||||
cache.hicache_host_free_room_trigger_ratio = 0.25
|
||||
cache._cp_required_host_tokens_by_rank = lambda _indices: (64, 0)
|
||||
cache._cp_host_capacity_snapshot = lambda: CpHiCacheCapacitySnapshot(
|
||||
target_capacity=(256, 256),
|
||||
draft_capacity=None,
|
||||
committed_target=(160, 0),
|
||||
committed_draft=(0, 0),
|
||||
pending_target=(0, 0),
|
||||
pending_draft=(0, 0),
|
||||
)
|
||||
cache._plan_cp_host_evictions = lambda deficit: CpHiCacheEvictionPlan(
|
||||
victims=(),
|
||||
planned_freed=tuple(0 for _ in deficit),
|
||||
remaining_deficit=tuple(0 for _ in deficit),
|
||||
)
|
||||
|
||||
admission = cache._cp_build_write_admission(
|
||||
torch.arange(64, dtype=torch.int64),
|
||||
node_id=600,
|
||||
phase="unit",
|
||||
)
|
||||
|
||||
self.assertEqual(admission.target_available_by_owner, (96, 256))
|
||||
self.assertEqual(admission.deficit_by_owner, (96, 0))
|
||||
|
||||
def test_cp_host_write_admission_does_not_evict_when_trigger_room_fits(self):
|
||||
cache = HiRadixCache.__new__(HiRadixCache)
|
||||
cache.page_size = 64
|
||||
cache.hicache_host_free_room_ratio = 0.5
|
||||
cache.hicache_host_free_room_trigger_ratio = 0.0
|
||||
cache._cp_required_host_tokens_by_rank = lambda _indices: (64, 0)
|
||||
cache._cp_host_capacity_snapshot = lambda: CpHiCacheCapacitySnapshot(
|
||||
target_capacity=(256, 256),
|
||||
draft_capacity=None,
|
||||
committed_target=(160, 0),
|
||||
committed_draft=(0, 0),
|
||||
pending_target=(0, 0),
|
||||
pending_draft=(0, 0),
|
||||
)
|
||||
cache._plan_cp_host_evictions = lambda deficit: CpHiCacheEvictionPlan(
|
||||
victims=(),
|
||||
planned_freed=tuple(0 for _ in deficit),
|
||||
remaining_deficit=tuple(0 for _ in deficit),
|
||||
)
|
||||
|
||||
admission = cache._cp_build_write_admission(
|
||||
torch.arange(64, dtype=torch.int64),
|
||||
node_id=601,
|
||||
phase="unit",
|
||||
)
|
||||
|
||||
self.assertEqual(admission.target_available_by_owner, (96, 256))
|
||||
self.assertEqual(admission.deficit_by_owner, (0, 0))
|
||||
|
||||
def test_cp_host_write_admission_uses_draft_room_deficit(self):
|
||||
cache = HiRadixCache.__new__(HiRadixCache)
|
||||
cache.page_size = 64
|
||||
cache.hicache_host_free_room_ratio = 0.25
|
||||
cache.hicache_host_free_room_trigger_ratio = 0.25
|
||||
cache._cp_required_host_tokens_by_rank = lambda _indices: (64, 0)
|
||||
cache._cp_host_capacity_snapshot = lambda: CpHiCacheCapacitySnapshot(
|
||||
target_capacity=(256, 256),
|
||||
draft_capacity=(256, 256),
|
||||
committed_target=(0, 0),
|
||||
committed_draft=(192, 0),
|
||||
pending_target=(0, 0),
|
||||
pending_draft=(0, 0),
|
||||
)
|
||||
cache._plan_cp_host_evictions = lambda deficit: CpHiCacheEvictionPlan(
|
||||
victims=(),
|
||||
planned_freed=tuple(0 for _ in deficit),
|
||||
remaining_deficit=tuple(0 for _ in deficit),
|
||||
)
|
||||
|
||||
admission = cache._cp_build_write_admission(
|
||||
torch.arange(64, dtype=torch.int64),
|
||||
node_id=602,
|
||||
phase="unit",
|
||||
)
|
||||
|
||||
self.assertEqual(admission.target_available_by_owner, (256, 256))
|
||||
self.assertEqual(admission.draft_available_by_owner, (64, 256))
|
||||
self.assertEqual(admission.deficit_by_owner, (64, 0))
|
||||
|
||||
|
||||
class FakeWriteFailure:
|
||||
metadata = None
|
||||
|
||||
@@ -2592,6 +2720,39 @@ class TestHiRadixCacheCPSplitEvict(CustomTestCase):
|
||||
|
||||
|
||||
class TestHiRadixCacheCPLoadBack(CustomTestCase):
|
||||
def test_cp_load_back_plan_uses_l1_free_room_target(self):
|
||||
class FreeRoomAllocator:
|
||||
def compute_owner_lane_stats(self, _page_owners):
|
||||
return [1, 0], [0, 8], [1, 0]
|
||||
|
||||
def compute_owner_lane_capacity_pages(self):
|
||||
return [8, 8]
|
||||
|
||||
cache = HiRadixCache.__new__(HiRadixCache)
|
||||
cache._uses_cp_hicache = True
|
||||
cache.page_size = 64
|
||||
cache.hicache_l1_free_room_ratio = 0.5
|
||||
cache.hicache_l1_free_room_trigger_ratio = 0.25
|
||||
cache.token_to_kv_pool_allocator = FreeRoomAllocator()
|
||||
|
||||
node = TreeNode(id=701)
|
||||
node.host_len = 64
|
||||
node.cp_hicache = CpHiCacheNodeMetadata(
|
||||
logical_len=64,
|
||||
padded_len=64,
|
||||
owned_positions=torch.arange(64, dtype=torch.int64),
|
||||
host_indices=torch.arange(64, dtype=torch.int64),
|
||||
page_owners=torch.tensor([0], dtype=torch.int8),
|
||||
page_size=64,
|
||||
)
|
||||
|
||||
plan = cache._build_cp_load_back_plan([node], node_id=701)
|
||||
|
||||
self.assertEqual(plan.required_by_owner, [1, 0])
|
||||
self.assertEqual(plan.available_by_owner, [0, 8])
|
||||
# required=1 page, available=0, target_room=ceil(8*0.5)=4 pages.
|
||||
self.assertEqual(plan.deficit_by_owner, [5, 0])
|
||||
|
||||
def test_cp_load_back_uses_host_len_not_host_value(self):
|
||||
cache = HiRadixCache.__new__(HiRadixCache)
|
||||
cache._uses_cp_hicache = True
|
||||
|
||||
@@ -341,7 +341,7 @@ class TestCPSharedPagedAllocator(CustomTestCase):
|
||||
|
||||
self.assertIsNotNone(locs)
|
||||
logical_pages = locs.view(-1, page_size)[:, 0] // page_size
|
||||
self.assertEqual(logical_pages.tolist(), [9, 3, 1, 7, 4])
|
||||
self.assertEqual(logical_pages.tolist(), [1, 3, 5, 7, 4])
|
||||
self.assertEqual(
|
||||
((logical_pages - 1) % cp_size).tolist(),
|
||||
page_compute_owners,
|
||||
@@ -392,6 +392,40 @@ class TestCPSharedPagedAllocator(CustomTestCase):
|
||||
self.assertEqual(allocator.free_pages.tolist(), [2, 3, 4])
|
||||
self.assertEqual(allocator.release_pages.tolist(), [])
|
||||
|
||||
def test_contiguous_owner_lane_selection_prefers_later_physical_run(self):
|
||||
from sglang.srt.mem_cache.allocator import CPSharedPagedTokenToKVPoolAllocator
|
||||
|
||||
page_size = 64
|
||||
cp_size = 4
|
||||
allocator = CPSharedPagedTokenToKVPoolAllocator(
|
||||
logical_size=page_size * 32,
|
||||
physical_size=page_size * 8,
|
||||
page_size=page_size,
|
||||
dtype=torch.bfloat16,
|
||||
device="cpu",
|
||||
kvcache=None,
|
||||
need_sort=False,
|
||||
cp_size=cp_size,
|
||||
cp_rank=0,
|
||||
)
|
||||
allocator.free_pages = torch.tensor(
|
||||
[1, 9, 13, 17]
|
||||
+ [page for page in range(2, 33) if page not in {9, 13, 17}],
|
||||
dtype=torch.int64,
|
||||
)
|
||||
|
||||
locs = allocator.alloc_pages_with_owners([0, 0, 0])
|
||||
|
||||
self.assertIsNotNone(locs)
|
||||
logical_pages = locs.view(-1, page_size)[:, 0] // page_size
|
||||
self.assertEqual(logical_pages.tolist(), [9, 13, 17])
|
||||
self.assertEqual(
|
||||
((logical_pages - 1) % cp_size).tolist(),
|
||||
[0, 0, 0],
|
||||
)
|
||||
for selected_page in logical_pages.tolist():
|
||||
self.assertNotIn(selected_page, allocator.free_pages.tolist())
|
||||
|
||||
def test_compute_owner_alloc_does_not_evict_lanes_when_first_try_succeeds(self):
|
||||
from types import SimpleNamespace
|
||||
|
||||
@@ -663,6 +697,54 @@ class TestCPSharedPagedAllocator(CustomTestCase):
|
||||
)
|
||||
self.assertIsNotNone(locs)
|
||||
|
||||
def test_compute_owner_lane_eviction_uses_l1_free_room_target(self):
|
||||
from sglang.srt.mem_cache.base_prefix_cache import EvictResult
|
||||
from sglang.srt.mem_cache.common import _evict_for_compute_owner_lanes
|
||||
|
||||
page_size = 64
|
||||
|
||||
class FakeAllocator:
|
||||
cp_size = 2
|
||||
|
||||
def __init__(self):
|
||||
self.page_size = page_size
|
||||
|
||||
def available_size(self):
|
||||
return 0
|
||||
|
||||
def compute_owner_lane_stats(self, _page_compute_owners):
|
||||
return [1, 0], [0, 8], [1, 0]
|
||||
|
||||
def compute_owner_lane_capacity_pages(self):
|
||||
return [8, 8]
|
||||
|
||||
class FakeTreeCache:
|
||||
hicache_l1_free_room_ratio = 0.5
|
||||
hicache_l1_free_room_trigger_ratio = 0.25
|
||||
|
||||
def __init__(self):
|
||||
self.owner_deficits = []
|
||||
|
||||
def is_chunk_cache(self):
|
||||
return False
|
||||
|
||||
def evictable_size(self):
|
||||
return page_size * 8
|
||||
|
||||
def evict(self, params):
|
||||
self.owner_deficits.append(list(params.owner_lane_deficits))
|
||||
return EvictResult(num_tokens_evicted=0)
|
||||
|
||||
tree_cache = FakeTreeCache()
|
||||
_evict_for_compute_owner_lanes(
|
||||
tree_cache=tree_cache,
|
||||
allocator=FakeAllocator(),
|
||||
page_compute_owners=[0],
|
||||
)
|
||||
|
||||
# required=1 page, available=0, target_room=ceil(8*0.5)=4 pages.
|
||||
self.assertEqual(tree_cache.owner_deficits[0], [5, 0])
|
||||
|
||||
def test_compute_owner_capacity_wait_reports_owner_lane_deficits(self):
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
Reference in New Issue
Block a user