Commit 9cd6a223 authored by Guido van Rossum's avatar Guido van Rossum

Implement some recommendations from Raymond H:

- Make all local variables in the template start with an underscore,
  to prevent name conflicts with the timed code.

- Added a method to print a traceback that shows source lines from the
  expanded template.

- Use that method in main().
parent 45e22f33
...@@ -58,6 +58,7 @@ except ImportError: ...@@ -58,6 +58,7 @@ except ImportError:
__all__ = ["Timer"] __all__ = ["Timer"]
dummy_src_name = "<timeit-src>"
default_number = 1000000 default_number = 1000000
default_repeat = 10 default_repeat = 10
...@@ -72,13 +73,13 @@ else: ...@@ -72,13 +73,13 @@ else:
# in Timer.__init__() depend on setup being indented 4 spaces and stmt # in Timer.__init__() depend on setup being indented 4 spaces and stmt
# being indented 8 spaces. # being indented 8 spaces.
template = """ template = """
def inner(seq, timer): def inner(_seq, _timer):
%(setup)s %(setup)s
t0 = timer() _t0 = _timer()
for i in seq: for _i in _seq:
%(stmt)s %(stmt)s
t1 = timer() _t1 = _timer()
return t1-t0 return _t1 - _t0
""" """
def reindent(src, indent): def reindent(src, indent):
...@@ -107,11 +108,36 @@ class Timer: ...@@ -107,11 +108,36 @@ class Timer:
stmt = reindent(stmt, 8) stmt = reindent(stmt, 8)
setup = reindent(setup, 4) setup = reindent(setup, 4)
src = template % {'stmt': stmt, 'setup': setup} src = template % {'stmt': stmt, 'setup': setup}
code = compile(src, "<src>", "exec") self.src = src # Save for traceback display
code = compile(src, dummy_src_name, "exec")
ns = {} ns = {}
exec code in globals(), ns exec code in globals(), ns
self.inner = ns["inner"] self.inner = ns["inner"]
def print_exc(self, file=None):
"""Helper to print a traceback from the timed code.
Typical use:
t = Timer(...) # outside the try/except
try:
t.timeit(...) # or t.repeat(...)
except:
t.print_exc()
The advantage over the standard traceback is that source lines
in the compiled template will be displayed.
The optional file argument directs where the traceback is
sent; it defaults to sys.stderr.
"""
import linecache, traceback
linecache.cache[dummy_src_name] = (len(self.src),
None,
self.src.split("\n"),
dummy_src_name)
traceback.print_exc(file=file)
def timeit(self, number=default_number): def timeit(self, number=default_number):
"""Time 'number' executions of the main statement. """Time 'number' executions of the main statement.
...@@ -162,6 +188,10 @@ def main(args=None): ...@@ -162,6 +188,10 @@ def main(args=None):
The return value is an exit code to be passed to sys.exit(); it The return value is an exit code to be passed to sys.exit(); it
may be None to indicate success. may be None to indicate success.
When an exception happens during timing, a traceback is printed to
stderr and the return value is 1. Exceptions at other times
(including the template compilation) are not caught.
""" """
if args is None: if args is None:
args = sys.argv[1:] args = sys.argv[1:]
...@@ -201,17 +231,25 @@ def main(args=None): ...@@ -201,17 +231,25 @@ def main(args=None):
# determine number so that 0.2 <= total time < 2.0 # determine number so that 0.2 <= total time < 2.0
for i in range(1, 10): for i in range(1, 10):
number = 10**i number = 10**i
x = t.timeit(number) try:
x = t.timeit(number)
except:
t.print_exc()
return 1
if x >= 0.2: if x >= 0.2:
break break
r = t.repeat(repeat, number) try:
r = t.repeat(repeat, number)
except:
t.print_exc()
return 1
best = min(r) best = min(r)
print "%d loops," % number, print "%d loops," % number,
usec = best * 1e6 / number usec = best * 1e6 / number
if repeat > 1: if repeat > 1:
print "best of %d: %.3f usec" % (repeat, usec) print "best of %d: %.3f usec per loop" % (repeat, usec)
else: else:
print "time: %.3f usec" % usec print "time: %.3f usec per loop" % usec
return None return None
if __name__ == "__main__": if __name__ == "__main__":
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment