Performance¶
This page describes how to use psutil efficiently.
Use oneshot() when reading multiple process attributes¶
If you’re dealing with a single Process instance and need to retrieve
multiple process attributes, use Process.oneshot(). Each method call
issues a separate system call, but the OS often returns multiple attributes at
once, which Process.oneshot() caches for subsequent calls.
Slow:
import psutil
p = psutil.Process()
p.name() # syscall
p.cpu_times() # syscall
p.memory_info() # syscall
p.status() # syscall
Fast:
import psutil
p = psutil.Process()
with p.oneshot():
p.name() # one syscall, result cached
p.cpu_times() # from cache
p.memory_info() # from cache
p.status() # from cache
The speed improvement depends on the platform and on how many attributes you
read. On Linux the gain is typically around 1.5x–2x; on Windows it can be much
higher. As a rule of thumb: if you read more than one attribute from the same
process, use Process.oneshot().
Use process_iter() with an attrs list¶
If you iterate over multiple PIDs, always use process_iter(). It accepts
an attrs argument that pre-fetches only the requested attributes in a
single pass, minimizing system calls by fetching multiple attributes at once.
This is faster than calling individual methods in a loop.
Slow:
import psutil
for p in psutil.process_iter():
try:
print(p.pid, p.name(), p.status())
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
Fast:
process_iter(attrs=...) is effectively equivalent
to using Process.oneshot() on each process. Using process_iter()
also saves you from race conditions (e.g. if a process disappears while
iterating), since NoSuchProcess and AccessDenied exceptions are
handled internally. A typical use case is to fetch all process attrs except the
slow ones (see Measuring APIs speed table below):
import psutil
for p in psutil.process_iter(psutil.Process.attrs - {"memory_footprint", "memory_maps"}):
...
Methods sped up by oneshot()¶
Here’s a list of method groups for each platform which can benefit from
Process.oneshot(). Methods in each group (in the same comma-separated
list) share the same underlying system call.
The speedup represents the estimated gain when all listed methods are called together (best case), as measured by scripts/internal/bench_oneshot.py.
Additionally, some methods are computed from other methods, so on every
platform Process.oneshot() also speeds up cpu_percent()
(from cpu_times()), memory_percent() (from
memory_info()), parent() and
parents() (from ppid()) and, on POSIX,
username() (from uids()).
Linux¶
cpu_num(),cpu_times(),create_time(),name(),page_faults(),ppid(),status(),terminal()gids(),memory_extras(),num_ctx_switches(),num_threads(),uids()
Speedup: +1.7×
Windows¶
cpu_times(),io_counters(),memory_info(),memory_extras(),num_ctx_switches(),num_handles(),num_threads(),page_faults(),status()
Some of these first try a faster dedicated call, and only use the shared one if
it raises AccessDenied. The second figure is for such processes.
Speedup: +1.8× / +6.5×
macOS¶
cpu_times(),memory_info(),num_ctx_switches(),num_threads(),page_faults()create_time(),gids(),name(),ppid(),status(),terminal(),uids()
Speedup: +1.6×
BSD¶
cpu_num(),cpu_times(),create_time(),gids(),io_counters(),memory_info(),name(),nice(),num_ctx_switches(),page_faults(),ppid(),status(),terminal(),uids()
Speedup: +2.7×
Measuring oneshot() speedup¶
scripts/internal/bench_oneshot.py measures Process.oneshot()
speedup. It also shows which APIs share the same internal kernel routines. E.g.
on Linux:
$ python3 scripts/internal/bench_oneshot.py --times 10000
17 methods pre-fetched by oneshot() on platform 'linux' (10,000 times, psutil 8.0.0):
cpu_num
cpu_percent
cpu_times
gids
memory_extras
memory_info
memory_percent
name
num_ctx_switches
num_threads
page_faults
parent
ppid
status
terminal
uids
username
regular: 2.600 secs
oneshot: 1.499 secs
speedup: +1.73x
Measuring APIs speed¶
scripts/internal/print_api_speed.py shows the relative cost of each API call. This helps you understand which operations are more expensive. E.g. on Linux:
$ python3 scripts/internal/print_api_speed.py
SYSTEM APIS NUM CALLS SECONDS
-------------------------------------------------
getloadavg 300 0.00013
heap_info 300 0.00028
heap_trim 300 0.00039
cpu_count 300 0.00061
disk_usage 300 0.00066
pid_exists 300 0.00235
users 300 0.00455
net_io_counters 300 0.00550
cpu_times 300 0.00667
boot_time 300 0.00700
cpu_percent 300 0.00766
net_if_stats 300 0.00783
virtual_memory 300 0.00834
cpu_times_percent 300 0.00885
net_if_addrs 300 0.01157
cpu_stats 300 0.01208
swap_memory 300 0.01558
disk_partitions 300 0.01664
disk_io_counters 300 0.02204
sensors_battery 300 0.02995
pids 300 0.05295
cpu_count (cores) 300 0.06943
process_iter (all) 300 0.08486
cpu_freq 300 0.18987
sensors_fans 300 0.74027
net_connections 161 2.00690
sensors_temperatures 100 2.00742
PROCESS APIS NUM CALLS SECONDS
-------------------------------------------------
exe 300 0.00017
create_time 300 0.00020
nice 300 0.00025
ionice 300 0.00041
cwd 300 0.00052
cpu_affinity 300 0.00059
num_fds 300 0.00097
memory_info 300 0.00201
cmdline 300 0.00222
io_counters 300 0.00226
cpu_num 300 0.00242
status 300 0.00242
terminal 300 0.00243
name 300 0.00249
page_faults 300 0.00258
memory_percent 300 0.00259
cpu_times 300 0.00272
threads 300 0.00278
num_threads 300 0.00278
gids 300 0.00296
num_ctx_switches 300 0.00299
uids 300 0.00311
cpu_percent 300 0.00346
net_connections 300 0.00373
open_files 300 0.00378
memory_extras 300 0.00398
username 300 0.00500
ppid 300 0.00556
environ 300 0.01176
memory_footprint 300 0.02218
memory_maps 300 0.27158