qwen-voice-studio / qvs /memory.py
techfreakworm's picture
LoRA live-toggle (PEFT enable/disable) + D4 UI + memory hygiene
c3b59db unverified
Raw
History Blame
3.24 kB
"""Memory accounting that is honest on macOS.
Why this exists: process RSS *undercounts* MPS/IOKit allocations (a 4.5 GB model
showed 0.9 GB RSS), while ``total - available`` *overcounts* (it charges the huge
reclaimable file cache from the HF cache). The gauge that matches Activity
Monitor's "Memory Used" — and that actually tracks MPS demand — is
``wired + active + compressed`` from ``vm_stat``.
Used to keep local test/inference under the operator's 80 GB ceiling.
"""
from __future__ import annotations
import os
import re
import subprocess
import sys
import threading
import time
from dataclasses import dataclass
_PAGE = 16384 # Apple-silicon page size (bytes)
def committed_gb() -> float:
"""Real committed memory in GB (Activity-Monitor "Memory Used").
macOS: parses ``vm_stat`` (wired + active + compressed). Other platforms:
falls back to psutil ``used``.
"""
if sys.platform == "darwin":
try:
out = subprocess.run(["vm_stat"], capture_output=True, text=True, timeout=5).stdout
def pages(pattern: str) -> int:
m = re.search(pattern + r":\s+(\d+)", out)
return int(m.group(1)) if m else 0
wired = pages(r"Pages wired down")
active = pages(r"Pages active")
comp = pages(r"Pages occupied by compressor")
return (wired + active + comp) * _PAGE / 2**30
except Exception:
pass
try:
import psutil
return psutil.virtual_memory().used / 2**30
except Exception:
return 0.0
def total_gb() -> float:
try:
import psutil
return psutil.virtual_memory().total / 2**30
except Exception:
return 0.0
@dataclass
class MemorySnapshot:
committed: float
total: float
def __str__(self) -> str:
return f"{self.committed:.1f}/{self.total:.0f} GB committed"
def snapshot() -> MemorySnapshot:
return MemorySnapshot(committed_gb(), total_gb())
class MemoryGuard:
"""Daemon thread that hard-aborts the process if committed memory crosses a
ceiling — a backstop against OOM-ing the OS during local testing.
``on_trip`` defaults to ``os._exit`` because a soft exception cannot reliably
unwind a runaway allocation.
"""
def __init__(self, hard_gb: float = 76.0, soft_gb: float | None = 72.0, interval: float = 0.5):
self.hard_gb = hard_gb
self.soft_gb = soft_gb
self.interval = interval
self._warned = False
self._thread: threading.Thread | None = None
def _loop(self) -> None:
while True:
c = committed_gb()
if c > self.hard_gb:
print(f"[MemoryGuard] committed {c:.1f} GB > {self.hard_gb} GB -> HARD ABORT", flush=True)
os._exit(97)
if self.soft_gb and c > self.soft_gb and not self._warned:
print(f"[MemoryGuard] WARN committed {c:.1f} GB > {self.soft_gb} GB (ceiling {self.hard_gb})", flush=True)
self._warned = True
time.sleep(self.interval)
def start(self) -> "MemoryGuard":
self._thread = threading.Thread(target=self._loop, daemon=True)
self._thread.start()
return self