[2] | 1 | #!/usr/bin/env python
|
---|
| 2 |
|
---|
| 3 | """ systimes() user and system timer implementations for use by
|
---|
| 4 | pybench.
|
---|
| 5 |
|
---|
| 6 | This module implements various different strategies for measuring
|
---|
| 7 | performance timings. It tries to choose the best available method
|
---|
[391] | 8 | based on the platform and available tools.
|
---|
[2] | 9 |
|
---|
| 10 | On Windows, it is recommended to have the Mark Hammond win32
|
---|
| 11 | package installed. Alternatively, the Thomas Heller ctypes
|
---|
| 12 | packages can also be used.
|
---|
| 13 |
|
---|
| 14 | On Unix systems, the standard resource module provides the highest
|
---|
| 15 | resolution timings. Unfortunately, it is not available on all Unix
|
---|
| 16 | platforms.
|
---|
| 17 |
|
---|
| 18 | If no supported timing methods based on process time can be found,
|
---|
| 19 | the module reverts to the highest resolution wall-clock timer
|
---|
| 20 | instead. The system time part will then always be 0.0.
|
---|
| 21 |
|
---|
| 22 | The module exports one public API:
|
---|
| 23 |
|
---|
| 24 | def systimes():
|
---|
| 25 |
|
---|
| 26 | Return the current timer values for measuring user and system
|
---|
| 27 | time as tuple of seconds (user_time, system_time).
|
---|
| 28 |
|
---|
| 29 | Copyright (c) 2006, Marc-Andre Lemburg (mal@egenix.com). See the
|
---|
| 30 | documentation for further information on copyrights, or contact
|
---|
| 31 | the author. All Rights Reserved.
|
---|
| 32 |
|
---|
| 33 | """
|
---|
| 34 | import time, sys
|
---|
| 35 |
|
---|
| 36 | #
|
---|
| 37 | # Note: Please keep this module compatible to Python 1.5.2.
|
---|
| 38 | #
|
---|
| 39 | # TODOs:
|
---|
| 40 | #
|
---|
| 41 | # * Add ctypes wrapper for new clock_gettime() real-time POSIX APIs;
|
---|
| 42 | # these will then provide nano-second resolution where available.
|
---|
| 43 | #
|
---|
| 44 | # * Add a function that returns the resolution of systimes()
|
---|
| 45 | # values, ie. systimesres().
|
---|
| 46 | #
|
---|
| 47 |
|
---|
| 48 | ### Choose an implementation
|
---|
| 49 |
|
---|
| 50 | SYSTIMES_IMPLEMENTATION = None
|
---|
| 51 | USE_CTYPES_GETPROCESSTIMES = 'ctypes GetProcessTimes() wrapper'
|
---|
| 52 | USE_WIN32PROCESS_GETPROCESSTIMES = 'win32process.GetProcessTimes()'
|
---|
| 53 | USE_RESOURCE_GETRUSAGE = 'resource.getrusage()'
|
---|
| 54 | USE_PROCESS_TIME_CLOCK = 'time.clock() (process time)'
|
---|
| 55 | USE_WALL_TIME_CLOCK = 'time.clock() (wall-clock)'
|
---|
| 56 | USE_WALL_TIME_TIME = 'time.time() (wall-clock)'
|
---|
| 57 |
|
---|
| 58 | if sys.platform[:3] == 'win':
|
---|
| 59 | # Windows platform
|
---|
| 60 | try:
|
---|
| 61 | import win32process
|
---|
| 62 | except ImportError:
|
---|
| 63 | try:
|
---|
| 64 | import ctypes
|
---|
| 65 | except ImportError:
|
---|
| 66 | # Use the wall-clock implementation time.clock(), since this
|
---|
| 67 | # is the highest resolution clock available on Windows
|
---|
| 68 | SYSTIMES_IMPLEMENTATION = USE_WALL_TIME_CLOCK
|
---|
| 69 | else:
|
---|
| 70 | SYSTIMES_IMPLEMENTATION = USE_CTYPES_GETPROCESSTIMES
|
---|
| 71 | else:
|
---|
| 72 | SYSTIMES_IMPLEMENTATION = USE_WIN32PROCESS_GETPROCESSTIMES
|
---|
| 73 | else:
|
---|
| 74 | # All other platforms
|
---|
| 75 | try:
|
---|
| 76 | import resource
|
---|
| 77 | except ImportError:
|
---|
| 78 | pass
|
---|
| 79 | else:
|
---|
| 80 | SYSTIMES_IMPLEMENTATION = USE_RESOURCE_GETRUSAGE
|
---|
| 81 |
|
---|
| 82 | # Fall-back solution
|
---|
| 83 | if SYSTIMES_IMPLEMENTATION is None:
|
---|
| 84 | # Check whether we can use time.clock() as approximation
|
---|
| 85 | # for systimes()
|
---|
| 86 | start = time.clock()
|
---|
| 87 | time.sleep(0.1)
|
---|
| 88 | stop = time.clock()
|
---|
| 89 | if stop - start < 0.001:
|
---|
| 90 | # Looks like time.clock() is usable (and measures process
|
---|
| 91 | # time)
|
---|
| 92 | SYSTIMES_IMPLEMENTATION = USE_PROCESS_TIME_CLOCK
|
---|
| 93 | else:
|
---|
| 94 | # Use wall-clock implementation time.time() since this provides
|
---|
| 95 | # the highest resolution clock on most systems
|
---|
| 96 | SYSTIMES_IMPLEMENTATION = USE_WALL_TIME_TIME
|
---|
| 97 |
|
---|
| 98 | ### Implementations
|
---|
| 99 |
|
---|
| 100 | def getrusage_systimes():
|
---|
| 101 | return resource.getrusage(resource.RUSAGE_SELF)[:2]
|
---|
| 102 |
|
---|
| 103 | def process_time_clock_systimes():
|
---|
| 104 | return (time.clock(), 0.0)
|
---|
| 105 |
|
---|
| 106 | def wall_clock_clock_systimes():
|
---|
| 107 | return (time.clock(), 0.0)
|
---|
| 108 |
|
---|
| 109 | def wall_clock_time_systimes():
|
---|
| 110 | return (time.time(), 0.0)
|
---|
| 111 |
|
---|
| 112 | # Number of clock ticks per second for the values returned
|
---|
| 113 | # by GetProcessTimes() on Windows.
|
---|
| 114 | #
|
---|
| 115 | # Note: Ticks returned by GetProcessTimes() are 100ns intervals on
|
---|
| 116 | # Windows XP. However, the process times are only updated with every
|
---|
| 117 | # clock tick and the frequency of these is somewhat lower: depending
|
---|
| 118 | # on the OS version between 10ms and 15ms. Even worse, the process
|
---|
| 119 | # time seems to be allocated to process currently running when the
|
---|
| 120 | # clock interrupt arrives, ie. it is possible that the current time
|
---|
| 121 | # slice gets accounted to a different process.
|
---|
| 122 |
|
---|
| 123 | WIN32_PROCESS_TIMES_TICKS_PER_SECOND = 1e7
|
---|
| 124 |
|
---|
| 125 | def win32process_getprocesstimes_systimes():
|
---|
| 126 | d = win32process.GetProcessTimes(win32process.GetCurrentProcess())
|
---|
| 127 | return (d['UserTime'] / WIN32_PROCESS_TIMES_TICKS_PER_SECOND,
|
---|
| 128 | d['KernelTime'] / WIN32_PROCESS_TIMES_TICKS_PER_SECOND)
|
---|
| 129 |
|
---|
| 130 | def ctypes_getprocesstimes_systimes():
|
---|
| 131 | creationtime = ctypes.c_ulonglong()
|
---|
| 132 | exittime = ctypes.c_ulonglong()
|
---|
| 133 | kerneltime = ctypes.c_ulonglong()
|
---|
| 134 | usertime = ctypes.c_ulonglong()
|
---|
| 135 | rc = ctypes.windll.kernel32.GetProcessTimes(
|
---|
| 136 | ctypes.windll.kernel32.GetCurrentProcess(),
|
---|
| 137 | ctypes.byref(creationtime),
|
---|
| 138 | ctypes.byref(exittime),
|
---|
| 139 | ctypes.byref(kerneltime),
|
---|
| 140 | ctypes.byref(usertime))
|
---|
| 141 | if not rc:
|
---|
| 142 | raise TypeError('GetProcessTimes() returned an error')
|
---|
| 143 | return (usertime.value / WIN32_PROCESS_TIMES_TICKS_PER_SECOND,
|
---|
| 144 | kerneltime.value / WIN32_PROCESS_TIMES_TICKS_PER_SECOND)
|
---|
| 145 |
|
---|
| 146 | # Select the default for the systimes() function
|
---|
| 147 |
|
---|
| 148 | if SYSTIMES_IMPLEMENTATION is USE_RESOURCE_GETRUSAGE:
|
---|
| 149 | systimes = getrusage_systimes
|
---|
| 150 |
|
---|
| 151 | elif SYSTIMES_IMPLEMENTATION is USE_PROCESS_TIME_CLOCK:
|
---|
| 152 | systimes = process_time_clock_systimes
|
---|
| 153 |
|
---|
| 154 | elif SYSTIMES_IMPLEMENTATION is USE_WALL_TIME_CLOCK:
|
---|
| 155 | systimes = wall_clock_clock_systimes
|
---|
| 156 |
|
---|
| 157 | elif SYSTIMES_IMPLEMENTATION is USE_WALL_TIME_TIME:
|
---|
| 158 | systimes = wall_clock_time_systimes
|
---|
| 159 |
|
---|
| 160 | elif SYSTIMES_IMPLEMENTATION is USE_WIN32PROCESS_GETPROCESSTIMES:
|
---|
| 161 | systimes = win32process_getprocesstimes_systimes
|
---|
| 162 |
|
---|
| 163 | elif SYSTIMES_IMPLEMENTATION is USE_CTYPES_GETPROCESSTIMES:
|
---|
| 164 | systimes = ctypes_getprocesstimes_systimes
|
---|
| 165 |
|
---|
| 166 | else:
|
---|
| 167 | raise TypeError('no suitable systimes() implementation found')
|
---|
| 168 |
|
---|
| 169 | def processtime():
|
---|
| 170 |
|
---|
| 171 | """ Return the total time spent on the process.
|
---|
| 172 |
|
---|
| 173 | This is the sum of user and system time as returned by
|
---|
| 174 | systimes().
|
---|
| 175 |
|
---|
| 176 | """
|
---|
| 177 | user, system = systimes()
|
---|
| 178 | return user + system
|
---|
| 179 |
|
---|
| 180 | ### Testing
|
---|
| 181 |
|
---|
| 182 | def some_workload():
|
---|
| 183 | x = 0L
|
---|
| 184 | for i in xrange(10000000L):
|
---|
| 185 | x = x + 1L
|
---|
| 186 |
|
---|
| 187 | def test_workload():
|
---|
| 188 | print 'Testing systimes() under load conditions'
|
---|
| 189 | t0 = systimes()
|
---|
| 190 | some_workload()
|
---|
| 191 | t1 = systimes()
|
---|
| 192 | print 'before:', t0
|
---|
| 193 | print 'after:', t1
|
---|
| 194 | print 'differences:', (t1[0] - t0[0], t1[1] - t0[1])
|
---|
| 195 | print
|
---|
| 196 |
|
---|
| 197 | def test_idle():
|
---|
| 198 | print 'Testing systimes() under idle conditions'
|
---|
| 199 | t0 = systimes()
|
---|
| 200 | time.sleep(1)
|
---|
| 201 | t1 = systimes()
|
---|
| 202 | print 'before:', t0
|
---|
| 203 | print 'after:', t1
|
---|
| 204 | print 'differences:', (t1[0] - t0[0], t1[1] - t0[1])
|
---|
| 205 | print
|
---|
| 206 |
|
---|
| 207 | if __name__ == '__main__':
|
---|
| 208 | print 'Using %s as timer' % SYSTIMES_IMPLEMENTATION
|
---|
| 209 | print
|
---|
| 210 | test_workload()
|
---|
| 211 | test_idle()
|
---|