blob: 2178d522644cf3113e6a8bc24171e464d2d3a23a [file] [log] [blame]
Guido van Rossumb7ab6002003-03-06 02:32:19 +00001"""Framework for measuring execution time for 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)
15 -s/--setup S: statements executed once before 'statement' (default 'pass')
16 -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
23argument in quotes and using leading spaces.
24
25If -n is not given, a suitable number of loops is calculated by trying
26successive powers of 10 until the total time is at least 0.2 seconds.
27
28The difference in default timer function is because on Windows,
29clock() has microsecond granularity but time()'s granularity is 1/60th
30of a second; on Unix, clock() has 1/100th of a second granularity and
31time() is much more precise. On either platform, the default timer
32functions measures wall clock time, not the CPU time. This means that
33other processes running on the same computer may interfere with the
34timing. The best thing to do when accurate timing is necessary is to
35repeat the timing a few times and use the best time; the -r option is
36good for this. On Unix, you can use clock() to measure CPU time.
Guido van Rossume8577b72003-03-06 03:02:10 +000037
38Note: there is a certain baseline overhead associated with executing a
39pass statement. The code here doesn't try to hide it, but you should
40be aware of it (especially when comparing different versions of
41Python). The baseline overhead is measured by invoking the program
42without arguments.
Guido van Rossumb3f09d42003-03-05 23:31:58 +000043"""
44
Guido van Rossumb7ab6002003-03-06 02:32:19 +000045# To use this module with older versions of Python, the dependency on
46# the itertools module is easily removed; in the template, instead of
Guido van Rossume8577b72003-03-06 03:02:10 +000047# itertools.repeat(None, number), use [None]*number. It's barely
48# slower. Note: the baseline overhead, measured by the default
49# invocation, differs for older Python versions! Also, to fairly
50# compare older Python versions to Python 2.3, you may want to use
51# python -O for the older versions to avoid timing SET_LINENO
52# instructions.
Guido van Rossumb7ab6002003-03-06 02:32:19 +000053
Guido van Rossum55735412003-03-06 16:11:17 +000054# XXX Maybe for convenience of comparing with previous Python versions,
55# itertools.repeat() should not be used at all?
56
Guido van Rossumb3f09d42003-03-05 23:31:58 +000057import sys
58import math
59import time
60import itertools
61
62__all__ = ["Timer"]
63
64default_number = 1000000
65default_repeat = 10
66
67if sys.platform == "win32":
68 # On Windows, the best timer is time.clock()
69 default_timer = time.clock
70else:
71 # On most other platforms the best timer is time.time()
72 default_timer = time.time
73
Guido van Rossumb7ab6002003-03-06 02:32:19 +000074# Don't change the indentation of the template; the reindent() calls
75# in Timer.__init__() depend on setup being indented 4 spaces and stmt
76# being indented 8 spaces.
Guido van Rossumb3f09d42003-03-05 23:31:58 +000077template = """
78def inner(number, timer):
79 %(setup)s
80 seq = itertools.repeat(None, number)
81 t0 = timer()
82 for i in seq:
83 %(stmt)s
84 t1 = timer()
85 return t1-t0
86"""
87
88def reindent(src, indent):
Guido van Rossumb7ab6002003-03-06 02:32:19 +000089 """Helper to reindent a multi-line statement."""
Guido van Rossume05dcce2003-03-06 13:09:09 +000090 return src.replace("\n", "\n" + " "*indent)
Guido van Rossumb3f09d42003-03-05 23:31:58 +000091
92class Timer:
Guido van Rossumb7ab6002003-03-06 02:32:19 +000093 """Class for timing execution speed of small code snippets.
94
95 The constructor takes a statement to be timed, an additional
96 statement used for setup, and a timer function. Both statements
97 default to 'pass'; the timer function is platform-dependent (see
98 module doc string).
99
100 To measure the execution time of the first statement, use the
101 timeit() method. The repeat() method is a convenience to call
102 timeit() multiple times and return a list of results.
103
104 The statements may contain newlines, as long as they don't contain
105 multi-line string literals.
106 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000107
108 def __init__(self, stmt="pass", setup="pass", timer=default_timer):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000109 """Constructor. See class doc string."""
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000110 self.timer = timer
111 stmt = reindent(stmt, 8)
112 setup = reindent(setup, 4)
113 src = template % {'stmt': stmt, 'setup': setup}
114 code = compile(src, "<src>", "exec")
115 ns = {}
116 exec code in globals(), ns
117 self.inner = ns["inner"]
118
119 def timeit(self, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000120 """Time 'number' executions of the main statement.
121
122 To be precise, this executes the setup statement once, and
123 then returns the time it takes to execute the main statement
124 a number of times, as a float measured in seconds. The
125 argument is the number of times through the loop, defaulting
126 to one million. The main statement, the setup statement and
127 the timer function to be used are passed to the constructor.
128 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000129 return self.inner(number, self.timer)
130
131 def repeat(self, repeat=default_repeat, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000132 """Call timer() a few times.
133
134 This is a convenience function that calls the timer()
135 repeatedly, returning a list of results. The first argument
136 specifies how many times to call timer(), defaulting to 10;
137 the second argument specifies the timer argument, defaulting
138 to one million.
Guido van Rossum55735412003-03-06 16:11:17 +0000139
140 Note: it's tempting to calculate mean and standard deviation
141 from the result vector and report these. However, this is not
142 very useful. In a typical case, the lowest value gives a
143 lower bound for how fast your machine can run the given code
144 snippet; higher values in the result vector are typically not
145 caused by variability in Python's speed, but by other
146 processes interfering with your timing accuracy. So the min()
147 of the result is probably the only number you should be
148 interested in. After that, you should look at the entire
149 vector and apply common sense rather than statistics.
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000150 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000151 r = []
152 for i in range(repeat):
153 t = self.timeit(number)
154 r.append(t)
155 return r
156
157def main(args=None):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000158 """Main program, used when run as a script.
159
160 The optional argument specifies the command line to be parsed,
161 defaulting to sys.argv[1:].
162
163 The return value is an exit code to be passed to sys.exit(); it
164 may be None to indicate success.
165 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000166 if args is None:
167 args = sys.argv[1:]
168 import getopt
169 try:
Guido van Rossume8577b72003-03-06 03:02:10 +0000170 opts, args = getopt.getopt(args, "n:s:r:tch",
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000171 ["number=", "setup=", "repeat=",
Guido van Rossume8577b72003-03-06 03:02:10 +0000172 "time", "clock", "help"])
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000173 except getopt.error, err:
174 print err
Guido van Rossume8577b72003-03-06 03:02:10 +0000175 print "use -h/--help for command line help"
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000176 return 2
177 timer = default_timer
178 stmt = "\n".join(args) or "pass"
179 number = 0 # auto-determine
180 setup = "pass"
181 repeat = 1
182 for o, a in opts:
183 if o in ("-n", "--number"):
184 number = int(a)
185 if o in ("-s", "--setup"):
186 setup = a
187 if o in ("-r", "--repeat"):
188 repeat = int(a)
189 if repeat <= 0:
190 repeat = 1
Guido van Rossume8577b72003-03-06 03:02:10 +0000191 if o in ("-t", "--time"):
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000192 timer = time.time
Guido van Rossume8577b72003-03-06 03:02:10 +0000193 if o in ("-c", "--clock"):
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000194 timer = time.clock
Guido van Rossume8577b72003-03-06 03:02:10 +0000195 if o in ("-h", "--help"):
196 print __doc__,
197 return 0
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000198 t = Timer(stmt, setup, timer)
199 if number == 0:
200 # determine number so that 0.2 <= total time < 2.0
201 for i in range(1, 10):
202 number = 10**i
203 x = t.timeit(number)
204 if x >= 0.2:
205 break
206 r = t.repeat(repeat, number)
207 best = min(r)
208 print "%d loops," % number,
209 usec = best * 1e6 / number
210 if repeat > 1:
211 print "best of %d: %.3f usec" % (repeat, usec)
212 else:
213 print "time: %.3f usec" % usec
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000214 return None
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000215
216if __name__ == "__main__":
217 sys.exit(main())