| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 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 | 
 | 8 |     based on the platforma and available tools. | 
 | 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, | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 19 |     the module reverts to the highest resolution wall-clock timer | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 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, struct | 
 | 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 = 'cytpes GetProcessTimes() wrapper' | 
 | 52 | USE_WIN32PROCESS_GETPROCESSTIMES = 'win32process.GetProcessTimes()' | 
 | 53 | USE_RESOURCE_GETRUSAGE = 'resource.getrusage()' | 
 | 54 | USE_PROCESS_TIME_CLOCK = 'time.clock() (process time)' | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 55 | USE_WALL_TIME_CLOCK = 'time.clock() (wall-clock)' | 
 | 56 | USE_WALL_TIME_TIME = 'time.time() (wall-clock)' | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 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: | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 66 |             # Use the wall-clock implementation time.clock(), since this | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 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: | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 94 |         # Use wall-clock implementation time.time() since this provides | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 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 |  | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 106 | def wall_clock_clock_systimes(): | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 107 |     return (time.clock(), 0.0) | 
 | 108 |  | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 109 | def wall_clock_time_systimes(): | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 110 |     return (time.time(), 0.0) | 
 | 111 |  | 
 | 112 | # Number of clock ticks per second for the values returned | 
 | 113 | # by GetProcessTimes() on Windows. | 
 | 114 | # | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 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 | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 124 |  | 
 | 125 | def win32process_getprocesstimes_systimes(): | 
 | 126 |     d = win32process.GetProcessTimes(win32process.GetCurrentProcess()) | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 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: | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 155 |     systimes = wall_clock_clock_systimes | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 156 |  | 
 | 157 | elif SYSTIMES_IMPLEMENTATION is USE_WALL_TIME_TIME: | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 158 |     systimes = wall_clock_time_systimes | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 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 |  | 
| Thomas Wouters | 0e3f591 | 2006-08-11 14:57:12 +0000 | [diff] [blame] | 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 |  | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 180 | ### Testing | 
 | 181 |  | 
 | 182 | def some_workload(): | 
| Guido van Rossum | cd16bf6 | 2007-06-13 18:07:49 +0000 | [diff] [blame] | 183 |     x = 0 | 
 | 184 |     for i in range(10000000): | 
 | 185 |         x = x + 1 | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 186 |  | 
 | 187 | def test_workload(): | 
| Guido van Rossum | 486364b | 2007-06-30 05:01:58 +0000 | [diff] [blame] | 188 |     print('Testing systimes() under load conditions') | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 189 |     t0 = systimes() | 
 | 190 |     some_workload() | 
 | 191 |     t1 = systimes() | 
| Guido van Rossum | 486364b | 2007-06-30 05:01:58 +0000 | [diff] [blame] | 192 |     print('before:', t0) | 
 | 193 |     print('after:', t1) | 
 | 194 |     print('differences:', (t1[0] - t0[0], t1[1] - t0[1])) | 
 | 195 |     print() | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 196 |  | 
 | 197 | def test_idle(): | 
| Guido van Rossum | 486364b | 2007-06-30 05:01:58 +0000 | [diff] [blame] | 198 |     print('Testing systimes() under idle conditions') | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 199 |     t0 = systimes() | 
 | 200 |     time.sleep(1) | 
 | 201 |     t1 = systimes() | 
| Guido van Rossum | 486364b | 2007-06-30 05:01:58 +0000 | [diff] [blame] | 202 |     print('before:', t0) | 
 | 203 |     print('after:', t1) | 
 | 204 |     print('differences:', (t1[0] - t0[0], t1[1] - t0[1])) | 
 | 205 |     print() | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 206 |  | 
 | 207 | if __name__ == '__main__': | 
| Guido van Rossum | 486364b | 2007-06-30 05:01:58 +0000 | [diff] [blame] | 208 |     print('Using %s as timer' % SYSTIMES_IMPLEMENTATION) | 
 | 209 |     print() | 
| Thomas Wouters | 4d70c3d | 2006-06-08 14:42:34 +0000 | [diff] [blame] | 210 |     test_workload() | 
 | 211 |     test_idle() |