| 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 |