blob: c2110829954643632f21b4e986352afd651be3e9 [file] [log] [blame]
Guido van Rossum6e31aad2003-03-07 01:33:18 +00001"""Tool for measuring execution time of small code snippets.
Guido van Rossumb3f09d42003-03-05 23:31:58 +00002
Guido van Rossumb7ab6002003-03-06 02:32:19 +00003This module avoids a number of common traps for measuring execution
4times. See also Tim Peters' introduction to the Algorithms chapter in
5the Python Cookbook, published by O'Reilly.
Guido van Rossumb3f09d42003-03-05 23:31:58 +00006
Guido van Rossumb7ab6002003-03-06 02:32:19 +00007Library usage: see the Timer class.
Guido van Rossumb3f09d42003-03-05 23:31:58 +00008
9Command line usage:
Guido van Rossume8577b72003-03-06 03:02:10 +000010 python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [-h] [statement]
Guido van Rossumb3f09d42003-03-05 23:31:58 +000011
12Options:
Guido van Rossumb7ab6002003-03-06 02:32:19 +000013 -n/--number N: how many times to execute 'statement' (default: see below)
Guido van Rossumb3f09d42003-03-05 23:31:58 +000014 -r/--repeat N: how many times to repeat the timer (default 1)
Guido van Rossum6e31aad2003-03-07 01:33:18 +000015 -s/--setup S: statement to be executed once initially (default 'pass')
Guido van Rossumb3f09d42003-03-05 23:31:58 +000016 -t/--time: use time.time() (default on Unix)
17 -c/--clock: use time.clock() (default on Windows)
Guido van Rossume8577b72003-03-06 03:02:10 +000018 -h/--help: print this usage message and exit
Guido van Rossumb3f09d42003-03-05 23:31:58 +000019 statement: statement to be timed (default 'pass')
Guido van Rossumb7ab6002003-03-06 02:32:19 +000020
21A multi-line statement may be given by specifying each line as a
22separate argument; indented lines are possible by enclosing an
Guido van Rossum6e31aad2003-03-07 01:33:18 +000023argument in quotes and using leading spaces. Multiple -s options are
24treated similarly.
Guido van Rossumb7ab6002003-03-06 02:32:19 +000025
26If -n is not given, a suitable number of loops is calculated by trying
27successive powers of 10 until the total time is at least 0.2 seconds.
28
29The difference in default timer function is because on Windows,
30clock() has microsecond granularity but time()'s granularity is 1/60th
31of a second; on Unix, clock() has 1/100th of a second granularity and
32time() is much more precise. On either platform, the default timer
33functions measures wall clock time, not the CPU time. This means that
34other processes running on the same computer may interfere with the
35timing. The best thing to do when accurate timing is necessary is to
36repeat the timing a few times and use the best time; the -r option is
37good for this. On Unix, you can use clock() to measure CPU time.
Guido van Rossume8577b72003-03-06 03:02:10 +000038
39Note: there is a certain baseline overhead associated with executing a
40pass statement. The code here doesn't try to hide it, but you should
Guido van Rossum6e31aad2003-03-07 01:33:18 +000041be aware of it. The baseline overhead can be measured by invoking the
42program without arguments.
43
44The baseline overhead differs between Python versions! Also, to
45fairly compare older Python versions to Python 2.3, you may want to
46use python -O for the older versions to avoid timing SET_LINENO
47instructions.
Guido van Rossumb3f09d42003-03-05 23:31:58 +000048"""
49
50import sys
51import math
52import time
Guido van Rossum6e31aad2003-03-07 01:33:18 +000053try:
54 import itertools
55except ImportError:
56 # Must be an older Python version (see timeit() below)
57 itertools = None
Guido van Rossumb3f09d42003-03-05 23:31:58 +000058
59__all__ = ["Timer"]
60
Guido van Rossum538f1d82003-03-14 17:21:00 +000061dummy_src_name = "<timeit-src>"
Guido van Rossumb3f09d42003-03-05 23:31:58 +000062default_number = 1000000
63default_repeat = 10
64
65if sys.platform == "win32":
66 # On Windows, the best timer is time.clock()
67 default_timer = time.clock
68else:
69 # On most other platforms the best timer is time.time()
70 default_timer = time.time
71
Guido van Rossumb7ab6002003-03-06 02:32:19 +000072# Don't change the indentation of the template; the reindent() calls
73# in Timer.__init__() depend on setup being indented 4 spaces and stmt
74# being indented 8 spaces.
Guido van Rossumb3f09d42003-03-05 23:31:58 +000075template = """
Guido van Rossum538f1d82003-03-14 17:21:00 +000076def inner(_seq, _timer):
Guido van Rossumb3f09d42003-03-05 23:31:58 +000077 %(setup)s
Guido van Rossum538f1d82003-03-14 17:21:00 +000078 _t0 = _timer()
79 for _i in _seq:
Guido van Rossumb3f09d42003-03-05 23:31:58 +000080 %(stmt)s
Guido van Rossum538f1d82003-03-14 17:21:00 +000081 _t1 = _timer()
82 return _t1 - _t0
Guido van Rossumb3f09d42003-03-05 23:31:58 +000083"""
84
85def reindent(src, indent):
Guido van Rossumb7ab6002003-03-06 02:32:19 +000086 """Helper to reindent a multi-line statement."""
Guido van Rossume05dcce2003-03-06 13:09:09 +000087 return src.replace("\n", "\n" + " "*indent)
Guido van Rossumb3f09d42003-03-05 23:31:58 +000088
89class Timer:
Guido van Rossumb7ab6002003-03-06 02:32:19 +000090 """Class for timing execution speed of small code snippets.
91
92 The constructor takes a statement to be timed, an additional
93 statement used for setup, and a timer function. Both statements
94 default to 'pass'; the timer function is platform-dependent (see
95 module doc string).
96
97 To measure the execution time of the first statement, use the
98 timeit() method. The repeat() method is a convenience to call
99 timeit() multiple times and return a list of results.
100
101 The statements may contain newlines, as long as they don't contain
102 multi-line string literals.
103 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000104
105 def __init__(self, stmt="pass", setup="pass", timer=default_timer):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000106 """Constructor. See class doc string."""
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000107 self.timer = timer
108 stmt = reindent(stmt, 8)
109 setup = reindent(setup, 4)
110 src = template % {'stmt': stmt, 'setup': setup}
Guido van Rossum538f1d82003-03-14 17:21:00 +0000111 self.src = src # Save for traceback display
112 code = compile(src, dummy_src_name, "exec")
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000113 ns = {}
114 exec code in globals(), ns
115 self.inner = ns["inner"]
116
Guido van Rossum538f1d82003-03-14 17:21:00 +0000117 def print_exc(self, file=None):
118 """Helper to print a traceback from the timed code.
119
120 Typical use:
121
122 t = Timer(...) # outside the try/except
123 try:
124 t.timeit(...) # or t.repeat(...)
125 except:
126 t.print_exc()
127
128 The advantage over the standard traceback is that source lines
129 in the compiled template will be displayed.
130
131 The optional file argument directs where the traceback is
132 sent; it defaults to sys.stderr.
133 """
134 import linecache, traceback
135 linecache.cache[dummy_src_name] = (len(self.src),
136 None,
137 self.src.split("\n"),
138 dummy_src_name)
139 traceback.print_exc(file=file)
140
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000141 def timeit(self, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000142 """Time 'number' executions of the main statement.
143
144 To be precise, this executes the setup statement once, and
145 then returns the time it takes to execute the main statement
146 a number of times, as a float measured in seconds. The
147 argument is the number of times through the loop, defaulting
148 to one million. The main statement, the setup statement and
149 the timer function to be used are passed to the constructor.
150 """
Guido van Rossum6e31aad2003-03-07 01:33:18 +0000151 if itertools:
152 seq = itertools.repeat(None, number)
153 else:
154 seq = [None] * number
155 return self.inner(seq, self.timer)
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000156
157 def repeat(self, repeat=default_repeat, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000158 """Call timer() a few times.
159
160 This is a convenience function that calls the timer()
161 repeatedly, returning a list of results. The first argument
162 specifies how many times to call timer(), defaulting to 10;
163 the second argument specifies the timer argument, defaulting
164 to one million.
Guido van Rossum55735412003-03-06 16:11:17 +0000165
166 Note: it's tempting to calculate mean and standard deviation
167 from the result vector and report these. However, this is not
168 very useful. In a typical case, the lowest value gives a
169 lower bound for how fast your machine can run the given code
170 snippet; higher values in the result vector are typically not
171 caused by variability in Python's speed, but by other
172 processes interfering with your timing accuracy. So the min()
173 of the result is probably the only number you should be
174 interested in. After that, you should look at the entire
175 vector and apply common sense rather than statistics.
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000176 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000177 r = []
178 for i in range(repeat):
179 t = self.timeit(number)
180 r.append(t)
181 return r
182
183def main(args=None):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000184 """Main program, used when run as a script.
185
186 The optional argument specifies the command line to be parsed,
187 defaulting to sys.argv[1:].
188
189 The return value is an exit code to be passed to sys.exit(); it
190 may be None to indicate success.
Guido van Rossum538f1d82003-03-14 17:21:00 +0000191
192 When an exception happens during timing, a traceback is printed to
193 stderr and the return value is 1. Exceptions at other times
194 (including the template compilation) are not caught.
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000195 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000196 if args is None:
197 args = sys.argv[1:]
198 import getopt
199 try:
Guido van Rossume8577b72003-03-06 03:02:10 +0000200 opts, args = getopt.getopt(args, "n:s:r:tch",
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000201 ["number=", "setup=", "repeat=",
Guido van Rossume8577b72003-03-06 03:02:10 +0000202 "time", "clock", "help"])
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000203 except getopt.error, err:
204 print err
Guido van Rossume8577b72003-03-06 03:02:10 +0000205 print "use -h/--help for command line help"
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000206 return 2
207 timer = default_timer
208 stmt = "\n".join(args) or "pass"
209 number = 0 # auto-determine
Guido van Rossum6e31aad2003-03-07 01:33:18 +0000210 setup = []
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000211 repeat = 1
212 for o, a in opts:
213 if o in ("-n", "--number"):
214 number = int(a)
215 if o in ("-s", "--setup"):
Guido van Rossum6e31aad2003-03-07 01:33:18 +0000216 setup.append(a)
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000217 if o in ("-r", "--repeat"):
218 repeat = int(a)
219 if repeat <= 0:
220 repeat = 1
Guido van Rossume8577b72003-03-06 03:02:10 +0000221 if o in ("-t", "--time"):
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000222 timer = time.time
Guido van Rossume8577b72003-03-06 03:02:10 +0000223 if o in ("-c", "--clock"):
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000224 timer = time.clock
Guido van Rossume8577b72003-03-06 03:02:10 +0000225 if o in ("-h", "--help"):
226 print __doc__,
227 return 0
Guido van Rossum6e31aad2003-03-07 01:33:18 +0000228 setup = "\n".join(setup) or "pass"
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000229 t = Timer(stmt, setup, timer)
230 if number == 0:
231 # determine number so that 0.2 <= total time < 2.0
232 for i in range(1, 10):
233 number = 10**i
Guido van Rossum538f1d82003-03-14 17:21:00 +0000234 try:
235 x = t.timeit(number)
236 except:
237 t.print_exc()
238 return 1
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000239 if x >= 0.2:
240 break
Guido van Rossum538f1d82003-03-14 17:21:00 +0000241 try:
242 r = t.repeat(repeat, number)
243 except:
244 t.print_exc()
245 return 1
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000246 best = min(r)
247 print "%d loops," % number,
248 usec = best * 1e6 / number
249 if repeat > 1:
Guido van Rossum538f1d82003-03-14 17:21:00 +0000250 print "best of %d: %.3f usec per loop" % (repeat, usec)
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000251 else:
Guido van Rossum538f1d82003-03-14 17:21:00 +0000252 print "time: %.3f usec per loop" % usec
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000253 return None
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000254
255if __name__ == "__main__":
256 sys.exit(main())