Files
XPUSYS-Monitor-NG/providers/base.py
T
2026-06-08 23:04:38 +02:00

135 lines
4.7 KiB
Python

"""
providers/base.py — Abstract base class and shared data contract.
All GPU provider implementations must subclass BaseGPUProvider and
implement get_snapshot(). The GPUSnapshot dataclass is the single
contract between the hardware layer and every consumer (server,
predictor, frontend).
"""
from __future__ import annotations
import threading
from dataclasses import dataclass, field
from typing import Optional
# ---------------------------------------------------------------------------
# Shared data snapshot — the contract between providers and consumers
# ---------------------------------------------------------------------------
@dataclass
class GPUSnapshot:
# --- VRAM (all in GB) ---
vram_total_gb: float = 0.0 # driver total
vram_free_gb: float = 0.0 # driver free
vram_driver_used_gb: float = 0.0 # total - free (all consumers)
vram_allocated_gb: float = 0.0 # torch allocated (ComfyUI workload)
vram_reserved_gb: float = 0.0 # torch cached / reserved pool
# --- GPU ---
gpu_load_pct: float = 0.0
gpu_freq_mhz: float = 0.0 # current GPU clock in MHz (0 = unavailable)
gpu_temp_c: float = -1.0 # GPU core temp in °C (-1 = unavailable)
# --- Power ---
power_w: float = -1.0 # -1 = unavailable
power_available: bool = False
tgp_w: float = 0.0 # sustained TGP limit in W (0 = unknown)
device_name: str = ""
# --- CPU ---
cpu_pct: float = 0.0
cpu_freq_ghz: float = 0.0
cpu_model: str = ""
cpu_threads: int = 0
# --- RAM ---
ram_pct: float = 0.0
ram_total_gb: float = 0.0
ram_used_gb: float = 0.0
ram_free_gb: float = 0.0
# Windows Commit Charge (≈ Task Manager "已提交")
commit_used_gb: float = 0.0 # CommitTotal = ullTotalPageFile - ullAvailPageFile
commit_limit_gb: float = 0.0 # CommitLimit = ullTotalPageFile
# --- Meta ---
is_admin: bool = False
gpu_vendor: str = "" # "intel" | "nvidia" | "amd" | "unknown"
error: Optional[str] = None
# ---------------------------------------------------------------------------
# Abstract base provider
# ---------------------------------------------------------------------------
class BaseGPUProvider:
"""
Abstract base class for all GPU hardware providers.
Subclasses implement _poll() to fill a GPUSnapshot and call
_update_snapshot(snap) when done. The base class handles
thread-safe snapshot storage and the polling loop lifecycle.
Consumers always call get_snapshot() — they never see the
concrete provider type.
"""
# Subclasses set this to identify their vendor in GPUSnapshot.gpu_vendor
GPU_VENDOR: str = "unknown"
def __init__(self, interval_ms: int = 1000):
self._interval = max(100, interval_ms) / 1000.0
self._snapshot = GPUSnapshot(gpu_vendor=self.GPU_VENDOR)
self._lock = threading.Lock()
self._stop = threading.Event()
self._thread = threading.Thread(
target=self._loop, daemon=True, name=f"XPUSYSMonitor-{self.GPU_VENDOR}"
)
self._thread.start()
# ------------------------------------------------------------------
# Public API
# ------------------------------------------------------------------
def get_snapshot(self) -> GPUSnapshot:
"""Return the latest hardware snapshot. Thread-safe."""
with self._lock:
return self._snapshot
def set_interval(self, ms: int) -> None:
"""Adjust polling interval at runtime."""
self._interval = max(100, ms) / 1000.0
def stop(self) -> None:
"""Stop the background polling thread."""
self._stop.set()
self._thread.join(timeout=5)
# ------------------------------------------------------------------
# Internal helpers
# ------------------------------------------------------------------
def _update_snapshot(self, snap: GPUSnapshot) -> None:
"""Atomically replace the stored snapshot. Called from _poll()."""
with self._lock:
self._snapshot = snap
def _loop(self) -> None:
"""Background polling loop — calls _poll() every interval."""
while not self._stop.is_set():
try:
self._poll()
except Exception as exc:
snap = GPUSnapshot(gpu_vendor=self.GPU_VENDOR, error=str(exc))
self._update_snapshot(snap)
self._stop.wait(self._interval)
def _poll(self) -> None:
"""
Override in subclass: collect hardware data and call
_update_snapshot(snap) with a freshly built GPUSnapshot.
"""
raise NotImplementedError