Pull requests / #2273
#2273 serve: an AMD card's load, VRAM, temperature and power on Windows
open · @unimo-chan · 0 commentaires · Sur GitHub
Setup & installServer & APIAMD / HIPModels & quantsWindowsLinux
Description
## What this changes On Windows an AMD card has no NVML and no amdgpu sysfs, so `serve/telemetry.py` had nothing to read and the dashboard said *"no GPU load or VRAM readings for AMD cards on Windows yet"* (the `gpu_note` below, from #1380). This adds a reader built from what the machine does have — all of it through `ctypes`, no package: - **DXGI** (`CreateDXGIFactory1` → `EnumAdapters1` → `GetDesc1`): the card's name, its dedicated memory total, and its LUID. The card is the one whose vendor ID is 0x1002, so an NVIDIA display card in the same PC is not mistaken for the card the engine runs on. - **Performance counters** (`PdhAddEnglishCounterW`, English counter names, instances picked by that LUID) — the same counters Task Manager's GPU column reads: `\GPU Adapter Memory(luid_…)\Dedicated Usage` for the VRAM in use, `\GPU Engine(*luid_…*)\Utilization Percentage` for load (the busiest engine, which is what Task Manager shows; measured on this machine: 93% on the compute engine where the `Running Time` counter beside it computed 94% over the same window). DXGI's own `QueryVideoMemoryInfo` is per-process and reported 0 while the engine held 19 GiB, so it is not used. - **ADLX** (`amdadlx64.dll`, installed with the Adrenalin driver) for temperature and power: the ADLX services reached through their vtable slots (ADLX interfaces are plain C++ vtables, not COM), the GPU found through the ADL mapping and matched to the name DXGI reported. On this driver plain `ADLXInitialize` answers an empty GPU list, so `ADLXInitializeWithIncompatibleDriver2` is the entry that works; `GPUPower` answers NOT_SUPPORTED, so power falls back to `GPUTotalBoardPower`. Initialization is once per process (a second `GetPerformanceMonitoringServices` hangs, measured). - `gpu_reader(amd=True)` returns this on Windows when there is no amdgpu sysfs; the Linux path and the NVIDIA path are untouched, and a card none of the three reaches still gets a word why instead of empty tiles. Enumeration, counters and property reads only — no device, no context: it takes no VRAM from the engine and cannot stall it. ## What it leaves alone - **No PCIe readings at all** — neither the link nor the traffic. The card's own PCI devnode describes the switch inside an RX 7000 (measured: Gen4 x16 reported by the endpoint while the card sits in a Gen3 x8 slot on a B450), and the three devices above it report nothing, so the host-facing link is one Windows cannot name; the tile stays blank there rather than showing the card's own capability (#1734, #2137). For the traffic: PDH has no PCIe counter set and ADLX 1.5's headers name no bus metric — that half is the engine's own byte counter, in #2272. - **No new dependency.** `adlxpybind` ships cp310–cp313 `win_amd64` wheels and no sdist, so a `pip install` fails on any other Python; `ctypes` needs nothing and the file already uses it for NVML. - Linux, NVIDIA, the engine, and every other file. ## Where this overlaps #1871 (dashboard readings for AMD cards), #1152 (Windows AMD telemetry, with an Arabic UI), #1224 (ADLX through `adlxpybind`) and #1434 are all open in this area, and I read them before writing this. This one is the ctypes-only route: no install step, and it is the ADLX *fallback chain* (`GPUPower` → `GPUTotalBoardPower`) and the LUID-picked performance counters that make the numbers appear on a driver where the direct calls answer NOT_SUPPORTED. If any of those PRs lands first, this should be rebased on it rather than shipped beside it. ## How I checked RX 7900 XT, driver 32.0.23033.1002, ADLX 1.5.0.124, Windows 11, Python 3.12.10, with the engine holding the model: - idle: 42–43 °C, 67–82 W, util 0, 19248 MiB of 20418 MiB in use (the engine's own allocation). - during one chat request (400 completion tokens in 9.1 s): util 90–95 %, power up to 292 W, 47 °C. - the ADLX path is the only one that answers temperature and power here: ADL (`atiadlxx`) enumerates the card but answers −1/−5 for every sensor call, and no performance counter or WMI class reports them. - `python -m unittest serve.test_server.AmdTelemetry serve.test_server.TelemetryNoDisks serve.test_server.SharingTheGpu serve.test_server.DesktopVramNote serve.test_server.WebApp serve.test_server.PrometheusMetrics serve.test_server.LiveRate`: **42 tests, OK** (Python 3.12.10, on main at `a0116b2`). Full `serve.test_server` on this branch: **308 tests, OK** in 101 s (Windows 11, Python 3.12.10), nothing skipped. - Cross-checked against AMD's own client for the same library — the `adlxpybind` 1.4.6 wheel, unpacked rather than installed: it answers the same 43.0 °C and the same 19248 MiB, and the same `GPUPower` 0.0 with `GPUTotalBoardPower` 64.0. The temperature and VRAM figures are not an artifact of reading the vtable by hand. One existing test changed: the sysfs link test built its reader with `gpu_reader(0, amd=True)`, which on Windows is now the DXGI reader; it constructs `telemetry._Amd(0)` directly instead. Its assertions are unchanged. Not tested: an RX 6000 or an older driver, a machine without `amdadlx64.dll` (the reader returns nothing and the existing note is shown), and a second card.
Sur le site
Liens install, modèles, releases.