os.cpu_count
cpu_count() counts the logical CPUs of the machine; process_cpu_count() (new in 3.13) counts the ones this process may actually run on, which can be fewer under CPU affinity. Both can return None. getloadavg() is Unix only; times() works on Unix and Windows, but on Windows only user and system are filled in.
Common call
workers = os.process_cpu_count() or 1
Returns
int, or None when undetermined
Replaces
multiprocessing.cpu_count() (which raises instead of returning None)
Watch out
None is a possible result: always add "or 1"
os.cpu_count() / os.process_cpu_count() / os.getloadavg() / os.times()
→ int | None
Common patterns
Pool size that works on 3.12 and 3.13
Prefer the usable-CPU count; fall back to the machine count; never None.
import os count = getattr(os, 'process_cpu_count', os.cpu_count)() or 1 workers = max(1, count - 1)
Usable CPUs before 3.13 on Linux
sched_getaffinity(0) is the set of CPUs this process may run on.
import os usable = len(os.sched_getaffinity(0))
Load average per CPU (Unix)
A 1-minute load above the CPU count means work is queueing.
import os one, five, fifteen = os.getloadavg() busy = one / (os.cpu_count() or 1) > 1.0
CPU time spent by a block
user + system is CPU time; elapsed is wall-clock time.
import os t0 = os.times() work() t1 = os.times() cpu = (t1.user - t0.user) + (t1.system - t0.system) wall = t1.elapsed - t0.elapsed
Examples
1. An int or None, never 0
import os
c = os.cpu_count()
c is None or (isinstance(c, int) and c >= 1)
Returns
True2. Usable CPUs never exceed the total
import os
usable = len(os.sched_getaffinity(0)) if hasattr(os, 'sched_getaffinity') else os.cpu_count()
1 <= usable <= os.cpu_count()
Returns
True3. times() is a 5-field struct sequence
import os
t = os.times()
(type(t).__name__, len(t), os.times_result.n_fields)
Returns
('times_result', 5, 5)4. Fields by name or by position
import os
t = os.times()
t.user == t[0] and t.elapsed == t[4]
Returns
True5. Building a times_result by hand
import os
t = os.times_result((1.5, 0.25, 0.0, 0.0, 100.0))
t.user + t.system
Returns
1.756. elapsed only moves forward
import os
t0 = os.times()
sum(i * i for i in range(100000))
t1 = os.times()
t1.elapsed >= t0.elapsed
Returns
True7. getloadavg exists only on Unix
import os
hasattr(os, 'getloadavg') == (os.name == 'posix')
Returns
TruePitfalls
1. Doing arithmetic on a None CPU count
cpu_count() returns None when the count is undetermined. The typical "cores minus one" line then crashes; "or 1" makes it safe.
count - 1
count = None # what os.cpu_count() returns when undetermined count - 1
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'
(count or 1)
count = None max(1, (count or 1) - 1)
1
2. Sizing a pool by the machine instead of the process
In a container or under taskset the process may be limited to a few CPUs while cpu_count() still reports all of them. Ask for the usable set.
cpu_count()
import os isinstance(os.cpu_count(), int)
True
usable CPUs
import os usable = (getattr(os, 'process_cpu_count', None) or os.cpu_count)() usable <= os.cpu_count()
True
When to use
Use it
- Choosing a default worker count for a thread or process pool
- Measuring CPU time (user + system) of your own process with times()
- Quick load checks on Unix servers with getloadavg()
Reach for something else
- Timing code precisely → time.perf_counter() or time.process_time()
- Per-process CPU and memory of other processes → the third-party psutil package
Notes
CPython impl
cpu_count and process_cpu_count honour the -X cpu_count=n option and the PYTHON_CPU_COUNT environment variable (3.13+), which override the detected value
Availability
cpu_count, times: Unix, Windows. getloadavg: Unix only (raises OSError if the load average is unobtainable). process_cpu_count: added in 3.13, not listed in os.__all__
Windows times()
Only user and system are known; children_user, children_system and elapsed are 0
times_result
A tuple-like struct sequence: user, system, children_user, children_system, elapsed (named attributes since 3.3)
FAQ
CPU counts, load averages and CPU times are properties of the machine running the code, so they differ for every reader. The examples check types and relations that hold everywhere. getloadavg is Unix only and does not exist on Windows.