[PR #238] fix(api/host): avoid garbage interrupt/dpc times in host CPU percent #237

Open
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/238
Author: @arinawang11
Created: 10/31/2026
Status: 🔄 Open

Base: main ← Head: fix/host-cpu-percent-windows


📝 Commits (1)

  • b757a3a fix(api/host): avoid garbage interrupt/dpc times in host CPU percent

📊 Changes

2 files changed (+39 additions, -0 deletions)

View changed files

📝 docs/source/spelling_wordlist.txt (+2 -0)
📝 nvitop/api/host.py (+37 -0)

📄 Description

Problem

On Windows machines with more than 64 logical CPUs the host CPU percentage occasionally jumps to 100% while the machine is nearly idle, making the TUI graph oscillate between 100% and 0%.

Root cause

psutil.cpu_percent() computes the busy time as user + system + interrupt + dpc. On Windows:

  • user / system / idle come from GetSystemTimes() (one aggregate call).
  • interrupt / dpc are summed from cext.per_cpu_times(), which fills a buffer through NtQuerySystemInformation(SystemProcessorPerformanceInformation). That API only fills the entries of the calling thread's processor group (at most 64 CPUs); on larger machines the remaining entries are never written and are read uninitialized.

Measured on a 96-logical-CPU Windows Server 2025 host with psutil 7.2.2:

>>> psutil.cpu_times()
scputimes(user=4355.5, system=542.7, idle=105646.5,
          interrupt=7262599160722.1, dpc=7579259287143.7)  # seconds

Whenever those values change between two samples, the busy delta dwarfs the real one and cpu_percent() returns 100.0. A 15 s run of the TUI produced four such samples.

Upstream: psutil#2972 (uninitialized memory, fixed on master by 3ea90fd9, not part of a release yet) and psutil#3061 (per-CPU APIs only cover one processor group, still open).

Fix

On Windows, compute the host utilization from user + system over user + system + idle. psutil.cpu_times().system is the kernel time minus idle and already includes interrupt/DPC time, so the result is complete and independent of the broken fields. It also removes the existing double counting of interrupt/DPC on healthy Windows machines and matches the \Processor Information(_Total)\% Processor Time counter (Task Manager's default % Processor Utility is additionally frequency-weighted, so small differences remain expected).

The baseline is kept per thread like psutil.cpu_percent(), the first call in each thread returns 0.0, and percpu=True / blocking interval calls are still delegated to psutil.

Placing the workaround in nvitop.api.host fixes every consumer at once (TUI host panel, nvitop --metrics collector, nvitop-exporter); other platforms are untouched.

Affected scope

Dimension Affected
OS Windows only (interrupt / dpc only exist there)
CPU More than 64 logical CPUs (multiple processor groups)
psutil 7.2.2, the latest release, still affected

Verification

On the 96-logical-CPU machine:

  • Before: 4 spurious 100.0 samples in 15 s (logged from host-snapshot-daemon).
  • After: 0 samples >= 99.9 in 20 s; values 0.4%–5.6%, consistent with the % Processor Time counter.
  • ruff check and ruff format --check pass.

dpc and uninitialized were added to the pylint spelling wordlist.


🔄 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/238 **Author:** [@arinawang11](https://github.com/arinawang11) **Created:** 10/31/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `fix/host-cpu-percent-windows` --- ### 📝 Commits (1) - [`b757a3a`](https://github.com/XuehaiPan/nvitop/commit/b757a3ac49b031f8337da71d0e3e4cf0cc0827e3) fix(api/host): avoid garbage `interrupt`/`dpc` times in host CPU percent ### 📊 Changes **2 files changed** (+39 additions, -0 deletions) <details> <summary>View changed files</summary> 📝 `docs/source/spelling_wordlist.txt` (+2 -0) 📝 `nvitop/api/host.py` (+37 -0) </details> ### 📄 Description ## Problem On Windows machines with more than 64 logical CPUs the host CPU percentage occasionally jumps to 100% while the machine is nearly idle, making the TUI graph oscillate between 100% and 0%. ## Root cause `psutil.cpu_percent()` computes the busy time as `user + system + interrupt + dpc`. On Windows: - `user` / `system` / `idle` come from `GetSystemTimes()` (one aggregate call). - `interrupt` / `dpc` are summed from `cext.per_cpu_times()`, which fills a buffer through `NtQuerySystemInformation(SystemProcessorPerformanceInformation)`. That API only fills the entries of the calling thread's processor group (at most 64 CPUs); on larger machines the remaining entries are never written and are read uninitialized. Measured on a 96-logical-CPU Windows Server 2025 host with psutil 7.2.2: ```python >>> psutil.cpu_times() scputimes(user=4355.5, system=542.7, idle=105646.5, interrupt=7262599160722.1, dpc=7579259287143.7) # seconds ``` Whenever those values change between two samples, the busy delta dwarfs the real one and `cpu_percent()` returns `100.0`. A 15 s run of the TUI produced four such samples. Upstream: psutil#2972 (uninitialized memory, fixed on `master` by `3ea90fd9`, not part of a release yet) and psutil#3061 (per-CPU APIs only cover one processor group, still open). ## Fix On Windows, compute the host utilization from `user + system` over `user + system + idle`. `psutil.cpu_times().system` is the kernel time minus idle and already includes interrupt/DPC time, so the result is complete and independent of the broken fields. It also removes the existing double counting of interrupt/DPC on healthy Windows machines and matches the `\Processor Information(_Total)\% Processor Time` counter (Task Manager's default `% Processor Utility` is additionally frequency-weighted, so small differences remain expected). The baseline is kept per thread like `psutil.cpu_percent()`, the first call in each thread returns `0.0`, and `percpu=True` / blocking `interval` calls are still delegated to psutil. Placing the workaround in `nvitop.api.host` fixes every consumer at once (TUI host panel, `nvitop --metrics` collector, nvitop-exporter); other platforms are untouched. ## Affected scope | Dimension | Affected | |---|---| | OS | Windows only (`interrupt` / `dpc` only exist there) | | CPU | More than 64 logical CPUs (multiple processor groups) | | psutil | 7.2.2, the latest release, still affected | ## Verification On the 96-logical-CPU machine: - Before: 4 spurious `100.0` samples in 15 s (logged from `host-snapshot-daemon`). - After: 0 samples `>= 99.9` in 20 s; values 0.4%–5.6%, consistent with the `% Processor Time` counter. - `ruff check` and `ruff format --check` pass. `dpc` and `uninitialized` were added to the pylint spelling wordlist. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
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#237
No description provided.