diff -r 353ced6ae182 Doc/library/timeit.rst --- a/Doc/library/timeit.rst Sun Sep 15 16:59:35 2013 -0700 +++ b/Doc/library/timeit.rst Mon Sep 16 10:53:26 2013 +0100 @@ -29,11 +29,11 @@ .. code-block:: sh $ python -m timeit '"-".join(str(n) for n in range(100))' - 10000 loops, best of 3: 40.3 usec per loop + 10000 loops, best of 3: 34.6 usec per loop (28870 loops per sec) $ python -m timeit '"-".join([str(n) for n in range(100)])' - 10000 loops, best of 3: 33.4 usec per loop + 10000 loops, best of 3: 31 usec per loop (32278 loops per sec) $ python -m timeit '"-".join(map(str, range(100)))' - 10000 loops, best of 3: 25.2 usec per loop + 10000 loops, best of 3: 25.6 usec per loop (39128 loops per sec) This can be achieved from the :ref:`python-interface` with:: @@ -239,9 +239,9 @@ .. code-block:: sh $ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text' - 10000000 loops, best of 3: 0.0877 usec per loop + 10000000 loops, best of 3: 0.0574 usec per loop (17415738 loops per sec) $ python -m timeit -s 'text = "sample string"; char = "g"' 'text.find(char)' - 1000000 loops, best of 3: 0.342 usec per loop + 1000000 loops, best of 3: 0.225 usec per loop (4440226 loops per sec) :: @@ -268,14 +268,14 @@ .. code-block:: sh $ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass' - 100000 loops, best of 3: 15.7 usec per loop + 1000000 loops, best of 3: 0.701 usec per loop (1425528 loops per sec) $ python -m timeit 'if hasattr(str, "__bool__"): pass' - 100000 loops, best of 3: 4.26 usec per loop + 1000000 loops, best of 3: 0.529 usec per loop (1890741 loops per sec) $ python -m timeit 'try:' ' int.__bool__' 'except AttributeError:' ' pass' - 1000000 loops, best of 3: 1.43 usec per loop + 10000000 loops, best of 3: 0.0689 usec per loop (14522881 loops per sec) $ python -m timeit 'if hasattr(int, "__bool__"): pass' - 100000 loops, best of 3: 2.23 usec per loop + 10000000 loops, best of 3: 0.154 usec per loop (6497353 loops per sec) :: diff -r 353ced6ae182 Lib/test/test_timeit.py --- a/Lib/test/test_timeit.py Sun Sep 15 16:59:35 2013 -0700 +++ b/Lib/test/test_timeit.py Mon Sep 16 10:53:26 2013 +0100 @@ -21,12 +21,14 @@ # number of loops during testing, but this would require changing the signature # of some functions that use the default as a default argument. + class FakeTimer: BASE_TIME = 42.0 + def __init__(self, seconds_per_increment=1.0): self.count = 0 self.setup_calls = 0 - self.seconds_per_increment=seconds_per_increment + self.seconds_per_increment = seconds_per_increment timeit._fake_timer = self def __call__(self): @@ -43,6 +45,7 @@ self.saved_timer = timer return self + class TestTimeit(unittest.TestCase): def tearDown(self): @@ -100,7 +103,7 @@ self.assertEqual(delta_time, number) # Takes too long to run in debug build. - #def test_timeit_default_iters(self): + # def test_timeit_default_iters(self): # self.timeit(self.fake_stmt, self.fake_setup) def test_timeit_zero_iters(self): @@ -114,17 +117,17 @@ def test_timeit_callable_stmt_and_setup(self): self.timeit(self.fake_callable_stmt, - self.fake_callable_setup, number=3) + self.fake_callable_setup, number=3) # Takes too long to run in debug build. - #def test_timeit_function(self): + # def test_timeit_function(self): # delta_time = timeit.timeit(self.fake_stmt, self.fake_setup, # timer=FakeTimer()) # self.assertEqual(delta_time, DEFAULT_NUMBER) def test_timeit_function_zero_iters(self): delta_time = timeit.timeit(self.fake_stmt, self.fake_setup, number=0, - timer=FakeTimer()) + timer=FakeTimer()) self.assertEqual(delta_time, 0) def repeat(self, stmt, setup, repeat=None, number=None): @@ -145,7 +148,7 @@ self.assertEqual(delta_times, repeat * [float(number)]) # Takes too long to run in debug build. - #def test_repeat_default(self): + # def test_repeat_default(self): # self.repeat(self.fake_stmt, self.fake_setup) def test_repeat_zero_reps(self): @@ -159,26 +162,26 @@ def test_repeat_callable_stmt(self): self.repeat(self.fake_callable_stmt, self.fake_setup, - repeat=3, number=5) + repeat=3, number=5) def test_repeat_callable_stmt_and_setup(self): self.repeat(self.fake_callable_stmt, self.fake_callable_setup, - repeat=3, number=5) + repeat=3, number=5) # Takes too long to run in debug build. - #def test_repeat_function(self): + # def test_repeat_function(self): # delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, # timer=FakeTimer()) # self.assertEqual(delta_times, DEFAULT_REPEAT * [float(DEFAULT_NUMBER)]) def test_repeat_function_zero_reps(self): delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, repeat=0, - timer=FakeTimer()) + timer=FakeTimer()) self.assertEqual(delta_times, []) def test_repeat_function_zero_iters(self): delta_times = timeit.repeat(self.fake_stmt, self.fake_setup, number=0, - timer=FakeTimer()) + timer=FakeTimer()) self.assertEqual(delta_times, DEFAULT_REPEAT * [0.0]) def assert_exc_string(self, exc_string, expected_exc_name): @@ -196,7 +199,7 @@ t.print_exc(s) self.assert_exc_string(s.getvalue(), 'ZeroDivisionError') - MAIN_DEFAULT_OUTPUT = "10 loops, best of 3: 1 sec per loop\n" + MAIN_DEFAULT_OUTPUT = "10 loops, best of 3: 1 sec per loop (1.000 loops per sec)\n" def run_main(self, seconds_per_increment=1.0, switches=None, timer=None): if timer is None: @@ -222,33 +225,39 @@ def test_main_seconds(self): s = self.run_main(seconds_per_increment=5.5) - self.assertEqual(s, "10 loops, best of 3: 5.5 sec per loop\n") + self.assertEqual( + s, "10 loops, best of 3: 5.5 sec per loop (0.182 loops per sec)\n") def test_main_milliseconds(self): s = self.run_main(seconds_per_increment=0.0055) - self.assertEqual(s, "100 loops, best of 3: 5.5 msec per loop\n") + self.assertEqual( + s, "100 loops, best of 3: 5.5 msec per loop (182 loops per sec)\n") def test_main_microseconds(self): s = self.run_main(seconds_per_increment=0.0000025, switches=['-n100']) - self.assertEqual(s, "100 loops, best of 3: 2.5 usec per loop\n") + self.assertEqual( + s, "100 loops, best of 3: 2.5 usec per loop (400000 loops per sec)\n") def test_main_fixed_iters(self): s = self.run_main(seconds_per_increment=2.0, switches=['-n35']) - self.assertEqual(s, "35 loops, best of 3: 2 sec per loop\n") + self.assertEqual( + s, "35 loops, best of 3: 2 sec per loop (0.500 loops per sec)\n") def test_main_setup(self): s = self.run_main(seconds_per_increment=2.0, - switches=['-n35', '-s', 'print("CustomSetup")']) + switches=['-n35', '-s', 'print("CustomSetup")']) self.assertEqual(s, "CustomSetup\n" * 3 + - "35 loops, best of 3: 2 sec per loop\n") + "35 loops, best of 3: 2 sec per loop (0.500 loops per sec)\n") def test_main_fixed_reps(self): s = self.run_main(seconds_per_increment=60.0, switches=['-r9']) - self.assertEqual(s, "10 loops, best of 9: 60 sec per loop\n") + self.assertEqual( + s, "10 loops, best of 9: 60 sec per loop (0.017 loops per sec)\n") def test_main_negative_reps(self): s = self.run_main(seconds_per_increment=60.0, switches=['-r-5']) - self.assertEqual(s, "10 loops, best of 1: 60 sec per loop\n") + self.assertEqual( + s, "10 loops, best of 1: 60 sec per loop (0.017 loops per sec)\n") @unittest.skipIf(sys.flags.optimize >= 2, "need __doc__") def test_main_help(self): @@ -274,7 +283,7 @@ self.assertEqual(s, dedent("""\ 10 loops -> 10 secs raw times: 10 10 10 - 10 loops, best of 3: 1 sec per loop + 10 loops, best of 3: 1 sec per loop (1.000 loops per sec) """)) def test_main_very_verbose(self): @@ -285,7 +294,7 @@ 1000 loops -> 0.05 secs 10000 loops -> 0.5 secs raw times: 0.5 0.5 0.5 - 10000 loops, best of 3: 50 usec per loop + 10000 loops, best of 3: 50 usec per loop (20000 loops per sec) """)) def test_main_exception(self): diff -r 353ced6ae182 Lib/timeit.py --- a/Lib/timeit.py Sun Sep 15 16:59:35 2013 -0700 +++ b/Lib/timeit.py Mon Sep 16 10:53:26 2013 +0100 @@ -73,10 +73,12 @@ return _t1 - _t0 """ + def reindent(src, indent): """Helper to reindent a multi-line statement.""" return src.replace("\n", "\n" + " "*indent) + def _template_func(setup, func): """Create a timer function. Used if the "statement" is a callable.""" def inner(_it, _timer, _func=func): @@ -88,7 +90,9 @@ return _t1 - _t0 return inner + class Timer: + """Class for timing execution speed of small code snippets. The constructor takes a statement to be timed, an additional @@ -118,7 +122,7 @@ ns['_setup'] = setup else: raise ValueError("setup is neither a string nor callable") - self.src = src # Save for traceback display + self.src = src # Save for traceback display code = compile(src, dummy_src_name, "exec") exec(code, globals(), ns) self.inner = ns["inner"] @@ -126,6 +130,7 @@ self.src = None if isinstance(setup, str): _setup = setup + def setup(): exec(_setup, globals(), ns) elif not callable(setup): @@ -151,7 +156,8 @@ The optional file argument directs where the traceback is sent; it defaults to sys.stderr. """ - import linecache, traceback + import linecache + import traceback if self.src is not None: linecache.cache[dummy_src_name] = (len(self.src), None, @@ -207,16 +213,19 @@ r.append(t) return r + def timeit(stmt="pass", setup="pass", timer=default_timer, number=default_number): """Convenience function to create Timer object and call timeit method.""" return Timer(stmt, setup, timer).timeit(number) + def repeat(stmt="pass", setup="pass", timer=default_timer, repeat=default_repeat, number=default_number): """Convenience function to create Timer object and call repeat method.""" return Timer(stmt, setup, timer).repeat(repeat, number) + def main(args=None, *, _wrap_timer=None): """Main program, used when run as a script. @@ -248,7 +257,7 @@ return 2 timer = default_timer stmt = "\n".join(args) or "pass" - number = 0 # auto-determine + number = 0 # auto-determine setup = [] repeat = default_repeat verbose = 0 @@ -287,7 +296,7 @@ if number == 0: # determine number so that 0.2 <= total time < 2.0 for i in range(1, 10): - number = 10**i + number = 10 ** i try: x = t.timeit(number) except: @@ -305,17 +314,27 @@ best = min(r) if verbose: print("raw times:", " ".join(["%.*g" % (precision, x) for x in r])) - print("%d loops," % number, end=' ') - usec = best * 1e6 / number - if usec < 1000: - print("best of %d: %.*g usec per loop" % (repeat, precision, usec)) - else: - msec = usec / 1000 - if msec < 1000: - print("best of %d: %.*g msec per loop" % (repeat, precision, msec)) - else: - sec = msec / 1000 - print("best of %d: %.*g sec per loop" % (repeat, precision, sec)) + + sec = best / number + duration = sec * 1e6 + unit = 'usec' + + if duration >= 1000: + unit = 'msec' + duration /= 1000 + + if duration >= 1000: + unit = 'sec' + duration /= 1000 + + per_second = 1 / sec + per_second_formatted = '{:.{precision}f}'.format( + per_second, precision=0 if per_second >= 100 else precision).replace(',', ' ') + + print("{0} loops, best of {1}: {2:.{precision}g} {3} per loop " + "({per_second} loops per sec)".format( + number, repeat, duration, unit, precision=precision, + per_second=per_second_formatted)) return None if __name__ == "__main__":