Skip to content

perf: Process.cpu_percent() no longer calls cpu_count() - #2955

Open
Dipet wants to merge 1 commit into
giampaolo:masterfrom
Dipet:perf/cpu-percent-drop-redundant-cpu-count
Open

perf: Process.cpu_percent() no longer calls cpu_count()#2955
Dipet wants to merge 1 commit into
giampaolo:masterfrom
Dipet:perf/cpu-percent-drop-redundant-cpu-count

Conversation

@Dipet

@Dipet Dipet commented Aug 5, 2026

Copy link
Copy Markdown

Summary

  • OS: Linux (the change itself is platform-independent)
  • Bug fix: no
  • Type: performance
  • Fixes:

Description

Process.cpu_percent() calls cpu_count() on every invocation, but the value cancels out algebraically:

num_cpus = cpu_count() or 1

def timer():
    return _timer() * num_cpus
...
delta_time = st2 - st1                                  # == num_cpus * (t2 - t1)
overall_cpus_percent = (delta_proc / delta_time) * 100  # divides by num_cpus
single_cpu_percent = overall_cpus_percent * num_cpus    # multiplies it back

num_cpus appears once in the denominator and once in the numerator, so the returned value never depends on it. Verified empirically by forcing cpu_count() to return 1, 8, 128 and 1000 while sampling a busy-loop process: the result is 100.0 in all four cases.

On Linux cpu_count_logical() is a sysconf(SC_NPROCESSORS_ONLN) call, so iterating over all processes pays it once per process. On a host with ~15 300 processes:

process_iter() with 12 attrs min
master 2732.6 ms
this PR 2559.7 ms

posix.sysconf disappears from the profile entirely — 15 242 calls → 0.

This also removes a latent correctness issue. In the non-blocking form st1 comes from a previous call. If the logical CPU count changed in between (CPU hotplug), st1 and st2 were scaled by different factors and the returned percentage was wrong. Without the factor the two samples are always comparable.

test_cpu_percent_numcpus_none asserted that cpu_count had been called, an implementation detail that no longer exists. It now asserts the guarantee issue #1087 actually asked for: cpu_percent() works when cpu_count() returns None. The or 1 guard is no longer needed since nothing divides by it any more.

Tests: tests/test_process.py tests/test_system.py tests/test_misc.py tests/test_contracts.py → 249 passed. test_long_name and test_memory_maps_lists_lib fail identically on unmodified master in this environment, so they are unrelated to this change.

@github-actions github-actions Bot added the tests component : tests/ - the suite itself, not a bug it happens to catch label Aug 5, 2026
@giampaolo giampaolo added enhancement performance anything that speeds up execution of any component (core, tests, ci scripts, build, ...) and removed tests component : tests/ - the suite itself, not a bug it happens to catch labels Aug 5, 2026
@Dipet
Dipet force-pushed the perf/cpu-percent-drop-redundant-cpu-count branch from 5039066 to bcd619e Compare August 7, 2026 15:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance anything that speeds up execution of any component (core, tests, ci scripts, build, ...)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants