Issue #28469: timeit now uses the sequence 1, 2, 5, 10, 20, 50,... instead

of 1, 10, 100,... for autoranging.
This commit is contained in:
Serhiy Storchaka 2016-10-23 15:17:05 +03:00
parent f8d7d41507
commit d3ff784f2d
4 changed files with 63 additions and 47 deletions

View File

@ -28,11 +28,11 @@ can be used to compare three different expressions:
.. code-block:: sh .. code-block:: sh
$ python3 -m timeit '"-".join(str(n) for n in range(100))' $ python3 -m timeit '"-".join(str(n) for n in range(100))'
10000 loops, best of 3: 30.2 usec per loop 10000 loops, best of 5: 30.2 usec per loop
$ python3 -m timeit '"-".join([str(n) for n in range(100)])' $ python3 -m timeit '"-".join([str(n) for n in range(100)])'
10000 loops, best of 3: 27.5 usec per loop 10000 loops, best of 5: 27.5 usec per loop
$ python3 -m timeit '"-".join(map(str, range(100)))' $ python3 -m timeit '"-".join(map(str, range(100)))'
10000 loops, best of 3: 23.2 usec per loop 10000 loops, best of 5: 23.2 usec per loop
This can be achieved from the :ref:`python-interface` with:: This can be achieved from the :ref:`python-interface` with::
@ -141,9 +141,8 @@ The module defines three convenience functions and a public class:
This is a convenience function that calls :meth:`.timeit` repeatedly This is a convenience function that calls :meth:`.timeit` repeatedly
so that the total time >= 0.2 second, returning the eventual so that the total time >= 0.2 second, returning the eventual
(number of loops, time taken for that number of loops). It calls (number of loops, time taken for that number of loops). It calls
:meth:`.timeit` with *number* set to successive powers of ten (10, :meth:`.timeit` with increasing numbers from the sequence 1, 2, 5,
100, 1000, ...) up to a maximum of one billion, until the time taken 10, 20, 50, ... until the time taken is at least 0.2 second.
is at least 0.2 second, or the maximum is reached.
If *callback* is given and is not ``None``, it will be called after If *callback* is given and is not ``None``, it will be called after
each trial with two arguments: ``callback(number, time_taken)``. each trial with two arguments: ``callback(number, time_taken)``.
@ -268,9 +267,9 @@ It is possible to provide a setup statement that is executed only once at the be
.. code-block:: sh .. code-block:: sh
$ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text' $ python -m timeit -s 'text = "sample string"; char = "g"' 'char in text'
10000000 loops, best of 3: 0.0877 usec per loop 5000000 loops, best of 5: 0.0877 usec per loop
$ python -m timeit -s 'text = "sample string"; char = "g"' 'text.find(char)' $ 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 5: 0.342 usec per loop
:: ::
@ -297,14 +296,14 @@ to test for missing and present object attributes:
.. code-block:: sh .. code-block:: sh
$ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass' $ python -m timeit 'try:' ' str.__bool__' 'except AttributeError:' ' pass'
100000 loops, best of 3: 15.7 usec per loop 20000 loops, best of 5: 15.7 usec per loop
$ python -m timeit 'if hasattr(str, "__bool__"): pass' $ python -m timeit 'if hasattr(str, "__bool__"): pass'
100000 loops, best of 3: 4.26 usec per loop 50000 loops, best of 5: 4.26 usec per loop
$ python -m timeit 'try:' ' int.__bool__' 'except AttributeError:' ' pass' $ python -m timeit 'try:' ' int.__bool__' 'except AttributeError:' ' pass'
1000000 loops, best of 3: 1.43 usec per loop 200000 loops, best of 5: 1.43 usec per loop
$ python -m timeit 'if hasattr(int, "__bool__"): pass' $ python -m timeit 'if hasattr(int, "__bool__"): pass'
100000 loops, best of 3: 2.23 usec per loop 100000 loops, best of 5: 2.23 usec per loop
:: ::

