repair-guy / core /vram.py
airayven7's picture
Sync from GitHub 876d199
d8f1e4d verified
Raw
History Blame Contribute Delete
4.01 kB
"""Tiny GPU-memory probe for understanding the VRAM budget.
The Space runs on a ZeroGPU `large` slice (48 GB of an RTX Pro 6000). Several
models are resident at once β€” the MiniCPM-V "eyes", the ColEmbed search model,
the text embedder, plus one swappable agent "brain" β€” so it's easy to lose track
of how much headroom is actually left. log_vram() prints a one-line snapshot you
can drop at any interesting point (a model load, a brain evict/load) to watch the
numbers move.
Real device memory is only meaningful INSIDE a @spaces.GPU context: at module
import ZeroGPU runs a CUDA *emulation* mode (models go to "cuda" but no physical
GPU is held), so mem_get_info() either errors or reports the host. Calls are
therefore wrapped defensively β€” outside a real GPU window they degrade to just
the allocator counters (or nothing) instead of raising.
Four numbers to read:
alloc bytes backing live tensors (what the model weights + activations use)
peak high-water mark of alloc since the last reset_peak() β€” catches the
transient activation/KV spike during generation that a point-in-time
alloc misses
reserved bytes the caching allocator holds from the driver (alloc + cached
free blocks); this is the real pressure on the 48 GB ceiling
free driver-reported free VRAM on the device (only inside @spaces.GPU)
Typical usage across one find turn (all inside the @spaces.GPU worker, so the
numbers are real β€” see the toggle note below):
set_enabled(vram_log) # apply the UI toggle for this turn
reset_peak() # zero the high-water mark
log_vram("turn-start") # resident models + current brain, idle
... use_model() logs evict/load deltas if the brain switches ...
log_vram("after-ground") # peak now reflects the VLM grounding spike
"""
import logging
import torch
log = logging.getLogger("repairguy.vram")
_GiB = 1024**3
# Off by default β€” the UI "VRAM logging" toggle flips this per turn (the find
# pipeline calls set_enabled() at the start of each turn, inside the GPU worker).
# When off, log_vram()/reset_peak() are no-ops, so the probe costs nothing and
# the logs stay quiet. Import-time snapshots (model loads) are therefore silent
# unless the toggle is on the next turn re-triggers a switch.
_ENABLED = False
def set_enabled(flag: bool) -> None:
"""Turn the VRAM probe on or off. Driven by the per-turn UI setting."""
global _ENABLED
_ENABLED = bool(flag)
def log_vram(label: str) -> None:
"""Log a GPU-memory snapshot tagged with `label`. No-op unless the probe is
enabled (set_enabled). When on: safe to call anywhere β€” no-ops cleanly when
CUDA is unavailable and tolerates ZeroGPU's import-time emulation mode (where
device free/total can't be queried)."""
if not _ENABLED:
return
if not torch.cuda.is_available():
log.info("vram[%s]: cuda unavailable", label)
return
alloc = torch.cuda.memory_allocated() / _GiB
peak = torch.cuda.max_memory_allocated() / _GiB
reserved = torch.cuda.memory_reserved() / _GiB
try:
free, total = torch.cuda.mem_get_info()
log.info(
"vram[%s]: alloc=%.2f peak=%.2f reserved=%.2f free=%.2f/%.2f GiB",
label,
alloc,
peak,
reserved,
free / _GiB,
total / _GiB,
)
except Exception:
# Import-time emulation mode: no real device to query free/total.
log.info(
"vram[%s]: alloc=%.2f peak=%.2f reserved=%.2f GiB (no device info)",
label,
alloc,
peak,
reserved,
)
def reset_peak() -> None:
"""Reset the alloc high-water mark so the next log_vram() peak reflects only
what happened since this call (e.g. one find turn). No-op when the probe is
disabled or CUDA is unavailable."""
if _ENABLED and torch.cuda.is_available():
torch.cuda.reset_peak_memory_stats()