feat: add amdsmi probe + typeperf max fix

This commit is contained in:
2026-06-09 00:15:01 +02:00
parent 75be282f41
commit aeeea8a170
2 changed files with 105 additions and 9 deletions
+95
View File
@@ -378,6 +378,100 @@ class _TypeperfGpuQuery:
return None
# ---------------------------------------------------------------------------
# amdsmi-based GPU utilisation (official AMD SMI library)
#
# Uses the AMD SMI Python package which ships with ROCm. Talks directly
# to the AMD driver — not through WDDM. Reports real GPU engine utilisation
# (GFX, MM, MEM) as percentages 0100%.
#
# Install: pip install amdsmi
# Requires: ROCm 6+ (user has ROCm 7.2)
# ---------------------------------------------------------------------------
class _AmdSmiGpuQuery:
"""GPU utilisation reader via official AMD SMI Python library."""
def __init__(self):
self._handle = None
self._initialized = False
self._ok = False
def init(self) -> bool:
try:
import amdsmi as _smi
_smi.amdsmi_init()
self._smi = _smi
handles = _smi.amdsmi_get_processor_handles()
if not handles:
logger.warning("XPUSYSMonitor: amdsmi — no processor handles.")
_smi.amdsmi_shut_down()
return False
self._handle = handles[0]
self._initialized = True
# Test read to confirm it works
try:
activity = _smi.amdsmi_get_gpu_activity(self._handle)
logger.info(
f"XPUSYSMonitor: amdsmi GPU activity test — "
f"{activity!r}"
)
except Exception as exc:
logger.warning(
f"XPUSYSMonitor: amdsmi activity test failed — {exc}"
)
self._ok = True
logger.info("XPUSYSMonitor: amdsmi GPU counters OK.")
return True
except ImportError:
logger.info(
"XPUSYSMonitor: amdsmi not installed — "
"run `pip install amdsmi` to enable AMD SMI monitoring."
)
return False
except Exception as exc:
logger.warning(f"XPUSYSMonitor: amdsmi init error — {exc}")
try:
self._smi.amdsmi_shut_down()
except Exception:
pass
return False
def read_gpu_utilization(self) -> float:
"""Return GPU utilisation % via amdsmi (GFX engine)."""
if not self._ok or self._handle is None:
return 0.0
try:
activity = self._smi.amdsmi_get_gpu_activity(self._handle)
# amdsmi_get_gpu_activity returns engine utilisation.
# The exact return type depends on the version. Try common
# access patterns: attribute, dict key, or index.
if hasattr(activity, 'gfx'):
return float(activity.gfx)
if isinstance(activity, dict):
return float(activity.get('gfx', activity.get('GFX', 0.0)))
if isinstance(activity, (list, tuple)):
return float(activity[0]) if activity else 0.0
# Last resort: try treating it as a number directly
return float(activity)
except Exception:
return 0.0
def close(self) -> None:
if self._initialized:
try:
self._smi.amdsmi_shut_down()
except Exception:
pass
self._ok = False
__all__ = [
"_is_admin",
"_get_cpu_info",
@@ -385,4 +479,5 @@ __all__ = [
"_read_commit_charge",
"_PdhQuery",
"_TypeperfGpuQuery",
"_AmdSmiGpuQuery",
]
+10 -9
View File
@@ -18,7 +18,7 @@ import sys
from typing import Tuple
from .base import BaseGPUProvider, GPUSnapshot
from ._utils import _get_cpu_info, _read_cpu_ram_stats, _TypeperfGpuQuery, _is_admin
from ._utils import _get_cpu_info, _read_cpu_ram_stats, _TypeperfGpuQuery, _AmdSmiGpuQuery, _is_admin
logger = logging.getLogger("XPUSYSMonitor")
@@ -52,11 +52,13 @@ class AMDProvider(BaseGPUProvider):
self._check_torch()
self._check_psutil()
# Windows GPU utilisation — typeperf (primary, reliable CSV output)
# Windows GPU utilisation — amdsmi (official) -> typeperf (fallback)
# PDH has wildcard-counter issues with AMD drivers, so skip it.
self._pdh_ok = False # PDH disabled for AMD (wildcard handling unreliable)
self._pdh_ok = False
self._as_gpu = _AmdSmiGpuQuery()
self._as_gpu_ok = self._as_gpu.init()
self._tp_gpu = _TypeperfGpuQuery()
self._tp_gpu_ok = self._tp_gpu.init()
self._tp_gpu_ok = self._tp_gpu.init() if not self._as_gpu_ok else False
# BaseGPUProvider.__init__ starts the polling thread — call last
super().__init__(interval_ms=interval_ms)
@@ -185,12 +187,11 @@ class AMDProvider(BaseGPUProvider):
"""
Return GPU utilisation %.
Tries PDH API first (sub-millisecond, ctypes).
Falls back to PowerShell Get-Counter if PDH is unavailable
(slower ~100-300ms but works on any Windows WDDM driver).
Tries amdsmi (official AMD SMI, bypasses WDDM).
Falls back to typeperf (WDDM counters, best-effort).
"""
if self._pdh_ok:
return self._pdh.read_gpu_utilization()
if self._as_gpu_ok:
return self._as_gpu.read_gpu_utilization()
if self._tp_gpu_ok:
return self._tp_gpu.read_gpu_utilization()
return 0.0