View File

@ -256,7 +256,7 @@ class TestTimeit(unittest.TestCase):
def test_main_milliseconds(self): def test_main_milliseconds(self):
s = self.run_main(seconds_per_increment=0.0055) s = self.run_main(seconds_per_increment=0.0055)
self.assertEqual(s, "100 loops, best of 5: 5.5 msec per loop\n") self.assertEqual(s, "50 loops, best of 5: 5.5 msec per loop\n")
def test_main_microseconds(self): def test_main_microseconds(self):
s = self.run_main(seconds_per_increment=0.0000025, switches=['-n100']) s = self.run_main(seconds_per_increment=0.0000025, switches=['-n100'])
@ -304,35 +304,43 @@ class TestTimeit(unittest.TestCase):
""")) """))
def test_main_very_verbose(self): def test_main_very_verbose(self):
s = self.run_main(seconds_per_increment=0.000050, switches=['-vv']) s = self.run_main(seconds_per_increment=0.000_030, switches=['-vv'])
self.assertEqual(s, dedent("""\ self.assertEqual(s, dedent("""\
1 loop -> 5e-05 secs 1 loop -> 3e-05 secs
10 loops -> 0.0005 secs 2 loops -> 6e-05 secs
100 loops -> 0.005 secs 5 loops -> 0.00015 secs
1000 loops -> 0.05 secs 10 loops -> 0.0003 secs
10000 loops -> 0.5 secs 20 loops -> 0.0006 secs
50 loops -> 0.0015 secs
100 loops -> 0.003 secs
200 loops -> 0.006 secs
500 loops -> 0.015 secs
1000 loops -> 0.03 secs
2000 loops -> 0.06 secs
5000 loops -> 0.15 secs
10000 loops -> 0.3 secs
raw times: 500 msec, 500 msec, 500 msec, 500 msec, 500 msec raw times: 300 msec, 300 msec, 300 msec, 300 msec, 300 msec
10000 loops, best of 5: 50 usec per loop 10000 loops, best of 5: 30 usec per loop
""")) """))
def test_main_with_time_unit(self): def test_main_with_time_unit(self):
unit_sec = self.run_main(seconds_per_increment=0.002, unit_sec = self.run_main(seconds_per_increment=0.003,
switches=['-u', 'sec']) switches=['-u', 'sec'])
self.assertEqual(unit_sec, self.assertEqual(unit_sec,
"100 loops, best of 5: 0.002 sec per loop\n") "100 loops, best of 5: 0.003 sec per loop\n")
unit_msec = self.run_main(seconds_per_increment=0.002, unit_msec = self.run_main(seconds_per_increment=0.003,
switches=['-u', 'msec']) switches=['-u', 'msec'])
self.assertEqual(unit_msec, self.assertEqual(unit_msec,
"100 loops, best of 5: 2 msec per loop\n") "100 loops, best of 5: 3 msec per loop\n")
unit_usec = self.run_main(seconds_per_increment=0.002, unit_usec = self.run_main(seconds_per_increment=0.003,
switches=['-u', 'usec']) switches=['-u', 'usec'])
self.assertEqual(unit_usec, self.assertEqual(unit_usec,
"100 loops, best of 5: 2e+03 usec per loop\n") "100 loops, best of 5: 3e+03 usec per loop\n")
# Test invalid unit input # Test invalid unit input
with captured_stderr() as error_stringio: with captured_stderr() as error_stringio:
invalid = self.run_main(seconds_per_increment=0.002, invalid = self.run_main(seconds_per_increment=0.003,
switches=['-u', 'parsec']) switches=['-u', 'parsec'])
self.assertEqual(error_stringio.getvalue(), self.assertEqual(error_stringio.getvalue(),
"Unrecognized unit. Please select nsec, usec, msec, or sec.\n") "Unrecognized unit. Please select nsec, usec, msec, or sec.\n")
@ -347,15 +355,15 @@ class TestTimeit(unittest.TestCase):
s = self.run_main(switches=['-n1', '1/0']) s = self.run_main(switches=['-n1', '1/0'])
self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError') self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
def autorange(self, seconds_per_increment=0.001, callback=None): def autorange(self, seconds_per_increment=1/1024, callback=None):
timer = FakeTimer(seconds_per_increment=seconds_per_increment) timer = FakeTimer(seconds_per_increment=seconds_per_increment)
t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer) t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer)
return t.autorange(callback) return t.autorange(callback)
def test_autorange(self): def test_autorange(self):
num_loops, time_taken = self.autorange() num_loops, time_taken = self.autorange()
self.assertEqual(num_loops, 1000) self.assertEqual(num_loops, 500)
self.assertEqual(time_taken, 1.0) self.assertEqual(time_taken, 500/1024)
def test_autorange_second(self): def test_autorange_second(self):
num_loops, time_taken = self.autorange(seconds_per_increment=1.0) num_loops, time_taken = self.autorange(seconds_per_increment=1.0)
@ -367,12 +375,17 @@ class TestTimeit(unittest.TestCase):
print("{} {:.3f}".format(a, b)) print("{} {:.3f}".format(a, b))
with captured_stdout() as s: with captured_stdout() as s:
num_loops, time_taken = self.autorange(callback=callback) num_loops, time_taken = self.autorange(callback=callback)
self.assertEqual(num_loops, 1000) self.assertEqual(num_loops, 500)
self.assertEqual(time_taken, 1.0) self.assertEqual(time_taken, 500/1024)
expected = ('1 0.001\n' expected = ('1 0.001\n'
'2 0.002\n'
'5 0.005\n'
'10 0.010\n' '10 0.010\n'
'100 0.100\n' '20 0.020\n'
'1000 1.000\n') '50 0.049\n'
'100 0.098\n'
'200 0.195\n'
'500 0.488\n')
self.assertEqual(s.getvalue(), expected) self.assertEqual(s.getvalue(), expected)

