blob: f244b7395e213858f53007a6295ae1f65d4c5eb9 [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
61default_number = 1000000
62default_repeat = 10
63
64if sys.platform == "win32":
65 # On Windows, the best timer is time.clock()
66 default_timer = time.clock
67else:
68 # On most other platforms the best timer is time.time()
69 default_timer = time.time
70
Guido van Rossumb7ab6002003-03-06 02:32:19 +000071# Don't change the indentation of the template; the reindent() calls
72# in Timer.__init__() depend on setup being indented 4 spaces and stmt
73# being indented 8 spaces.
Guido van Rossumb3f09d42003-03-05 23:31:58 +000074template = """
Guido van Rossum6e31aad2003-03-07 01:33:18 +000075def inner(seq, timer):
Guido van Rossumb3f09d42003-03-05 23:31:58 +000076 %(setup)s
Guido van Rossumb3f09d42003-03-05 23:31:58 +000077 t0 = timer()
78 for i in seq:
79 %(stmt)s
80 t1 = timer()
81 return t1-t0
82"""
83
84def reindent(src, indent):
Guido van Rossumb7ab6002003-03-06 02:32:19 +000085 """Helper to reindent a multi-line statement."""
Guido van Rossume05dcce2003-03-06 13:09:09 +000086 return src.replace("\n", "\n" + " "*indent)
Guido van Rossumb3f09d42003-03-05 23:31:58 +000087
88class Timer:
Guido van Rossumb7ab6002003-03-06 02:32:19 +000089 """Class for timing execution speed of small code snippets.
90
91 The constructor takes a statement to be timed, an additional
92 statement used for setup, and a timer function. Both statements
93 default to 'pass'; the timer function is platform-dependent (see
94 module doc string).
95
96 To measure the execution time of the first statement, use the
97 timeit() method. The repeat() method is a convenience to call
98 timeit() multiple times and return a list of results.
99
100 The statements may contain newlines, as long as they don't contain
101 multi-line string literals.
102 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000103
104 def __init__(self, stmt="pass", setup="pass", timer=default_timer):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000105 """Constructor. See class doc string."""
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000106 self.timer = timer
107 stmt = reindent(stmt, 8)
108 setup = reindent(setup, 4)
109 src = template % {'stmt': stmt, 'setup': setup}
110 code = compile(src, "<src>", "exec")
111 ns = {}
112 exec code in globals(), ns
113 self.inner = ns["inner"]
114
115 def timeit(self, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000116 """Time 'number' executions of the main statement.
117
118 To be precise, this executes the setup statement once, and
119 then returns the time it takes to execute the main statement
120 a number of times, as a float measured in seconds. The
121 argument is the number of times through the loop, defaulting
122 to one million. The main statement, the setup statement and
123 the timer function to be used are passed to the constructor.
124 """
Guido van Rossum6e31aad2003-03-07 01:33:18 +0000125 if itertools:
126 seq = itertools.repeat(None, number)
127 else:
128 seq = [None] * number
129 return self.inner(seq, self.timer)
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000130
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
Guido van Rossum6e31aad2003-03-07 01:33:18 +0000180 setup = []
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000181 repeat = 1
182 for o, a in opts:
183 if o in ("-n", "--number"):
184 number = int(a)
185 if o in ("-s", "--setup"):
Guido van Rossum6e31aad2003-03-07 01:33:18 +0000186 setup.append(a)
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000187 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 Rossum6e31aad2003-03-07 01:33:18 +0000198 setup = "\n".join(setup) or "pass"
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000199 t = Timer(stmt, setup, timer)
200 if number == 0:
201 # determine number so that 0.2 <= total time < 2.0
202 for i in range(1, 10):
203 number = 10**i
204 x = t.timeit(number)
205 if x >= 0.2:
206 break
207 r = t.repeat(repeat, number)
208 best = min(r)
209 print "%d loops," % number,
210 usec = best * 1e6 / number
211 if repeat > 1:
212 print "best of %d: %.3f usec" % (repeat, usec)
213 else:
214 print "time: %.3f usec" % usec
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000215 return None
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000216
217if __name__ == "__main__":
218 sys.exit(main())