Architecture

gputop is organized as a one-way pipeline:

Providers ──► Collectors ──► Publisher ──► Snapshot ──► TUI / JSON / API / Prometheus
                                 │
                                 ├─► derive (state, efficiency, imbalance, health)
                                 ├─► events + alerts
                                 └─► history store

Each stage has one owner. Collectors never render. The history store never collects. The UI never calls a provider.

Vendor-neutral core (internal/gpu)

gpu.Provider is the only interface a vendor backend implements:

type Provider interface {
    Name() string
    Vendor() Vendor
    Open(ctx) (Diagnostics, error)
    Close() error
    System(ctx) (SystemInfo, error)
    Devices(ctx) ([]Device, error)
    Sample(ctx, id) (Sample, error)
    Processes(ctx, id) ([]Process, error)
    Health(ctx, id) (HealthCounters, error)
    Links(ctx, id) ([]Link, error)
    Partitions(ctx, id) ([]Partition, error)
}

Optional interfaces: gpu.EventSource (event-driven notifications such as Xids) and gpu.TopologyProvider (PCIe relationships).

Design rules:

Adding a vendor means creating internal/gpu/<vendor>, implementing Provider, and registering it in internal/app. No other package changes.

NVIDIA provider (internal/gpu/nvidia)

Apple silicon provider (internal/gpu/apple)

Collector engine (internal/collector)

Tier Default Collectors
fast 1s gpu (samples), host (CPU, memory)
normal 3s processes, health, links, network, disks, inference
slow 30s partitions (MIG), filesystems, kubernetes
inventory 5m inventory (discovery, static info, topology)
event — provider event sources

Each collector:

Per-device work runs on a bounded worker pool (4 by default). In-flight state is tracked per (collector, device): a hung call on one GPU does not delay the others, and the pass returns at its deadline. A GPU with no fresh sample for max(3×interval, 2×timeout) is marked unavailable (“not responding”).

A GPU reported as lost (ERROR_GPU_IS_LOST) becomes unavailable, generates an event, and scores 0 health. Other GPUs keep updating. A device that disappears from enumeration triggers an inventory refresh.

Publisher and snapshots

On every fast tick the publisher:

  1. copies collector state into a new model.Snapshot (deep-copying slices);
  2. runs derive.Tracker.Apply: state, VRAM headroom, idle tracking, efficiency, NVLink rates, health score and fleet summary;
  3. runs events.Detector.Diff against the previous snapshot and adds provider events; computes active alerts;
  4. feeds the history store;
  5. atomically stores the snapshot and notifies subscribers.

Published snapshots are immutable. Subscribers receive the latest snapshot on a buffered channel of size one; slow consumers skip intermediate snapshots but never block the engine.

History (internal/history)

TUI (internal/tui)

Service and remote

Concurrency and cancellation

Spotted something wrong? Edit this page on GitHub ↗