summaryrefslogtreecommitdiffstats
path: root/Tools/pybench/systimes.py
blob: 6bc7e80ef46b4ef39f3dd691d64d96ed7bb635db (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
201
202
203
204
205
206
207
208
209
210
211
212
213
214
#!/usr/bin/env python

""" systimes() user and system timer implementations for use by
    pybench.

    This module implements various different strategies for measuring
    performance timings. It tries to choose the best available method
    based on the platforma and available tools.

    On Windows, it is recommended to have the Mark Hammond win32
    package installed. Alternatively, the Thomas Heller ctypes
    packages can also be used.

    On Unix systems, the standard resource module provides the highest
    resolution timings. Unfortunately, it is not available on all Unix
    platforms.

    If no supported timing methods based on process time can be found,
    the module reverts to the highest resolution wall-clock timer
    instead. The system time part will then always be 0.0.

    The module exports one public API:

    def systimes():

        Return the current timer values for measuring user and system
        time as tuple of seconds (user_time, system_time).

    Copyright (c) 2006, Marc-Andre Lemburg (mal@egenix.com). See the
    documentation for further information on copyrights, or contact
    the author. All Rights Reserved.

"""

from __future__ import print_function

import time, sys

#
# Note: Please keep this module compatible to Python 1.5.2.
#
# TODOs:
#
# * Add ctypes wrapper for new clock_gettime() real-time POSIX APIs;
#   these will then provide nano-second resolution where available.
#
# * Add a function that returns the resolution of systimes()
#   values, ie. systimesres().
#

### Choose an implementation

SYSTIMES_IMPLEMENTATION = None
USE_CTYPES_GETPROCESSTIMES = 'ctypes GetProcessTimes() wrapper'
USE_WIN32PROCESS_GETPROCESSTIMES = 'win32process.GetProcessTimes()'
USE_RESOURCE_GETRUSAGE = 'resource.getrusage()'
USE_PROCESS_TIME_CLOCK = 'time.clock() (process time)'
USE_WALL_TIME_CLOCK = 'time.clock() (wall-clock)'
USE_WALL_TIME_TIME = 'time.time() (wall-clock)'

if sys.platform[:3] == 'win':
    # Windows platform
    try:
        import win32process
    except ImportError:
        try:
            import ctypes
        except ImportError:
            # Use the wall-clock implementation time.clock(), since this
            # is the highest resolution clock available on Windows
            SYSTIMES_IMPLEMENTATION = USE_WALL_TIME_CLOCK
        else:
            SYSTIMES_IMPLEMENTATION = USE_CTYPES_GETPROCESSTIMES
    else:
        SYSTIMES_IMPLEMENTATION = USE_WIN32PROCESS_GETPROCESSTIMES
else:
    # All other platforms
    try:
        import resource
    except ImportError:
        pass
    else:
        SYSTIMES_IMPLEMENTATION = USE_RESOURCE_GETRUSAGE

# Fall-back solution
if SYSTIMES_IMPLEMENTATION is None:
    # Check whether we can use time.clock() as approximation
    # for systimes()
    start = time.clock()
    time.sleep(0.1)
    stop = time.clock()
    if stop - start < 0.001:
        # Looks like time.clock() is usable (and measures process
        # time)
        SYSTIMES_IMPLEMENTATION = USE_PROCESS_TIME_CLOCK
    else:
        # Use wall-clock implementation time.time() since this provides
        # the highest resolution clock on most systems
        SYSTIMES_IMPLEMENTATION = USE_WALL_TIME_TIME

### Implementations

def getrusage_systimes():
    return resource.getrusage(resource.RUSAGE_SELF)[:2]

def process_time_clock_systimes():
    return (time.clock(), 0.0)

def wall_clock_clock_systimes():
    return (time.clock(), 0.0)

def wall_clock_time_systimes():
    return (time.time(), 0.0)

# Number of clock ticks per second for the values returned
# by GetProcessTimes() on Windows.
#
# Note: Ticks returned by GetProcessTimes() are 100ns intervals on
# Windows XP. However, the process times are only updated with every
# clock tick and the frequency of these is somewhat lower: depending
# on the OS version between 10ms and 15ms. Even worse, the process
# time seems to be allocated to process currently running when the
# clock interrupt arrives, ie. it is possible that the current time
# slice gets accounted to a different process.

WIN32_PROCESS_TIMES_TICKS_PER_SECOND = 1e7

def win32process_getprocesstimes_systimes():
    d = win32process.GetProcessTimes(win32process.GetCurrentProcess())
    return (d['UserTime'] / WIN32_PROCESS_TIMES_TICKS_PER_SECOND,
            d['KernelTime'] / WIN32_PROCESS_TIMES_TICKS_PER_SECOND)

def ctypes_getprocesstimes_systimes():
    creationtime = ctypes.c_ulonglong()
    exittime = ctypes.c_ulonglong()
    kerneltime = ctypes.c_ulonglong()
    usertime = ctypes.c_ulonglong()
    rc = ctypes.windll.kernel32.GetProcessTimes(
        ctypes.windll.kernel32.GetCurrentProcess(),
        ctypes.byref(creationtime),
        ctypes.byref(exittime),
        ctypes.byref(kerneltime),
        ctypes.byref(usertime))
    if not rc:
        raise TypeError('GetProcessTimes() returned an error')
    return (usertime.value / WIN32_PROCESS_TIMES_TICKS_PER_SECOND,
            kerneltime.value / WIN32_PROCESS_TIMES_TICKS_PER_SECOND)

# Select the default for the systimes() function

if SYSTIMES_IMPLEMENTATION is USE_RESOURCE_GETRUSAGE:
    systimes = getrusage_systimes

elif SYSTIMES_IMPLEMENTATION is USE_PROCESS_TIME_CLOCK:
    systimes = process_time_clock_systimes

elif SYSTIMES_IMPLEMENTATION is USE_WALL_TIME_CLOCK:
    systimes = wall_clock_clock_systimes

elif SYSTIMES_IMPLEMENTATION is USE_WALL_TIME_TIME:
    systimes = wall_clock_time_systimes

elif SYSTIMES_IMPLEMENTATION is USE_WIN32PROCESS_GETPROCESSTIMES:
    systimes = win32process_getprocesstimes_systimes

elif SYSTIMES_IMPLEMENTATION is USE_CTYPES_GETPROCESSTIMES:
    systimes = ctypes_getprocesstimes_systimes

else:
    raise TypeError('no suitable systimes() implementation found')

def processtime():

    """ Return the total time spent on the process.

        This is the sum of user and system time as returned by
        systimes().

    """
    user, system = systimes()
    return user + system

### Testing

def some_workload():
    x = 0
    for i in range(10000000):
        x = x + 1

def test_workload():
    print('Testing systimes() under load conditions')
    t0 = systimes()
    some_workload()
    t1 = systimes()
    print('before:', t0)
    print('after:', t1)
    print('differences:', (t1[0] - t0[0], t1[1] - t0[1]))
    print()

def test_idle():
    print('Testing systimes() under idle conditions')
    t0 = systimes()
    time.sleep(1)
    t1 = systimes()
    print('before:', t0)
    print('after:', t1)
    print('differences:', (t1[0] - t0[0], t1[1] - t0[1]))
    print()

if __name__ == '__main__':
    print('Using %s as timer' % SYSTIMES_IMPLEMENTATION)
    print()
    test_workload()
    test_idle()