blob: 3cb4af63026804ce7fa7a244cbda2b7ae7620158 [file] [edit]
Performance
===========
This page describes how to use psutil efficiently.
.. _perf-oneshot:
Use oneshot() when reading multiple process attributes
------------------------------------------------------
If you're dealing with a single :class:`Process` instance and need to retrieve
multiple process attributes, use :meth:`Process.oneshot`. Each method call
issues a separate system call, but the OS often returns multiple attributes at
once, which :meth:`Process.oneshot` caches for subsequent calls.
Slow:
.. code-block:: python
import psutil
p = psutil.Process()
p.name() # syscall
p.cpu_times() # syscall
p.memory_info() # syscall
p.status() # syscall
Fast:
.. code-block:: python
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 :meth:`Process.oneshot`.
.. _perf-process-iter:
Use process_iter() with an attrs list
--------------------------------------
If you iterate over multiple PIDs, always use :func:`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:
.. code-block:: python
import psutil
for p in psutil.process_iter():
try:
print(p.pid, p.name(), p.status())
except (psutil.NoSuchProcess, psutil.AccessDenied):
pass
Fast:
.. code-block:: python
import psutil
for p in psutil.process_iter(["name", "status"]):
print(p.pid, p.name(), p.status()) # return cached values, never raise
:func:`process_iter(attrs=...) <psutil.process_iter>` is effectively equivalent
to using :meth:`Process.oneshot` on each process. Using :func:`process_iter`
also saves you from **race conditions** (e.g. if a process disappears while
iterating), since :exc:`NoSuchProcess` and :exc:`AccessDenied` exceptions are
handled internally. A typical use case is to fetch all process attrs except the
slow ones (see :ref:`perf-api-speed` table below):
.. code-block:: python
import psutil
for p in psutil.process_iter(psutil.Process.attrs - {"memory_footprint", "memory_maps"}):
...
.. _perf-oneshot-methods:
Methods sped up by oneshot()
----------------------------
Here's a list of method groups for each platform which can benefit from
:meth:`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 :src:`scripts/internal/bench_oneshot.py`.
Additionally, some methods are computed from other methods, so on every
platform :meth:`Process.oneshot` also speeds up :meth:`~Process.cpu_percent`
(from :meth:`~Process.cpu_times`), :meth:`~Process.memory_percent` (from
:meth:`~Process.memory_info`), :meth:`~Process.parent` and
:meth:`~Process.parents` (from :meth:`~Process.ppid`) and, on POSIX,
:meth:`~Process.username` (from :meth:`~Process.uids`).
Linux
"""""
* :meth:`~Process.cpu_num`, :meth:`~Process.cpu_times`,
:meth:`~Process.create_time`, :meth:`~Process.name`,
:meth:`~Process.page_faults`, :meth:`~Process.ppid`,
:meth:`~Process.status`, :meth:`~Process.terminal`
* :meth:`~Process.gids`, :meth:`~Process.memory_extras`,
:meth:`~Process.num_ctx_switches`, :meth:`~Process.num_threads`,
:meth:`~Process.uids`
* :meth:`~Process.memory_footprint`, :meth:`~Process.memory_maps`
*Speedup: +1.7×*
Windows
"""""""
* :meth:`~Process.cpu_times`, :meth:`~Process.io_counters`,
:meth:`~Process.memory_info`, :meth:`~Process.memory_extras`,
:meth:`~Process.num_ctx_switches`, :meth:`~Process.num_handles`,
:meth:`~Process.num_threads`, :meth:`~Process.page_faults`,
:meth:`~Process.status`
* :meth:`~Process.exe`, :meth:`~Process.name`
Some of these first try a faster dedicated call, and only use the shared one if
it raises :exc:`AccessDenied`. The second figure is for such processes.
*Speedup: +1.8× / +6.5×*
macOS
"""""
* :meth:`~Process.cpu_times`, :meth:`~Process.memory_info`,
:meth:`~Process.num_ctx_switches`, :meth:`~Process.num_threads`,
:meth:`~Process.page_faults`
* :meth:`~Process.create_time`, :meth:`~Process.gids`, :meth:`~Process.name`,
:meth:`~Process.ppid`, :meth:`~Process.status`, :meth:`~Process.terminal`,
:meth:`~Process.uids`
*Speedup: +1.6×*
BSD
"""
* :meth:`~Process.cpu_num`, :meth:`~Process.cpu_times`,
:meth:`~Process.create_time`, :meth:`~Process.gids`,
:meth:`~Process.io_counters`, :meth:`~Process.memory_info`,
:meth:`~Process.name`, :meth:`~Process.nice`,
:meth:`~Process.num_ctx_switches`, :meth:`~Process.page_faults`,
:meth:`~Process.ppid`, :meth:`~Process.status`, :meth:`~Process.terminal`,
:meth:`~Process.uids`
*Speedup: +2.7×*
.. _perf-oneshot-bench:
Measuring oneshot() speedup
---------------------------
:src:`scripts/internal/bench_oneshot.py` measures :meth:`Process.oneshot`
speedup. It also shows which APIs share the same internal kernel routines. E.g.
on Linux:
.. code-block:: none
$ 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
.. _perf-api-speed:
Measuring APIs speed
--------------------
:src:`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:
.. code-block:: none
$ 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