Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 09f4f71

Browse files
author
Steven D'Aprano
committed
Issue6422 add autorange method to timeit.Timer
1 parent 9171a8b commit 09f4f71

3 files changed

Lines changed: 69 additions & 13 deletions

File tree

Doc/library/timeit.rst

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,8 +100,8 @@ The module defines three convenience functions and a public class:
100100
can be controlled by passing a namespace to *globals*.
101101

102102
To measure the execution time of the first statement, use the :meth:`.timeit`
103-
method. The :meth:`.repeat` method is a convenience to call :meth:`.timeit`
104-
multiple times and return a list of results.
103+
method. The :meth:`.repeat` and :meth:`.autorange` methods are convenience
104+
methods to call :meth:`.timeit` multiple times.
105105

106106
The execution time of *setup* is excluded from the overall timed execution run.
107107

@@ -134,6 +134,21 @@ The module defines three convenience functions and a public class:
134134
timeit.Timer('for i in range(10): oct(i)', 'gc.enable()').timeit()
135135

136136

137+
.. method:: Timer.autorange(callback=None)
138+
139+
Automatically determine how many times to call :meth:`.timeit`.
140+
141+
This is a convenience function that calls :meth:`.timeit` repeatedly
142+
so that the total time >= 0.2 second, returning the eventual
143+
(number of loops, time taken for that number of loops). It calls
144+
:meth:`.timeit` with *number* set to successive powers of ten (10,
145+
100, 1000, ...) up to a maximum of one billion, until the time taken
146+
is at least 0.2 second, or the maximum is reached.
147+
148+
If *callback* is given and is not *None*, it will be called after
149+
each trial with two arguments: ``callback(number, time_taken)``.
150+
151+
137152
.. method:: Timer.repeat(repeat=3, number=1000000)
138153

139154
Call :meth:`.timeit` a few times.

Lib/test/test_timeit.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,28 @@ def test_main_exception_fixed_reps(self):
354354
s = self.run_main(switches=['-n1', '1/0'])
355355
self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')
356356

357+
def autorange(self, callback=None):
358+
timer = FakeTimer(seconds_per_increment=0.001)
359+
t = timeit.Timer(stmt=self.fake_stmt, setup=self.fake_setup, timer=timer)
360+
return t.autorange(callback)
361+
362+
def test_autorange(self):
363+
num_loops, time_taken = self.autorange()
364+
self.assertEqual(num_loops, 1000)
365+
self.assertEqual(time_taken, 1.0)
366+
367+
def test_autorange_with_callback(self):
368+
def callback(a, b):
369+
print("{} {:.3f}".format(a, b))
370+
with captured_stdout() as s:
371+
num_loops, time_taken = self.autorange(callback)
372+
self.assertEqual(num_loops, 1000)
373+
self.assertEqual(time_taken, 1.0)
374+
expected = ('10 0.010\n'
375+
'100 0.100\n'
376+
'1000 1.000\n')
377+
self.assertEqual(s.getvalue(), expected)
378+
357379

358380
if __name__ == '__main__':
359381
unittest.main()

Lib/timeit.py

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,26 @@ def repeat(self, repeat=default_repeat, number=default_number):
207207
r.append(t)
208208
return r
209209

210+
def autorange(self, callback=None):
211+
"""Return the number of loops so that total time >= 0.2.
212+
213+
Calls the timeit method with *number* set to successive powers of
214+
ten (10, 100, 1000, ...) up to a maximum of one billion, until
215+
the time taken is at least 0.2 second, or the maximum is reached.
216+
Returns ``(number, time_taken)``.
217+
218+
If *callback* is given and is not None, it will be called after
219+
each trial with two arguments: ``callback(number, time_taken)``.
220+
"""
221+
for i in range(1, 10):
222+
number = 10**i
223+
time_taken = self.timeit(number)
224+
if callback:
225+
callback(number, time_taken)
226+
if time_taken >= 0.2:
227+
break
228+
return (number, time_taken)
229+
210230
def timeit(stmt="pass", setup="pass", timer=default_timer,
211231
number=default_number, globals=None):
212232
"""Convenience function to create Timer object and call timeit method."""
@@ -295,17 +315,16 @@ def main(args=None, *, _wrap_timer=None):
295315
t = Timer(stmt, setup, timer)
296316
if number == 0:
297317
# determine number so that 0.2 <= total time < 2.0
298-
for i in range(1, 10):
299-
number = 10**i
300-
try:
301-
x = t.timeit(number)
302-
except:
303-
t.print_exc()
304-
return 1
305-
if verbose:
306-
print("%d loops -> %.*g secs" % (number, precision, x))
307-
if x >= 0.2:
308-
break
318+
callback = None
319+
if verbose:
320+
def callback(number, time_taken):
321+
msg = "{num} loops -> {secs:.{prec}g} secs"
322+
print(msg.format(num=number, secs=time_taken, prec=precision))
323+
try:
324+
number, _ = t.autorange(callback)
325+
except:
326+
t.print_exc()
327+
return 1
309328
try:
310329
r = t.repeat(repeat, number)
311330
except:

0 commit comments

Comments
 (0)