View File

@ -208,22 +208,23 @@ class Timer:
def autorange(self, callback=None): def autorange(self, callback=None):
"""Return the number of loops so that total time >= 0.2. """Return the number of loops so that total time >= 0.2.
Calls the timeit method with *number* set to successive powers of Calls the timeit method with increasing numbers from the sequence
ten (10, 100, 1000, ...) up to a maximum of one billion, until 1, 2, 5, 10, 20, 50, ... until the time taken is at least 0.2
the time taken is at least 0.2 second, or the maximum is reached. second. Returns (number, time_taken).
Returns ``(number, time_taken)``.
If *callback* is given and is not None, it will be called after If *callback* is given and is not None, it will be called after
each trial with two arguments: ``callback(number, time_taken)``. each trial with two arguments: ``callback(number, time_taken)``.
""" """
for i in range(0, 10): i = 1
number = 10**i while True:
time_taken = self.timeit(number) for j in 1, 2, 5:
if callback: number = i * j
callback(number, time_taken) time_taken = self.timeit(number)
if time_taken >= 0.2: if callback:
break callback(number, time_taken)
return (number, time_taken) if time_taken >= 0.2:
return (number, time_taken)
i *= 10
def timeit(stmt="pass", setup="pass", timer=default_timer, def timeit(stmt="pass", setup="pass", timer=default_timer,
number=default_number, globals=None): number=default_number, globals=None):

View File

@ -97,6 +97,9 @@ Core and Builtins
Library Library
------- -------
- Issue #28469: timeit now uses the sequence 1, 2, 5, 10, 20, 50,... instead
of 1, 10, 100,... for autoranging.
- Issue #28115: Command-line interface of the zipfile module now uses argparse. - Issue #28115: Command-line interface of the zipfile module now uses argparse.
Added support of long options. Added support of long options.