blob: fd177cd2a17f11c624429d8997adf45a6cc7f9d7 [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 Rossumb7ab6002003-03-06 02:32:19 +000010 python timeit.py [-n N] [-r N] [-s S] [-t] [-c] [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)
18 statement: statement to be timed (default 'pass')
Guido van Rossumb7ab6002003-03-06 02:32:19 +000019
20A multi-line statement may be given by specifying each line as a
21separate argument; indented lines are possible by enclosing an
22argument in quotes and using leading spaces.
23
24If -n is not given, a suitable number of loops is calculated by trying
25successive powers of 10 until the total time is at least 0.2 seconds.
26
27The difference in default timer function is because on Windows,
28clock() has microsecond granularity but time()'s granularity is 1/60th
29of a second; on Unix, clock() has 1/100th of a second granularity and
30time() is much more precise. On either platform, the default timer
31functions measures wall clock time, not the CPU time. This means that
32other processes running on the same computer may interfere with the
33timing. The best thing to do when accurate timing is necessary is to
34repeat the timing a few times and use the best time; the -r option is
35good for this. On Unix, you can use clock() to measure CPU time.
Guido van Rossumb3f09d42003-03-05 23:31:58 +000036"""
37
Guido van Rossumb7ab6002003-03-06 02:32:19 +000038# To use this module with older versions of Python, the dependency on
39# the itertools module is easily removed; in the template, instead of
40# itertools.repeat(None, count), use [None]*count. It's barely slower.
41
Guido van Rossumb3f09d42003-03-05 23:31:58 +000042import sys
43import math
44import time
45import itertools
46
47__all__ = ["Timer"]
48
49default_number = 1000000
50default_repeat = 10
51
52if sys.platform == "win32":
53 # On Windows, the best timer is time.clock()
54 default_timer = time.clock
55else:
56 # On most other platforms the best timer is time.time()
57 default_timer = time.time
58
Guido van Rossumb7ab6002003-03-06 02:32:19 +000059# Don't change the indentation of the template; the reindent() calls
60# in Timer.__init__() depend on setup being indented 4 spaces and stmt
61# being indented 8 spaces.
Guido van Rossumb3f09d42003-03-05 23:31:58 +000062template = """
63def inner(number, timer):
64 %(setup)s
65 seq = itertools.repeat(None, number)
66 t0 = timer()
67 for i in seq:
68 %(stmt)s
69 t1 = timer()
70 return t1-t0
71"""
72
73def reindent(src, indent):
Guido van Rossumb7ab6002003-03-06 02:32:19 +000074 """Helper to reindent a multi-line statement."""
Guido van Rossumb3f09d42003-03-05 23:31:58 +000075 return ("\n" + " "*indent).join(src.split("\n"))
76
77class Timer:
Guido van Rossumb7ab6002003-03-06 02:32:19 +000078 """Class for timing execution speed of small code snippets.
79
80 The constructor takes a statement to be timed, an additional
81 statement used for setup, and a timer function. Both statements
82 default to 'pass'; the timer function is platform-dependent (see
83 module doc string).
84
85 To measure the execution time of the first statement, use the
86 timeit() method. The repeat() method is a convenience to call
87 timeit() multiple times and return a list of results.
88
89 The statements may contain newlines, as long as they don't contain
90 multi-line string literals.
91 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +000092
93 def __init__(self, stmt="pass", setup="pass", timer=default_timer):
Guido van Rossumb7ab6002003-03-06 02:32:19 +000094 """Constructor. See class doc string."""
Guido van Rossumb3f09d42003-03-05 23:31:58 +000095 self.timer = timer
96 stmt = reindent(stmt, 8)
97 setup = reindent(setup, 4)
98 src = template % {'stmt': stmt, 'setup': setup}
99 code = compile(src, "<src>", "exec")
100 ns = {}
101 exec code in globals(), ns
102 self.inner = ns["inner"]
103
104 def timeit(self, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000105 """Time 'number' executions of the main statement.
106
107 To be precise, this executes the setup statement once, and
108 then returns the time it takes to execute the main statement
109 a number of times, as a float measured in seconds. The
110 argument is the number of times through the loop, defaulting
111 to one million. The main statement, the setup statement and
112 the timer function to be used are passed to the constructor.
113 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000114 return self.inner(number, self.timer)
115
116 def repeat(self, repeat=default_repeat, number=default_number):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000117 """Call timer() a few times.
118
119 This is a convenience function that calls the timer()
120 repeatedly, returning a list of results. The first argument
121 specifies how many times to call timer(), defaulting to 10;
122 the second argument specifies the timer argument, defaulting
123 to one million.
124 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000125 r = []
126 for i in range(repeat):
127 t = self.timeit(number)
128 r.append(t)
129 return r
130
131def main(args=None):
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000132 """Main program, used when run as a script.
133
134 The optional argument specifies the command line to be parsed,
135 defaulting to sys.argv[1:].
136
137 The return value is an exit code to be passed to sys.exit(); it
138 may be None to indicate success.
139 """
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000140 if args is None:
141 args = sys.argv[1:]
142 import getopt
143 try:
144 opts, args = getopt.getopt(args, "n:s:r:tc",
145 ["number=", "setup=", "repeat=",
146 "time", "clock"])
147 except getopt.error, err:
148 print err
149 return 2
150 timer = default_timer
151 stmt = "\n".join(args) or "pass"
152 number = 0 # auto-determine
153 setup = "pass"
154 repeat = 1
155 for o, a in opts:
156 if o in ("-n", "--number"):
157 number = int(a)
158 if o in ("-s", "--setup"):
159 setup = a
160 if o in ("-r", "--repeat"):
161 repeat = int(a)
162 if repeat <= 0:
163 repeat = 1
164 if o in ("-t", "time"):
165 timer = time.time
166 if o in ("-c", "clock"):
167 timer = time.clock
168 t = Timer(stmt, setup, timer)
169 if number == 0:
170 # determine number so that 0.2 <= total time < 2.0
171 for i in range(1, 10):
172 number = 10**i
173 x = t.timeit(number)
174 if x >= 0.2:
175 break
176 r = t.repeat(repeat, number)
177 best = min(r)
178 print "%d loops," % number,
179 usec = best * 1e6 / number
180 if repeat > 1:
181 print "best of %d: %.3f usec" % (repeat, usec)
182 else:
183 print "time: %.3f usec" % usec
Guido van Rossumb7ab6002003-03-06 02:32:19 +0000184 return None
Guido van Rossumb3f09d42003-03-05 23:31:58 +0000185
186if __name__ == "__main__":
187 sys.exit(main())