Home | History | Annotate | Download | only in Lib
      1 #! /usr/bin/env python

      2 
      3 """Tool for measuring execution time of small code snippets.
      4 
      5 This module avoids a number of common traps for measuring execution
      6 times.  See also Tim Peters' introduction to the Algorithms chapter in
      7 the Python Cookbook, published by O'Reilly.
      8 
      9 Library usage: see the Timer class.
     10 
     11 Command line usage:
     12     python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [--] [statement]
     13 
     14 Options:
     15   -n/--number N: how many times to execute 'statement' (default: see below)
     16   -r/--repeat N: how many times to repeat the timer (default 3)
     17   -s/--setup S: statement to be executed once initially (default 'pass')
     18   -t/--time: use time.time() (default on Unix)
     19   -c/--clock: use time.clock() (default on Windows)
     20   -v/--verbose: print raw timing results; repeat for more digits precision
     21   -h/--help: print this usage message and exit
     22   --: separate options from statement, use when statement starts with -
     23   statement: statement to be timed (default 'pass')
     24 
     25 A multi-line statement may be given by specifying each line as a
     26 separate argument; indented lines are possible by enclosing an
     27 argument in quotes and using leading spaces.  Multiple -s options are
     28 treated similarly.
     29 
     30 If -n is not given, a suitable number of loops is calculated by trying
     31 successive powers of 10 until the total time is at least 0.2 seconds.
     32 
     33 The difference in default timer function is because on Windows,
     34 clock() has microsecond granularity but time()'s granularity is 1/60th
     35 of a second; on Unix, clock() has 1/100th of a second granularity and
     36 time() is much more precise.  On either platform, the default timer
     37 functions measure wall clock time, not the CPU time.  This means that
     38 other processes running on the same computer may interfere with the
     39 timing.  The best thing to do when accurate timing is necessary is to
     40 repeat the timing a few times and use the best time.  The -r option is
     41 good for this; the default of 3 repetitions is probably enough in most
     42 cases.  On Unix, you can use clock() to measure CPU time.
     43 
     44 Note: there is a certain baseline overhead associated with executing a
     45 pass statement.  The code here doesn't try to hide it, but you should
     46 be aware of it.  The baseline overhead can be measured by invoking the
     47 program without arguments.
     48 
     49 The baseline overhead differs between Python versions!  Also, to
     50 fairly compare older Python versions to Python 2.3, you may want to
     51 use python -O for the older versions to avoid timing SET_LINENO
     52 instructions.
     53 """
     54 
     55 import gc
     56 import sys
     57 import time
     58 try:
     59     import itertools
     60 except ImportError:
     61     # Must be an older Python version (see timeit() below)

     62     itertools = None
     63 
     64 __all__ = ["Timer"]
     65 
     66 dummy_src_name = "<timeit-src>"
     67 default_number = 1000000
     68 default_repeat = 3
     69 
     70 if sys.platform == "win32":
     71     # On Windows, the best timer is time.clock()

     72     default_timer = time.clock
     73 else:
     74     # On most other platforms the best timer is time.time()

     75     default_timer = time.time
     76 
     77 # Don't change the indentation of the template; the reindent() calls

     78 # in Timer.__init__() depend on setup being indented 4 spaces and stmt

     79 # being indented 8 spaces.

     80 template = """
     81 def inner(_it, _timer):
     82     %(setup)s
     83     _t0 = _timer()
     84     for _i in _it:
     85         %(stmt)s
     86     _t1 = _timer()
     87     return _t1 - _t0
     88 """
     89 
     90 def reindent(src, indent):
     91     """Helper to reindent a multi-line statement."""
     92     return src.replace("\n", "\n" + " "*indent)
     93 
     94 def _template_func(setup, func):
     95     """Create a timer function. Used if the "statement" is a callable."""
     96     def inner(_it, _timer, _func=func):
     97         setup()
     98         _t0 = _timer()
     99         for _i in _it:
    100             _func()
    101         _t1 = _timer()
    102         return _t1 - _t0
    103     return inner
    104 
    105 class Timer:
    106     """Class for timing execution speed of small code snippets.
    107 
    108     The constructor takes a statement to be timed, an additional
    109     statement used for setup, and a timer function.  Both statements
    110     default to 'pass'; the timer function is platform-dependent (see
    111     module doc string).
    112 
    113     To measure the execution time of the first statement, use the
    114     timeit() method.  The repeat() method is a convenience to call
    115     timeit() multiple times and return a list of results.
    116 
    117     The statements may contain newlines, as long as they don't contain
    118     multi-line string literals.
    119     """
    120 
    121     def __init__(self, stmt="pass", setup="pass", timer=default_timer):
    122         """Constructor.  See class doc string."""
    123         self.timer = timer
    124         ns = {}
    125         if isinstance(stmt, basestring):
    126             stmt = reindent(stmt, 8)
    127             if isinstance(setup, basestring):
    128                 setup = reindent(setup, 4)
    129                 src = template % {'stmt': stmt, 'setup': setup}
    130             elif hasattr(setup, '__call__'):
    131                 src = template % {'stmt': stmt, 'setup': '_setup()'}
    132                 ns['_setup'] = setup
    133             else:
    134                 raise ValueError("setup is neither a string nor callable")
    135             self.src = src # Save for traceback display

    136             code = compile(src, dummy_src_name, "exec")
    137             exec code in globals(), ns
    138             self.inner = ns["inner"]
    139         elif hasattr(stmt, '__call__'):
    140             self.src = None
    141             if isinstance(setup, basestring):
    142                 _setup = setup
    143                 def setup():
    144                     exec _setup in globals(), ns
    145             elif not hasattr(setup, '__call__'):
    146                 raise ValueError("setup is neither a string nor callable")
    147             self.inner = _template_func(setup, stmt)
    148         else:
    149             raise ValueError("stmt is neither a string nor callable")
    150 
    151     def print_exc(self, file=None):
    152         """Helper to print a traceback from the timed code.
    153 
    154         Typical use:
    155 
    156             t = Timer(...)       # outside the try/except
    157             try:
    158                 t.timeit(...)    # or t.repeat(...)
    159             except:
    160                 t.print_exc()
    161 
    162         The advantage over the standard traceback is that source lines
    163         in the compiled template will be displayed.
    164 
    165         The optional file argument directs where the traceback is
    166         sent; it defaults to sys.stderr.
    167         """
    168         import linecache, traceback
    169         if self.src is not None:
    170             linecache.cache[dummy_src_name] = (len(self.src),
    171                                                None,
    172                                                self.src.split("\n"),
    173                                                dummy_src_name)
    174         # else the source is already stored somewhere else

    175 
    176         traceback.print_exc(file=file)
    177 
    178     def timeit(self, number=default_number):
    179         """Time 'number' executions of the main statement.
    180 
    181         To be precise, this executes the setup statement once, and
    182         then returns the time it takes to execute the main statement
    183         a number of times, as a float measured in seconds.  The
    184         argument is the number of times through the loop, defaulting
    185         to one million.  The main statement, the setup statement and
    186         the timer function to be used are passed to the constructor.
    187         """
    188         if itertools:
    189             it = itertools.repeat(None, number)
    190         else:
    191             it = [None] * number
    192         gcold = gc.isenabled()
    193         gc.disable()
    194         timing = self.inner(it, self.timer)
    195         if gcold:
    196             gc.enable()
    197         return timing
    198 
    199     def repeat(self, repeat=default_repeat, number=default_number):
    200         """Call timeit() a few times.
    201 
    202         This is a convenience function that calls the timeit()
    203         repeatedly, returning a list of results.  The first argument
    204         specifies how many times to call timeit(), defaulting to 3;
    205         the second argument specifies the timer argument, defaulting
    206         to one million.
    207 
    208         Note: it's tempting to calculate mean and standard deviation
    209         from the result vector and report these.  However, this is not
    210         very useful.  In a typical case, the lowest value gives a
    211         lower bound for how fast your machine can run the given code
    212         snippet; higher values in the result vector are typically not
    213         caused by variability in Python's speed, but by other
    214         processes interfering with your timing accuracy.  So the min()
    215         of the result is probably the only number you should be
    216         interested in.  After that, you should look at the entire
    217         vector and apply common sense rather than statistics.
    218         """
    219         r = []
    220         for i in range(repeat):
    221             t = self.timeit(number)
    222             r.append(t)
    223         return r
    224 
    225 def timeit(stmt="pass", setup="pass", timer=default_timer,
    226            number=default_number):
    227     """Convenience function to create Timer object and call timeit method."""
    228     return Timer(stmt, setup, timer).timeit(number)
    229 
    230 def repeat(stmt="pass", setup="pass", timer=default_timer,
    231            repeat=default_repeat, number=default_number):
    232     """Convenience function to create Timer object and call repeat method."""
    233     return Timer(stmt, setup, timer).repeat(repeat, number)
    234 
    235 def main(args=None):
    236     """Main program, used when run as a script.
    237 
    238     The optional argument specifies the command line to be parsed,
    239     defaulting to sys.argv[1:].
    240 
    241     The return value is an exit code to be passed to sys.exit(); it
    242     may be None to indicate success.
    243 
    244     When an exception happens during timing, a traceback is printed to
    245     stderr and the return value is 1.  Exceptions at other times
    246     (including the template compilation) are not caught.
    247     """
    248     if args is None:
    249         args = sys.argv[1:]
    250     import getopt
    251     try:
    252         opts, args = getopt.getopt(args, "n:s:r:tcvh",
    253                                    ["number=", "setup=", "repeat=",
    254                                     "time", "clock", "verbose", "help"])
    255     except getopt.error, err:
    256         print err
    257         print "use -h/--help for command line help"
    258         return 2
    259     timer = default_timer
    260     stmt = "\n".join(args) or "pass"
    261     number = 0 # auto-determine

    262     setup = []
    263     repeat = default_repeat
    264     verbose = 0
    265     precision = 3
    266     for o, a in opts:
    267         if o in ("-n", "--number"):
    268             number = int(a)
    269         if o in ("-s", "--setup"):
    270             setup.append(a)
    271         if o in ("-r", "--repeat"):
    272             repeat = int(a)
    273             if repeat <= 0:
    274                 repeat = 1
    275         if o in ("-t", "--time"):
    276             timer = time.time
    277         if o in ("-c", "--clock"):
    278             timer = time.clock
    279         if o in ("-v", "--verbose"):
    280             if verbose:
    281                 precision += 1
    282             verbose += 1
    283         if o in ("-h", "--help"):
    284             print __doc__,
    285             return 0
    286     setup = "\n".join(setup) or "pass"
    287     # Include the current directory, so that local imports work (sys.path

    288     # contains the directory of this script, rather than the current

    289     # directory)

    290     import os
    291     sys.path.insert(0, os.curdir)
    292     t = Timer(stmt, setup, timer)
    293     if number == 0:
    294         # determine number so that 0.2 <= total time < 2.0

    295         for i in range(1, 10):
    296             number = 10**i
    297             try:
    298                 x = t.timeit(number)
    299             except:
    300                 t.print_exc()
    301                 return 1
    302             if verbose:
    303                 print "%d loops -> %.*g secs" % (number, precision, x)
    304             if x >= 0.2:
    305                 break
    306     try:
    307         r = t.repeat(repeat, number)
    308     except:
    309         t.print_exc()
    310         return 1
    311     best = min(r)
    312     if verbose:
    313         print "raw times:", " ".join(["%.*g" % (precision, x) for x in r])
    314     print "%d loops," % number,
    315     usec = best * 1e6 / number
    316     if usec < 1000:
    317         print "best of %d: %.*g usec per loop" % (repeat, precision, usec)
    318     else:
    319         msec = usec / 1000
    320         if msec < 1000:
    321             print "best of %d: %.*g msec per loop" % (repeat, precision, msec)
    322         else:
    323             sec = msec / 1000
    324             print "best of %d: %.*g sec per loop" % (repeat, precision, sec)
    325     return None
    326 
    327 if __name__ == "__main__":
    328     sys.exit(main())
    329