[Model] Cap derived cache budget and account for stager detach

Address review on #6629:

- Cap the derived retention budget at a quarter of device capacity so a
  large staged total cannot monopolize a small device; the multiplier
  itself stays pending an instrumented target run.
- Log every release decision (reason, cached bytes, effective budget and
  cap, staged total, free/total memory) and every staging OOM retry with
  its staged byte total, so a single target run validates the headroom
  multiplier directly.
- Make stager detach symmetric: set_cache_retention now shrinks the
  previous cache's budget before growing the new one, so cache -> None ->
  same cache counts the staged bytes exactly once (regression test added).

Signed-off-by: hsliuustc0106 <liuhongsheng4@huawei.com>
This commit is contained in:
hsliuustc0106 committed 2026-08-26 11:12:09 +08:00
1 parent ba5943dddb
commit c5295f3ffe
3 files changed
+97 -9

No files matched your search

@@ -237,12 +237,53 @@ def test_allocator_cache_budget_is_derived_from_attached_stager_bytes(patched_ru
stager.set_cache_retention(retention)
assert retention.budget_bytes == 2 * expected_bytes
# Detaching and re-attaching the same cache also counts the bytes once.
stager.set_cache_retention(None)
assert retention.budget_bytes == 0
stager.set_cache_retention(retention)
assert retention.budget_bytes == 2 * expected_bytes
# A second stager grows the budget by its own staged bytes.
extra = PinnedModuleStager(torch.nn.Linear(2, 2), torch.device("cpu"), pin_memory=False)
extra.set_cache_retention(retention)
assert retention.budget_bytes == 2 * (expected_bytes + extra.staged_bytes)
@pytest.mark.parametrize(
("reserved", "allocated", "free", "expected_release"),
[
# Derived budget 2 * 1000 = 2000 uncapped, but capacity cap = 25 and
# cached = 30 exceeds it, so the cap must trigger the release.
(40, 10, 80, True),
# Within the cap (cached = 20 <= 25) and the free floor holds.
(30, 10, 80, False),
],
)
def test_allocator_cache_budget_is_capped_by_device_capacity(
monkeypatch,
patched_runtime,
reserved,
allocated,
free,
expected_release,
):
empty_cache, _ = patched_runtime
monkeypatch.setattr(torch.accelerator, "memory_reserved", lambda _device: reserved)
monkeypatch.setattr(torch.accelerator, "memory_allocated", lambda _device: allocated)
monkeypatch.setattr(
residency_module.current_omni_platform,
"get_device_memory",
lambda _device: (free, 100),
)
retention = BoundedAllocatorCache(torch.device("cpu"))
retention.grow_retention_budget(1000)
released = retention.release_if_needed()
assert released is expected_release
assert empty_cache.call_count == int(expected_release)
def test_allocator_cache_releases_when_telemetry_is_unavailable(monkeypatch, patched_runtime):
empty_cache, _ = patched_runtime