Sparkview — GPU monitor tool with GB10-aware unified memory handling

Interesting, thanks for the reply and the education. I’m still learning the ins-and-out of this so I really appreciate it.

Last night after some googling I supposed that the memory pressure had something to do so I’ve reduced a few points on my --gpu-memory… parameter. I left a few jobs running last night and this morning while idling the results are the following:

With again IO hovering from high to critical. But SWAP now is not being utilized and overall memory utilization is a bit lower which I like. I don’t see almost any disk utilization… I wonder if I’m looking at the wrong thing but btop seems to confirm that there’s no IO, much less on idle

Sample log entries on an idle Spark:

  42 │ --- 2026-04-19 12:26:46 ---
  43 │ GPU:   0%  35°C (peak 35°C)  4.2W  Mem 2.3Gi/121.7Gi
  44 │ MEM:   1.9%  Used 2.3Gi / 121.7Gi
  45 │ SWAP:  0.0%  Used 0.0Gi / 16.0Gi
  46 │ CPU:   0.0%  Active 0/20  37°C (peak 38°C)
  47 │ CLOCK: IDLE  208MHz / 3003MHz  P8
  48 │ PSI:   LOW  some 0.00  full 0.00
  49 │ IO:    HIGH  some 0.06  full 0.06
  50 │ 
  51 │ --- 2026-04-19 12:26:48 ---
  52 │ GPU:   0%  35°C (peak 35°C)  4.3W  Mem 2.3Gi/121.7Gi
  53 │ MEM:   1.9%  Used 2.3Gi / 121.7Gi
  54 │ SWAP:  0.0%  Used 0.0Gi / 16.0Gi
  55 │ CPU:   0.0%  Active 0/20  37°C (peak 38°C)
  56 │ CLOCK: IDLE  208MHz / 3003MHz  P8
  57 │ PSI:   LOW  some 0.00  full 0.00
  58 │ IO:    CRITICAL  some 0.23  full 0.23
  59 │ 

Notice the IO readings!

In layers/pressure.py you define:

 25 │         s = result["some_avg10"]
 26 │         f = result["full_avg10"]
 27 │         if f > 0.10 or s > 0.30:
 28 │             result["level"] = "CRITICAL"
 29 │         elif f > 0.05 or s > 0.15:
 30 │             result["level"] = "HIGH"
 31 │         elif s > 0.05:
 32 │             result["level"] = "MOD"
 33 │         else:
 34 │             result["level"] = "LOW"

You’re using only avg10 reading and ignore avg60 and avg300, so only the short-term spikes are noticed. However, more time might be a better reading to evaluate if the system is struggling.

Good progress — SWAP clearing confirms the gpu_memory_utilization
reduction helped with the memory pressure.

The IO CRITICAL signal at idle is a separate condition. With GPU at
0%, this points to background I/O activity rather than active compute.
Try expanding the terminal or scrolling down to the PROC section —
something may still be running from an earlier workload.

Can you share a screenshot with the PROC section visible?

Also worth checking:
mount | grep -E “nfs|cifs|smb”

If model weights or datasets are on a network mount,
/proc/pressure/io will reflect those stalls as well.

v0.2.2 adds IO anomaly logging — IO CRITICAL events are captured
automatically to ~/sparkview_logs/ with a summary.json showing
trigger and full snapshot timeline. That should help isolate the
source of the stall.

Got nothing on the network, all local :)

Just updated to 0.2.2 for this reply :)

-– 2026-04-19 15:34:39 —
GPU: 0% 36°C (peak 36°C) 11.8W Mem 108.8Gi/121.6Gi
MEM: 89.5% Used 108.8Gi / 121.6Gi
SWAP: 0.0% Used 0.0Gi / 16.0Gi
CPU: 0.0% Active 0/20 38°C (peak 38°C)
CLOCK: IDLE 2405MHz / 3003MHz P0
PSI: LOW some 0.00 full 0.00
IO: CRITICAL some 1.91 full 1.91
PROC:
4519 root 96.6Gi 0.0% VLLM::EngineCore
2841 mudo 0.3Gi 0.0% gnome-remote-desktop-daemon
2910 mudo 0.1Gi 0.0% gnome-shell
2451 mudo 0.1Gi 0.0% Xorg

I also took an IO ScreenShot from htop:

Again, I don’t see anything out of the ordinary.

Those thresholds were tuned on discrete GPU systems — not GB10. The IO HIGH/CRITICAL signal at idle with ~1.9% memory is exactly the kind of data needed to recalibrate for coherent UMA.

To establish a baseline, a small collector tool is now in the repo:

git pull
python3 tools/collect_psi_baseline.py --duration 120 --label idle

Then run under load conditions:

python3 tools/collect_psi_baseline.py --duration 120 --label vllm_loaded
python3 tools/collect_psi_baseline.py --duration 120 --label inference_running

Each run writes JSON to ~/sparkview_logs/psi_baseline/ with min, max, mean, p90, and p99 per PSI channel.

If you can share those outputs, the thresholds in pressure.py can be recalibrated against real GB10 behavior.

The htop screenshot confirms it — no process-level disk IO, but
IO PSI elevated. The pressure is not coming from user-space reads
or writes.

