[Python-checkins] cpython: timeit: start autorange with 1 iteration, not 10

victor.stinner python-checkins at python.org
Tue Oct 18 11:14:49 EDT 2016


https://hg.python.org/cpython/rev/3aba5552b976
changeset:   104537:3aba5552b976
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Tue Oct 18 17:06:56 2016 +0200
summary:
  timeit: start autorange with 1 iteration, not 10

Issue #28240: timeit autorange now uses a single loop iteration if the
benchmark takes less than 10 seconds, instead of 10 iterations.

"python3 -m timeit -s 'import time' 'time.sleep(1)'" now takes 4 seconds
instead of 40 seconds.

files:
  Lib/test/test_timeit.py |  35 +++++++++++++++++-----------
  Lib/timeit.py           |  10 ++++---
  Misc/NEWS               |   5 ++++
  3 files changed, 32 insertions(+), 18 deletions(-)


diff --git a/Lib/test/test_timeit.py b/Lib/test/test_timeit.py
--- a/Lib/test/test_timeit.py
+++ b/Lib/test/test_timeit.py
@@ -226,7 +226,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 = "1 loop, best of 3: 1 sec per loop\n"
 
     def run_main(self, seconds_per_increment=1.0, switches=None, timer=None):
         if timer is None:
@@ -252,7 +252,7 @@
 
     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, "1 loop, best of 3: 5.5 sec per loop\n")
 
     def test_main_milliseconds(self):
         s = self.run_main(seconds_per_increment=0.0055)
@@ -280,11 +280,11 @@
 
     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, "1 loop, best of 9: 60 sec per loop\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, "1 loop, best of 1: 60 sec per loop\n")
 
     @unittest.skipIf(sys.flags.optimize >= 2, "need __doc__")
     def test_main_help(self):
@@ -308,14 +308,15 @@
     def test_main_verbose(self):
         s = self.run_main(switches=['-v'])
         self.assertEqual(s, dedent("""\
-                10 loops -> 10 secs
-                raw times: 10 10 10
-                10 loops, best of 3: 1 sec per loop
+                1 loop -> 1 secs
+                raw times: 1 1 1
+                1 loop, best of 3: 1 sec per loop
             """))
 
     def test_main_very_verbose(self):
         s = self.run_main(seconds_per_increment=0.000050, switches=['-vv'])
         self.assertEqual(s, dedent("""\
+                1 loop -> 5e-05 secs
                 10 loops -> 0.0005 secs
                 100 loops -> 0.005 secs
                 1000 loops -> 0.05 secs
@@ -328,15 +329,15 @@
         unit_sec = self.run_main(seconds_per_increment=0.002,
                 switches=['-u', 'sec'])
         self.assertEqual(unit_sec,
-                "1000 loops, best of 3: 0.002 sec per loop\n")
+                "100 loops, best of 3: 0.002 sec per loop\n")
         unit_msec = self.run_main(seconds_per_increment=0.002,
                 switches=['-u', 'msec'])
         self.assertEqual(unit_msec,
-                "1000 loops, best of 3: 2 msec per loop\n")
+                "100 loops, best of 3: 2 msec per loop\n")
         unit_usec = self.run_main(seconds_per_increment=0.002,
                 switches=['-u', 'usec'])
         self.assertEqual(unit_usec,
-                "1000 loops, best of 3: 2e+03 usec per loop\n")
+                "100 loops, best of 3: 2e+03 usec per loop\n")
         # Test invalid unit input
         with captured_stderr() as error_stringio:
             invalid = self.run_main(seconds_per_increment=0.002,
@@ -354,8 +355,8 @@
             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)
+    def autorange(self, seconds_per_increment=0.001, callback=None):
+        timer = FakeTimer(seconds_per_increment=seconds_per_increment)
         t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer)
         return t.autorange(callback)
 
@@ -364,14 +365,20 @@
         self.assertEqual(num_loops, 1000)
         self.assertEqual(time_taken, 1.0)
 
+    def test_autorange_second(self):
+        num_loops, time_taken = self.autorange(seconds_per_increment=1.0)
+        self.assertEqual(num_loops, 1)
+        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)
+            num_loops, time_taken = self.autorange(callback=callback)
         self.assertEqual(num_loops, 1000)
         self.assertEqual(time_taken, 1.0)
-        expected = ('10 0.010\n'
+        expected = ('1 0.001\n'
+                    '10 0.010\n'
                     '100 0.100\n'
                     '1000 1.000\n')
         self.assertEqual(s.getvalue(), expected)
diff --git a/Lib/timeit.py b/Lib/timeit.py
--- a/Lib/timeit.py
+++ b/Lib/timeit.py
@@ -218,7 +218,7 @@
         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):
+        for i in range(0, 10):
             number = 10**i
             time_taken = self.timeit(number)
             if callback:
@@ -318,8 +318,10 @@
         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))
+                msg = "{num} loop{s} -> {secs:.{prec}g} secs"
+                plural = (number != 1)
+                print(msg.format(num=number, s='s' if plural else '',
+                                  secs=time_taken, prec=precision))
         try:
             number, _ = t.autorange(callback)
         except:
@@ -333,7 +335,7 @@
     best = min(r)
     if verbose:
         print("raw times:", " ".join(["%.*g" % (precision, x) for x in r]))
-    print("%d loops," % number, end=' ')
+    print("%d loop%s," % (number, 's' if number != 1 else ''), end=' ')
     usec = best * 1e6 / number
     if time_unit is not None:
         scale = units[time_unit]
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -88,6 +88,11 @@
 Library
 -------
 
+- Issue #28240: timeit autorange now uses a single loop iteration if the
+  benchmark takes less than 10 seconds, instead of 10 iterations.
+  "python3 -m timeit -s 'import time' 'time.sleep(1)'" now takes 4 seconds
+  instead of 40 seconds.
+
 - Distutils.sdist now looks for README and setup.py files with case
   sensitivity. This behavior matches that found in Setuptools 6.0 and
   later. See `setuptools 100

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list