Files
hao-ai-lab-FastVideo/v2/memory/allocator.py
T
SolitaryThinker 145082cf74 [refactor] rename package mini_fastvideo → v2
Directory rename (git mv, history preserved) plus rewrite of all references: absolute imports in
tests, the zero-dep runner, docstrings, comments, and the README. No behavior change.

Run: python3 -m pytest v2/tests/ -q ; python3 v2/run_tests.py ; python3 -m v2.examples
2026-06-18 09:41:12 +00:00

82 lines
2.6 KiB
Python

"""Tagged memory pools with reservation-before-admission (design_v3 §6.2, §7.3).
> Reservation must be pre-flight. A scheduler that admits a diffusion step and then
> discovers it cannot allocate the VAE tile is wrong.
Sleep/wake is component-granular (tags are component names) for RL: drop DiT + caches,
keep VAE/text-encoder resident (design_v3 §7.3; CuMem-style).
"""
from __future__ import annotations
import itertools
from dataclasses import dataclass, field
class OutOfMemory(Exception):
pass
_res_ctr = itertools.count(1)
@dataclass
class Reservation:
res_id: int
tag: str
nbytes: int
active: bool = True
class MemoryManager:
def __init__(self, total_bytes: int = 1 << 40, per_tag_budget: dict[str, int] | None = None):
self.total_bytes = total_bytes
self.per_tag_budget = dict(per_tag_budget or {})
self.reserved = 0
self._by_tag: dict[str, int] = {}
self._reservations: dict[int, Reservation] = {}
self._asleep: set[str] = set()
@property
def available(self) -> int:
return self.total_bytes - self.reserved
def can_reserve(self, tag: str, nbytes: int) -> bool:
if nbytes > self.available:
return False
budget = self.per_tag_budget.get(tag)
if budget is not None and self._by_tag.get(tag, 0) + nbytes > budget:
return False
return True
def reserve(self, tag: str, nbytes: int) -> Reservation:
if not self.can_reserve(tag, nbytes):
raise OutOfMemory(f"cannot reserve {nbytes} bytes for tag {tag!r} "
f"(available={self.available}, used={self._by_tag.get(tag, 0)})")
res = Reservation(next(_res_ctr), tag, nbytes)
self._reservations[res.res_id] = res
self.reserved += nbytes
self._by_tag[tag] = self._by_tag.get(tag, 0) + nbytes
return res
def release(self, res: Reservation) -> None:
if not res.active:
return
res.active = False
self._reservations.pop(res.res_id, None)
self.reserved -= res.nbytes
self._by_tag[res.tag] = max(0, self._by_tag.get(res.tag, 0) - res.nbytes)
# component-granular sleep/wake (tags = component names) ------------------- #
def sleep(self, tags: list[str]) -> int:
freed = 0
for tag in tags:
self._asleep.add(tag)
for res in [r for r in self._reservations.values() if r.tag == tag]:
freed += res.nbytes
self.release(res)
return freed
def wake(self, tags: list[str]) -> None:
for tag in tags:
self._asleep.discard(tag)