The difference is what each tool measures:

  • htop I/O tab → per-process bytes to block devices
  • sparkview (/proc/pressure/io) → kernel time stalled on IO,
    including memory management

With 96.6Gi resident in unified memory and no active process IO,
this signal is coming from below the process layer.

The current thresholds flag this as CRITICAL, but with your data
it’s clear this is not a fault condition — it’s baseline GB10
behavior with VLLM loaded.

Since you’re on v0.2.2, open a new terminal in your sparkview
folder and run two collections — first with VLLM stopped:

git pull
python3 tools/collect_psi_baseline.py --duration 120 --label idle

Then with VLLM running:

python3 tools/collect_psi_baseline.py --duration 120 --label vllm_loaded

Each runs for 2 minutes and prints a summary at the end. Paste
both summaries here — that gives the GB10 floor and the loaded
baseline, and the thresholds in pressure.py can be recalibrated
from real hardware data.

Update — peak power landed in v0.2.2.

On GB10 with spark_hwmon installed, the PWR row now shows:

PWR GPU 11W peak 47W DC 38.8W Cap 30W ● PROCHOT PL1 Tj+4.8°C
(example values)

Instantaneous GPU draw, session peak, DC input, power cap,
PROCHOT state, active PL level, and thermal rise — all on
one line, updated every refresh cycle.

spark_hwmon install instructions are in the README.

sparkview_psi_baseline_.zip (6.0 KB) Here!

Thanks for the baseline data — that’s exactly what was needed.

If you’re set up, one additional measurement would help. A companion tool measures UMA fault latency using PTX clock instrumentation (no CUPTI dependency).

Build:

git clone GitHub - parallelArchitect/nvidia-uma-fault-probe: Cycle-accurate UMA fault latency and bandwidth measurement for NVIDIA GPUs. C and PTX. No Python. Pascal (SM 6.0) through Blackwell GB10 (SM 12.1). · GitHub
cd nvidia-uma-fault-probe
nvcc -O2 -std=c++17 probe_launcher.cu -o uma_probe -lcudart -lcuda -lpthread

Run twice:

./uma_probe # first with VLLM stopped
./uma_probe # then with VLLM loaded

If you can share both outputs, it provides a direct before/after comparison of UMA fault latency under unified memory pressure on GB10.


Got this error:

mudo@gx10-a1db:~/nvidia-uma-fault-probe$ nvcc -O2 -std=c++17 probe_launcher.cu -o uma_probe -lcudart -lcuda -lpthread
probe_launcher.cu(189): error: no suitable constructor exists to convert from “int” to “cudaMemLocation”
do { cudaError_t e = (cudaMemPrefetchAsync(data, n*sizeof(float), device, 0)); if (e != cudaSuccess) { fprintf(
^

probe_launcher.cu(195): error: no suitable constructor exists to convert from “int” to “cudaMemLocation”
do { cudaError_t e = (cudaMemPrefetchAsync(data + n/2, (n/2)*sizeof(float), device, 0)); if (e != cudaSuccess) { fprintf(
^

2 errors detected in the compilation of “probe_launcher.cu”.

Fix is in — the issue was a CUDA 13 type strictness on aarch64.
Pull the update and rebuild:

cd nvidia-uma-fault-probe
git pull
nvcc -O2 -std=c++17 probe_launcher.cu -o uma_probe -lcudart -lcuda -lpthread
./uma_probe

Thanks for this, created this collapsable UI.

@azampatti — did the fix work? Any output from ./uma_probe?

Thanks for making this its really neat. Its helping me with my NVFP4 research im doing.

Glad it’s helping. Still early and evolving — feedback from the community is what’s shaping it right now.

Sorry, I DMd you to avoid clutter here, you might have missed it.
mudo@gx10-a1db:~/nvidia-uma-fault-probe$ nvcc -O2 -std=
c++17 probe_launcher.cu -o uma_probe -lcudart -lcuda -l
pthread
probe_launcher.cu(189): error: no suitable constructor
exists to convert from “int” to “cudaMemLocation”
do { cudaError_t e = (cudaMemPrefetchAsync(da
ta, n*sizeof(float), device, 0x00)); if (e != cudaSucce
ss) { fprintf(

                ^ 

probe_launcher.cu(195): error: no suitable constructor
exists to convert from “int” to “cudaMemLocation”
do { cudaError_t e = (cudaMemPrefetchAsync(da
ta + n/2, (n/2)*sizeof(float), device, 0x00)); if (e !=
cudaSuccess) { fprintf(

                          ^ 

2 errors detected in the compilation of “probe_launcher
.cu”.

It still errored out :(

v0.2.3 pushed — fixes the IO CRITICAL false alarm reported
in GitHub issue #3.

On GB10 with VLLM loaded but idle, the IO row always showed
CRITICAL because VLLM worker processes block waiting for
inference requests, which inflates the PSI IO metric. IO row
removed entirely.

Pull the latest:

git pull
python3 main.py

Just upgraded and will run this all day today and report back if I see anything out of the ordinary.

Full writeup and GB10 baseline data now published:

Credit to @azampatti for running every iteration — patient and
committed from start to finish. Without his persistence on real
hardware none of this data would exist.

For tool documentation, build instructions, and example output:

Thanks to the entire community for the support, sosreports,
field data, bug logs and feedback. It would not be possible without it.