summaryrefslogtreecommitdiff
path: root/scripts/internal/print_api_speed.py
blob: e85b7038203d9ab4581c27816099a0ad73382f8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
#!/usr/bin/env python3

# Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
Benchmark all API calls and print them from fastest to slowest.

$ make print_api_speed
SYSTEM APIS                NUM CALLS      SECONDS
-------------------------------------------------
disk_usage                       300      0.00157
cpu_count                        300      0.00255
pid_exists                       300      0.00792
cpu_times                        300      0.01044
boot_time                        300      0.01136
cpu_percent                      300      0.01290
cpu_times_percent                300      0.01515
virtual_memory                   300      0.01594
users                            300      0.01964
net_io_counters                  300      0.02027
cpu_stats                        300      0.02034
net_if_addrs                     300      0.02962
swap_memory                      300      0.03209
sensors_battery                  300      0.05186
pids                             300      0.07954
net_if_stats                     300      0.09321
disk_io_counters                 300      0.09406
cpu_count (cores)                300      0.10293
disk_partitions                  300      0.10345
cpu_freq                         300      0.20817
sensors_fans                     300      0.63476
sensors_temperatures             231      2.00039
process_iter (all)               171      2.01300
net_connections                   97      2.00206

PROCESS APIS               NUM CALLS      SECONDS
-------------------------------------------------
create_time                      300      0.00009
exe                              300      0.00015
nice                             300      0.00057
ionice                           300      0.00091
cpu_affinity                     300      0.00091
cwd                              300      0.00151
num_fds                          300      0.00391
memory_info                      300      0.00597
memory_percent                   300      0.00648
io_counters                      300      0.00707
name                             300      0.00894
status                           300      0.00900
ppid                             300      0.00906
num_threads                      300      0.00932
cpu_num                          300      0.00933
num_ctx_switches                 300      0.00943
uids                             300      0.00979
gids                             300      0.01002
cpu_times                        300      0.01008
cmdline                          300      0.01009
terminal                         300      0.01059
is_running                       300      0.01063
threads                          300      0.01209
connections                      300      0.01276
cpu_percent                      300      0.01463
open_files                       300      0.01630
username                         300      0.01655
environ                          300      0.02250
memory_full_info                 300      0.07066
memory_maps                      300      0.74281
"""

from __future__ import division
from __future__ import print_function

import argparse
import inspect
import os
import sys
from timeit import default_timer as timer

import psutil
from psutil._common import print_color


TIMES = 300
timings = []
templ = "%-25s %10s   %10s"


def print_header(what):
    s = templ % (what, "NUM CALLS", "SECONDS")
    print_color(s, color=None, bold=True)
    print("-" * len(s))


def print_timings():
    timings.sort(key=lambda x: (x[1], -x[2]), reverse=True)
    i = 0
    while timings[:]:
        title, times, elapsed = timings.pop(0)
        s = templ % (title, str(times), "%.5f" % elapsed)
        if i > len(timings) - 5:
            print_color(s, color="red")
        else:
            print(s)


def timecall(title, fun, *args, **kw):
    print("%-50s" % title, end="")
    sys.stdout.flush()
    t = timer()
    for n in range(TIMES):
        fun(*args, **kw)
        elapsed = timer() - t
        if elapsed > 2:
            break
    print("\r", end="")
    sys.stdout.flush()
    timings.append((title, n + 1, elapsed))


def set_highest_priority():
    """Set highest CPU and I/O priority (requires root)."""
    p = psutil.Process()
    if psutil.WINDOWS:
        p.nice(psutil.HIGH_PRIORITY_CLASS)
    else:
        p.nice(-20)

    if psutil.LINUX:
        p.ionice(psutil.IOPRIO_CLASS_RT, value=7)
    elif psutil.WINDOWS:
        p.ionice(psutil.IOPRIO_HIGH)


def main():
    global TIMES

    parser = argparse.ArgumentParser(
        description=__doc__, formatter_class=argparse.RawTextHelpFormatter)
    parser.add_argument('-t', '--times', type=int, default=TIMES)
    args = parser.parse_args()
    TIMES = args.times
    assert TIMES > 1, TIMES

    try:
        set_highest_priority()
    except psutil.AccessDenied:
        prio_set = False
    else:
        prio_set = True

    # --- system

    public_apis = []
    ignore = ['wait_procs', 'process_iter', 'win_service_get',
              'win_service_iter']
    if psutil.MACOS:
        ignore.append('net_connections')  # raises AD
    for name in psutil.__all__:
        obj = getattr(psutil, name, None)
        if inspect.isfunction(obj):
            if name not in ignore:
                public_apis.append(name)

    print_header("SYSTEM APIS")
    for name in public_apis:
        fun = getattr(psutil, name)
        args = ()
        if name == 'pid_exists':
            args = (os.getpid(), )
        elif name == 'disk_usage':
            args = (os.getcwd(), )
        timecall(name, fun, *args)
    timecall('cpu_count (cores)', psutil.cpu_count, logical=False)
    timecall('process_iter (all)', lambda: list(psutil.process_iter()))
    print_timings()

    # --- process
    print("")
    print_header("PROCESS APIS")
    ignore = ['send_signal', 'suspend', 'resume', 'terminate', 'kill', 'wait',
              'as_dict', 'parent', 'parents', 'memory_info_ex', 'oneshot',
              'pid', 'rlimit', 'children']
    if psutil.MACOS:
        ignore.append('memory_maps')  # XXX
    p = psutil.Process()
    for name in sorted(dir(p)):
        if not name.startswith('_') and name not in ignore:
            fun = getattr(p, name)
            timecall(name, fun)
    print_timings()

    if not prio_set:
        print_color("\nWARN: couldn't set highest process priority " +
                    "(requires root)", "red")


if __name__ == '__main__':
    main()