Files
hao-ai-lab-FastVideo/v2/runtime/cache/manager.py
T
Will Lin 1fe50c0092 [refactor] v2: group flat top-level into planes; isolate vendored under _vendor/
The v2 top level had grown to ~27 dirs + 13 loose files — half of them
single-concept abstraction shells (memory/ 86 LOC, transport/ 222, parity/ 178,
extend/ 226) and half vendored fastvideo code sitting as peers to the actual v2
design. Regroup to mirror README section 3 "Planes & dependency order":

  core/     enums+types, card, loop, program, parity, request, parallel
            (the model-native contracts; no kernels)
  runtime/  + folded-in substrate: cache, memory, transport, extend
            (the import graph shows only runtime consumes them; compile/cudagraph
            already lived here)
  serving/  + deploy/ (products / fleet)
  _vendor/  all copied fastvideo: models, layers, attention, configs, distributed,
            platforms, api, hooks, logging_utils, third_party + fastvideo_args/
            utils/logger/envs/forward_context — internal layout unchanged, still
            mirrors upstream for diffing

28 dirs + 13 root files -> 8 dirs + 6 root files. Pure mechanical move: 947
absolute-import paths rewritten (v2.X -> v2.{core,runtime,serving,_vendor}.X),
boundary-anchored so platform/ (native dispatch) and platforms/ (vendored CUDA
detect) no longer collide and neither does hooks/ vs extend/. Deleted the empty
v2/loader/. README section 3 + 16 updated; stale test count corrected
(34 files/216 tests -> 22 files/143 tests).

Validated: `import v2` stays torch-free; `v2/run_tests.py` and `pytest v2/tests/`
-> 143 passed, 2 skipped on the numpy toy backend. (On a GPU box force the toy
backend with CUDA_VISIBLE_DEVICES="" or detect() picks cuda.) No external importer
changed — examples use the re-exported `from v2 import VideoGenerator`.
2026-07-04 17:15:34 +00:00

76 lines
2.8 KiB
Python

"""CacheManager — per-class pools with static budgets, behind one handle.
Static partitioning makes cross-class fragmentation impossible (jumbo slab traffic cannot
strand text-KV pages and vice versa). Each class gets a budget carved at init from the card's
CacheContracts. ``invalidate_weights`` implements the wholesale RL weight-epoch bump.
"""
from __future__ import annotations
from typing import Any
from v2.runtime.cache.classes import _Pool, make_pool
from v2.runtime.cache.keys import CachePolicy
class CacheManager:
def __init__(self, policies: list[CachePolicy] | None = None):
self._pools: dict[str, _Pool] = {}
for p in (policies or []):
self._pools[p.class_name] = make_pool(p)
@classmethod
def from_card(cls, card) -> CacheManager:
"""Build the per-class pools a card declares (KV pools materialize only if declared)."""
policies = []
for cc in card.caches.values():
policies.append(
CachePolicy(
class_name=cc.cache_class,
max_bytes=cc.max_bytes,
block_bytes=cc.block_bytes,
eviction=cc.eviction,
reuse_across_requests=cc.reuse_across_requests,
per_component=dict(cc.per_component),
training_mode_disables_recycle=cc.training_mode_disables_recycle,
))
return cls(policies)
def pool(self, class_name: str) -> _Pool:
if class_name not in self._pools:
raise KeyError(f"no cache pool for class {class_name!r}; card did not declare it")
return self._pools[class_name]
def has(self, class_name: str) -> bool:
return class_name in self._pools
def invalidate_weights(self, version: str) -> None:
for pool in self._pools.values():
if hasattr(pool, "invalidate_weights"):
pool.invalidate_weights(version)
def invalidate_components(self, components) -> None:
"""Component-scoped invalidation: only drop caches for changed components."""
comps = set(components)
for pool in self._pools.values():
if hasattr(pool, "invalidate_components"):
pool.invalidate_components(comps)
def clear_namespace(self, namespace: str) -> None:
"""Release a finished request's per-request caches (residual/slab)."""
for pool in self._pools.values():
if hasattr(pool, "clear_namespace"):
pool.clear_namespace(namespace)
if hasattr(pool, "free_namespace"):
pool.free_namespace(namespace)
def stats(self) -> dict[str, Any]:
return {
name: {
"used_bytes": p.used_bytes,
"hits": p.hits,
"misses": p.misses
}
for name, p in self._pools.items()
}