blob: 3d1f30024050908569f916a7638c127393c5bda5 [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 Rossumb3f09d42003-03-05 23:31:58 +000054import sys
55import math
56import time
57import itertools
58
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 = """
75def inner(number, timer):
76 %(setup)s
77 seq = itertools.repeat(None, number)
78 t0 = timer()
79 for i in seq:
80 %(stmt)s
81 t1 = timer()
82 return t1-t0
83"""
84
85def reindent(src, indent):
Guido van Rossumb7ab6002003-03-06 02:32:19 +000086 """Helper to reindent a multi-line statement."""
Guido van Rossumb3f09d42003-03-05 23:31:58 +000087 return ("\n" + " "*indent).join(src.split("\n"))
88
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}
111 code = compile(src, "<src>", "exec")
112 ns = {}
113 exec code in globals(), ns
114 self.inner = ns["inner"]
115
116 def timeit(self, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000117 """Time 'number' executions of the main statement.
118
119 To be precise, this executes the setup statement once, and
120 then returns the time it takes to execute the main statement
121 a number of times, as a float measured in seconds. The
122 argument is the number of times through the loop, defaulting
123 to one million. The main statement, the setup statement and
124 the timer function to be used are passed to the constructor.
125 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000126 return self.inner(number, self.timer)
127
128 def repeat(self, repeat=default_repeat, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000129 """Call timer() a few times.
130
131 This is a convenience function that calls the timer()
132 repeatedly, returning a list of results. The first argument
133 specifies how many times to call timer(), defaulting to 10;
134 the second argument specifies the timer argument, defaulting
135 to one million.
136 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000137 r = []
138 for i in range(repeat):
139 t = self.timeit(number)
140 r.append(t)
141 return r
142
143def main(args=None):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000144 """Main program, used when run as a script.
145
146 The optional argument specifies the command line to be parsed,
147 defaulting to sys.argv[1:].
148
149 The return value is an exit code to be passed to sys.exit(); it
150 may be None to indicate success.
151 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000152 if args is None:
153 args = sys.argv[1:]
154 import getopt
155 try:
Guido van Rossume8577b72003-03-06 03:02:10 +0000156 opts, args = getopt.getopt(args, "n:s:r:tch",
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000157 ["number=", "setup=", "repeat=",
Guido van Rossume8577b72003-03-06 03:02:10 +0000158 "time", "clock", "help"])
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000159 except getopt.error, err:
160 print err
Guido van Rossume8577b72003-03-06 03:02:10 +0000161 print "use -h/--help for command line help"
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000162 return 2
163 timer = default_timer
164 stmt = "\n".join(args) or "pass"
165 number = 0 # auto-determine
166 setup = "pass"
167 repeat = 1
168 for o, a in opts:
169 if o in ("-n", "--number"):
170 number = int(a)
171 if o in ("-s", "--setup"):
172 setup = a
173 if o in ("-r", "--repeat"):
174 repeat = int(a)
175 if repeat <= 0:
176 repeat = 1
Guido van Rossume8577b72003-03-06 03:02:10 +0000177 if o in ("-t", "--time"):
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000178 timer = time.time
Guido van Rossume8577b72003-03-06 03:02:10 +0000179 if o in ("-c", "--clock"):
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000180 timer = time.clock
Guido van Rossume8577b72003-03-06 03:02:10 +0000181 if o in ("-h", "--help"):
182 print __doc__,
183 return 0
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000184 t = Timer(stmt, setup, timer)
185 if number == 0:
186 # determine number so that 0.2 <= total time < 2.0
187 for i in range(1, 10):
188 number = 10**i
189 x = t.timeit(number)
190 if x >= 0.2:
191 break
192 r = t.repeat(repeat, number)
193 best = min(r)
194 print "%d loops," % number,
195 usec = best * 1e6 / number
196 if repeat > 1:
197 print "best of %d: %.3f usec" % (repeat, usec)
198 else:
199 print "time: %.3f usec" % usec
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000200 return None
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000201
202if __name__ == "__main__":
203 sys.exit(main())