summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven D'Aprano <steve@pearwood.info>2016-08-15 01:27:03 +1000
committerSteven D'Aprano <steve@pearwood.info>2016-08-15 01:27:03 +1000
commit09f4f711b67a310873a4cb084f7c413ef3addcd3 (patch)
tree6b018c4f8307857aa5cbc25adffa1daaaf5225b2
parent9171a8b4cea3e7b30e4f2c0a989d7f7b8ffabe73 (diff)
downloadcpython-git-09f4f711b67a310873a4cb084f7c413ef3addcd3.tar.gz
Issue6422 add autorange method to timeit.Timer
-rw-r--r--Doc/library/timeit.rst19
-rw-r--r--Lib/test/test_timeit.py22
-rw-r--r--Lib/timeit.py41
3 files changed, 69 insertions, 13 deletions
diff --git a/Doc/library/timeit.rst b/Doc/library/timeit.rst
index 57a4834c90..f046591a80 100644
--- a/Doc/library/timeit.rst
+++ b/Doc/library/timeit.rst
@@ -100,8 +100,8 @@ The module defines three convenience functions and a public class:
can be controlled by passing a namespace to *globals*.
To measure the execution time of the first statement, use the :meth:`.timeit`
- method. The :meth:`.repeat` method is a convenience to call :meth:`.timeit`
- multiple times and return a list of results.
+ method. The :meth:`.repeat` and :meth:`.autorange` methods are convenience
+ methods to call :meth:`.timeit` multiple times.
The execution time of *setup* is excluded from the overall timed execution run.
@@ -134,6 +134,21 @@ The module defines three convenience functions and a public class:
timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()
+ .. method:: Timer.autorange(callback=None)
+
+ Automatically determine how many times to call :meth:`.timeit`.
+
+ This is a convenience function that calls :meth:`.timeit` repeatedly
+ so that the total time >= 0.2 second, returning the eventual
+ (number of loops, time taken for that number of loops). It calls
+ :meth:`.timeit` with *number* set to successive powers of ten (10,
+ 100, 1000, ...) up to a maximum of one billion, until the time taken
+ is at least 0.2 second, or the maximum is reached.
+
+ If *callback* is given and is not *None*, it will be called after
+ each trial with two arguments: ``callback(number, time_taken)``.
+
+
.. method:: Timer.repeat(repeat=3, number=1000000)
Call :meth:`.timeit` a few times.
diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py
index 2db3c1bed2..1a95e2979c 100644
--- a/Lib/test/test_timeit.py
+++ b/Lib/test/test_timeit.py
@@ -354,6 +354,28 @@ class TestTimeit(unittest.TestCase):
s = self.run_main(switches=['-n1', '1/0'])
self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
+ def autorange(self, callback=None):
+ timer = FakeTimer(seconds_per_increment=0.001)
+ t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer)
+ return t.autorange(callback)
+
+ def test_autorange(self):
+ num_loops, time_taken = self.autorange()
+ self.assertEqual(num_loops, 1000)
+ self.assertEqual(time_taken, 1.0)
+
+ def test_autorange_with_callback(self):
+ def callback(a, b):
+ print("{} {:.3f}".format(a, b))
+ with captured_stdout() as s:
+ num_loops, time_taken = self.autorange(callback)
+ self.assertEqual(num_loops, 1000)
+ self.assertEqual(time_taken, 1.0)
+ expected = ('10 0.010\n'
+ '100 0.100\n'
+ '1000 1.000\n')
+ self.assertEqual(s.getvalue(), expected)
+
if __name__ == '__main__':
unittest.main()
diff --git a/Lib/timeit.py b/Lib/timeit.py
index 98cb3eb89a..2770efa35a 100644
--- a/Lib/timeit.py
+++ b/Lib/timeit.py
@@ -207,6 +207,26 @@ class Timer:
r.append(t)
return r
+ def autorange(self, callback=None):
+ """Return the number of loops so that total time >= 0.2.
+
+ Calls the timeit method with *number* set to successive powers of
+ ten (10, 100, 1000, ...) up to a maximum of one billion, until
+ the time taken is at least 0.2 second, or the maximum is reached.
+ Returns ``(number, time_taken)``.
+
+ If *callback* is given and is not None, it will be called after
+ each trial with two arguments: ``callback(number, time_taken)``.
+ """
+ for i in range(1, 10):
+ number = 10**i
+ time_taken = self.timeit(number)
+ if callback:
+ callback(number, time_taken)
+ if time_taken >= 0.2:
+ break
+ return (number, time_taken)
+
def timeit(stmt="pass", setup="pass", timer=default_timer,
number=default_number, globals=None):
"""Convenience function to create Timer object and call timeit method."""
@@ -295,17 +315,16 @@ def main(args=None, *, _wrap_timer=None):
t = Timer(stmt, setup, timer)
if number == 0:
# determine number so that 0.2 <= total time < 2.0
- for i in range(1, 10):
- number = 10**i
- try:
- x = t.timeit(number)
- except:
- t.print_exc()
- return 1
- if verbose:
- print("%d loops -> %.*g secs" % (number, precision, x))
- if x >= 0.2:
- break
+ callback = None
+ if verbose:
+ def callback(number, time_taken):
+ msg = "{num} loops -> {secs:.{prec}g} secs"
+ print(msg.format(num=number, secs=time_taken, prec=precision))
+ try:
+ number, _ = t.autorange(callback)
+ except:
+ t.print_exc()
+ return 1
try:
r = t.repeat(repeat, number)
except: