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