[PR #237] [MERGED] fix(tui/process): cache CPU utilization samples across screens #236

Closed
opened 2026-10-31 22:03:36 -06:00 by gitea-mirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/XuehaiPan/nvitop/pull/237
Author: @arinawang11
Created: 10/31/2026
Status: ✅ Merged
Merged: 10/31/2026
Merged by: @XuehaiPan

Base: main ← Head: fix/cpu-percent-sampling-race


📝 Commits (4)

  • 41cf5c6 fix(tui/process): cache non-blocking cpu_percent samples
  • 0bea27e docs(tui/process): clarify CPU sampling cache rationale
  • 5076387 docs(changelog): note TUI CPU sampling fix
  • e7d44e2 fix(process): preserve subclasses and enlarge CPU sample cache

📊 Changes

4 files changed (+36 additions, -14 deletions)

View changed files

📝 CHANGELOG.md (+1 -1)
📝 nvitop/api/device.py (+2 -2)
📝 nvitop/api/process.py (+17 -9)
📝 nvitop/tui/library/process.py (+16 -2)

📄 Description

Problem

HostProcess caches one instance per PID (HostProcess.INSTANCES), and every screen shares that instance. The TUI runs several snapshot daemons concurrently: the main process panel keeps sampling while the process-metrics screen (Enter) and the treeview (t) are open, and those screens sample the same cached GpuProcess / HostProcess.

psutil.Process.cpu_percent(interval=None) is stateful: it reports the utilization since the previous call on the same instance and resets its baseline on every call. When two daemons sample concurrently, the first one measures the elapsed window (the real value), while the others measure a near-zero window. On Windows GetProcessTimes only advances every ~15.6 ms, so that window reads a zero delta and returns exactly 0.0%. The visible symptom is a strict 0% / real-value alternation in the process-metrics CPU graph and in the treeview %CPU column.

Fixes #236.

Change

  • nvitop/api/process.py: add GpuProcess.HOST_PROCESS_CLASS (defaults to HostProcess) and use it when creating the host process, following the Device.GPU_PROCESS_CLASS pattern.
  • nvitop/tui/library/process.py: subclass HostProcess and cache cpu_percent with ttl_cache(ttl=0.1) (the minimum sampling interval recommended by psutil). The TUI GpuProcess sets HOST_PROCESS_CLASS to this subclass.

The API classes keep their previous behaviour; only the TUI layer caches the sample.

Verification

Minimal repro (two threads sampling one cached HostProcess):

  • before: strict alternation, e.g. thread-1=100.0% thread-2=0.0%
  • after: both threads report 100.0% on every sample

Also checked on Windows Server 2025 / Python 3.12 / psutil 7.2.2:

  • GpuProcess(pid, device).host is the TUI HostProcess subclass
  • python -m nvitop --once still renders
  • ruff check, ruff format --check, pylint pass on the changed modules

The first non-blocking call on a freshly created instance still returns 0.0, which is psutil's documented behaviour and is unchanged.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/XuehaiPan/nvitop/pull/237 **Author:** [@arinawang11](https://github.com/arinawang11) **Created:** 10/31/2026 **Status:** ✅ Merged **Merged:** 10/31/2026 **Merged by:** [@XuehaiPan](https://github.com/XuehaiPan) **Base:** `main` ← **Head:** `fix/cpu-percent-sampling-race` --- ### 📝 Commits (4) - [`41cf5c6`](https://github.com/XuehaiPan/nvitop/commit/41cf5c6d98112823446e1144591a18a66e6453c8) fix(tui/process): cache non-blocking `cpu_percent` samples - [`0bea27e`](https://github.com/XuehaiPan/nvitop/commit/0bea27ecb977904dee23e6dc51b958dba3f9fee7) docs(tui/process): clarify CPU sampling cache rationale - [`5076387`](https://github.com/XuehaiPan/nvitop/commit/5076387a6281ac2a0f8394ad2abe7c8e2ce1a206) docs(changelog): note TUI CPU sampling fix - [`e7d44e2`](https://github.com/XuehaiPan/nvitop/commit/e7d44e2f0aa1cf43b2bbddf524458d786ff4c43c) fix(process): preserve subclasses and enlarge CPU sample cache ### 📊 Changes **4 files changed** (+36 additions, -14 deletions) <details> <summary>View changed files</summary> 📝 `CHANGELOG.md` (+1 -1) 📝 `nvitop/api/device.py` (+2 -2) 📝 `nvitop/api/process.py` (+17 -9) 📝 `nvitop/tui/library/process.py` (+16 -2) </details> ### 📄 Description ## Problem `HostProcess` caches one instance per PID (`HostProcess.INSTANCES`), and every screen shares that instance. The TUI runs several snapshot daemons concurrently: the main process panel keeps sampling while the process-metrics screen (`Enter`) and the treeview (`t`) are open, and those screens sample the same cached `GpuProcess` / `HostProcess`. `psutil.Process.cpu_percent(interval=None)` is stateful: it reports the utilization since the previous call on the *same instance* and resets its baseline on every call. When two daemons sample concurrently, the first one measures the elapsed window (the real value), while the others measure a near-zero window. On Windows `GetProcessTimes` only advances every ~15.6 ms, so that window reads a zero delta and returns exactly `0.0%`. The visible symptom is a strict 0% / real-value alternation in the process-metrics CPU graph and in the treeview `%CPU` column. Fixes #236. ## Change - `nvitop/api/process.py`: add `GpuProcess.HOST_PROCESS_CLASS` (defaults to `HostProcess`) and use it when creating the host process, following the `Device.GPU_PROCESS_CLASS` pattern. - `nvitop/tui/library/process.py`: subclass `HostProcess` and cache `cpu_percent` with `ttl_cache(ttl=0.1)` (the minimum sampling interval recommended by psutil). The TUI `GpuProcess` sets `HOST_PROCESS_CLASS` to this subclass. The API classes keep their previous behaviour; only the TUI layer caches the sample. ## Verification Minimal repro (two threads sampling one cached `HostProcess`): - before: strict alternation, e.g. `thread-1=100.0% thread-2=0.0%` - after: both threads report `100.0%` on every sample Also checked on Windows Server 2025 / Python 3.12 / psutil 7.2.2: - `GpuProcess(pid, device).host` is the TUI `HostProcess` subclass - `python -m nvitop --once` still renders - `ruff check`, `ruff format --check`, `pylint` pass on the changed modules The first non-blocking call on a freshly created instance still returns `0.0`, which is psutil's documented behaviour and is unchanged. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
gitea-mirror 2026-10-31 22:03:36 -06:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
github-starred/nvitop#236
No description provided.