A modern, Textual-based TUI for watching GPU
engagement on a node — in the spirit of nvtop, built on Rich/Textual, and
covering NVIDIA, AMD, and Apple Silicon.
- Live per-GPU panels: utilization, memory, temperature, power, fan, clocks, encoder/decoder engagement, and a rolling utilization sparkline.
- Per-GPU process table (PID, name, device memory) — NVIDIA only for now.
- Adjustable refresh rate (
+/-at runtime). - Backend-pluggable, three vendors today:
- NVIDIA — via NVML (
nvidia-ml-py). Full field coverage, including per-process memory. - AMD — via the
rocm-smiCLI (ROCm must be installed). Utilization, memory, temperature, power, fan, clocks; no per-process view yet. Parsesrocm-smi's JSON defensively (case-insensitive key matching) to tolerate key-name drift across ROCm versions, but hasn't been run against real ROCm hardware — if a field looks off, please open an issue with yourrocm-smi -a --jsonoutput. - Apple Silicon — via
ioreg'sIOAcceleratorregistry (no sudo required, macOS only). Utilization and a GPU-attributed memory figure (against total unified memory, there's no separate VRAM pool). Apple doesn't expose temperature/power/fan/clocks/per-process GPU stats withoutpowermetrics, which requires root — those fields show as—.
- NVIDIA — via NVML (
- A
demobackend with synthetic data, so you can preview the UI on a machine with no GPU at all.
Adding a vendor means writing one GpuBackend subclass that returns
HostSnapshot/GpuSnapshot objects — the app and widgets are backend-agnostic.
pip install gpu-scopeThe project is gpu-scope end to end — gpu-top was blocked by PyPI's
typosquat-similarity check against an unrelated existing package
(gputop). The gpu-top name lives on as a second command, installed
alongside gpu-scope and pointing at the exact same program, for anyone
used to typing it.
One install gets every backend — there's nothing to opt into. NVIDIA support
(nvidia-ml-py) ships as a core dependency: it's a small, pure-Python
ctypes wrapper with no platform-specific build, so it installs cleanly
everywhere and simply reports "unavailable" at runtime on a machine with no
NVIDIA driver. AMD and Apple need no extra pip packages at all — they shell
out to system tools (rocm-smi, ioreg) instead. The one thing pip
can't do for you: AMD support only activates if ROCm's rocm-smi is on
your PATH (a system package, install it via your distro/ROCm docs).
pip install gpu-scope[nvidia] is still accepted (as a no-op) if you're
used to typing an extra — it doesn't install anything beyond the plain
command above.
Installing from source instead: pip install "git+https://github.com/matplo/gpu-scope.git".
gpu-scope # auto-detects the first available backend (gpu-top works identically)
gpu-scope --backend demo # synthetic data, no GPU required
gpu-scope --list-backends # show what's available on this host
gpu-scope --interval 0.5 # poll twice a secondRunning with no GPU present, or forcing --backend nvidia/--backend amd on a
host without that vendor's tooling, exits with a short explanation and a
--backend demo suggestion instead of a driver-library stack trace.
Keys: q quit · + / - refresh rate · d toggle light/dark theme.
gpu_scope/
├── models.py # GpuSnapshot / GpuProcess / HostSnapshot dataclasses
├── backends/
│ ├── base.py # GpuBackend ABC — open() / poll() / close()
│ ├── nvidia.py # NVML-backed implementation
│ ├── amd.py # rocm-smi (CLI + JSON) implementation
│ ├── apple.py # ioreg IOAccelerator implementation
│ └── demo.py # synthetic backend for development/preview
├── widgets/
│ ├── meters.py # Rich-renderable bars/sparklines (no extra deps)
│ └── gpu_panel.py # the per-GPU card widget
├── app.py # Textual App: layout + polling loop
└── cli.py # argparse entry point (`gpu-scope` / `gpu-top`)
pip install -e ".[dev]"
pytest
gpu-scope --backend demo # preview the UI with synthetic data, no GPU requiredFor live Textual devtools (a separate log console while the TUI runs), see
the Textual docs — in short,
run textual console in one terminal and textual run --dev "gpu_scope.cli:main"
in another.
MIT