| 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() |
| ---------------------------- |
| |
| The table below lists methods that benefit from :meth:`Process.oneshot`, |
| grouped by platform. Methods separated by an empty row share the same |
| underlying system call. The *speedup* row estimates the gain when all listed |
| methods are called together (best case), as measured by |
| :src:`scripts/internal/bench_oneshot.py`. |
| |
| .. list-table:: |
| :header-rows: 1 |
| :class: wide-table |
| |
| * - Linux |
| - Windows |
| - macOS |
| - BSD |
| * - :meth:`~Process.cpu_num` |
| - :meth:`~Process.cpu_percent` |
| - :meth:`~Process.cpu_percent` |
| - :meth:`~Process.cpu_num` |
| * - :meth:`~Process.cpu_percent` |
| - :meth:`~Process.cpu_times` |
| - :meth:`~Process.cpu_times` |
| - :meth:`~Process.cpu_percent` |
| * - :meth:`~Process.cpu_times` |
| - :meth:`~Process.io_counters` |
| - :meth:`~Process.memory_info` |
| - :meth:`~Process.cpu_times` |
| * - :meth:`~Process.create_time` |
| - :meth:`~Process.memory_info` |
| - :meth:`~Process.memory_percent` |
| - :meth:`~Process.create_time` |
| * - :meth:`~Process.name` |
| - :meth:`~Process.memory_info_ex` |
| - :meth:`~Process.num_ctx_switches` |
| - :meth:`~Process.gids` |
| * - :meth:`~Process.page_faults` |
| - :meth:`~Process.num_ctx_switches` |
| - :meth:`~Process.num_threads` |
| - :meth:`~Process.io_counters` |
| * - :meth:`~Process.ppid` |
| - :meth:`~Process.num_handles` |
| - |
| - :meth:`~Process.name` |
| * - :meth:`~Process.status` |
| - :meth:`~Process.num_threads` |
| - :meth:`~Process.create_time` |
| - :meth:`~Process.memory_info` |
| * - :meth:`~Process.terminal` |
| - |
| - :meth:`~Process.gids` |
| - :meth:`~Process.memory_percent` |
| * - |
| - :meth:`~Process.exe` |
| - :meth:`~Process.name` |
| - :meth:`~Process.num_ctx_switches` |
| * - :meth:`~Process.gids` |
| - :meth:`~Process.name` |
| - :meth:`~Process.ppid` |
| - :meth:`~Process.ppid` |
| * - :meth:`~Process.memory_info_ex` |
| - |
| - :meth:`~Process.status` |
| - :meth:`~Process.status` |
| * - :meth:`~Process.num_ctx_switches` |
| - |
| - :meth:`~Process.terminal` |
| - :meth:`~Process.terminal` |
| * - :meth:`~Process.num_threads` |
| - |
| - :meth:`~Process.terminal` |
| - :meth:`~Process.terminal` |
| * - :meth:`~Process.uids` |
| - |
| - :meth:`~Process.uids` |
| - :meth:`~Process.uids` |
| * - :meth:`~Process.username` |
| - |
| - :meth:`~Process.username` |
| - :meth:`~Process.username` |
| * - |
| - |
| - |
| - |
| * - :meth:`~Process.memory_footprint` |
| - |
| - |
| - |
| * - :meth:`~Process.memory_maps` |
| - |
| - |
| - |
| * - *speedup: +1.8x* |
| - *speedup: +1.8x / +6.5x* |
| - *speedup: +1.9x* |
| - *speedup: +2.0x* |
| |
| .. _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_info |
| memory_info_ex |
| memory_percent |
| name |
| num_ctx_switches |
| num_threads |
| page_faults |
| parent |
| ppid |
| status |
| terminal |
| uids |
| username |
| |
| regular: 2.766 secs |
| oneshot: 1.537 secs |
| speedup: +1.80x |
| |
| .. _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_trim 300 0.00027 |
| heap_info 300 0.00028 |
| cpu_count 300 0.00066 |
| disk_usage 300 0.00071 |
| pid_exists 300 0.00249 |
| users 300 0.00394 |
| cpu_times 300 0.00647 |
| virtual_memory 300 0.00648 |
| boot_time 300 0.00727 |
| cpu_percent 300 0.00745 |
| net_io_counters 300 0.00754 |
| cpu_times_percent 300 0.00870 |
| net_if_addrs 300 0.01156 |
| cpu_stats 300 0.01195 |
| swap_memory 300 0.01292 |
| net_if_stats 300 0.01360 |
| disk_partitions 300 0.01696 |
| disk_io_counters 300 0.02583 |
| sensors_battery 300 0.03103 |
| pids 300 0.04896 |
| cpu_count (cores) 300 0.07208 |
| process_iter (all) 300 0.07900 |
| cpu_freq 300 0.15635 |
| sensors_fans 300 0.75810 |
| net_connections 224 2.00111 |
| sensors_temperatures 81 2.00266 |
| |
| PROCESS APIS NUM CALLS SECONDS |
| ------------------------------------------------- |
| create_time 300 0.00013 |
| exe 300 0.00016 |
| nice 300 0.00024 |
| ionice 300 0.00039 |
| cwd 300 0.00052 |
| cpu_affinity 300 0.00057 |
| num_fds 300 0.00100 |
| memory_info 300 0.00208 |
| io_counters 300 0.00229 |
| cmdline 300 0.00232 |
| cpu_num 300 0.00254 |
| terminal 300 0.00255 |
| status 300 0.00258 |
| page_faults 300 0.00259 |
| name 300 0.00261 |
| memory_percent 300 0.00265 |
| cpu_times 300 0.00278 |
| threads 300 0.00300 |
| gids 300 0.00304 |
| num_threads 300 0.00305 |
| num_ctx_switches 300 0.00308 |
| uids 300 0.00321 |
| cpu_percent 300 0.00372 |
| net_connections 300 0.00376 |
| open_files 300 0.00453 |
| username 300 0.00505 |
| ppid 300 0.00554 |
| memory_info_ex 300 0.00651 |
| environ 300 0.01013 |
| memory_footprint 300 0.02241 |
| memory_maps 300 0.30282 |
| |