Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions Doc/library/timeit.rst
Original file line number Diff line number Diff line change
Expand Up @@ -143,21 +143,24 @@ 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)
.. method:: Timer.autorange(callback=None, target_time=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
so that the total time >= *Timer.target_time* seconds, returning the eventual
(number of loops, time taken for that number of loops). It calls
:meth:`.timeit` with increasing numbers from the sequence 1, 2, 5,
10, 20, 50, ... until the time taken is at least 0.2 seconds.
10, 20, 50, ... until the time taken is at least *target_time* seconds.

If *callback* is given and is not ``None``, it will be called after
each trial with two arguments: ``callback(number, time_taken)``.

.. versionadded:: 3.6

.. versionchanged:: next
The optional *target_time* parameter was added.


.. method:: Timer.repeat(repeat=5, number=1000000)

Expand Down Expand Up @@ -239,6 +242,13 @@ Where the following options are understood:

.. versionadded:: 3.5

.. option:: -t, --target-time=T

if :option:`--number` is 0, the code will run until it takes at
least this many seconds (default: 0.2)

.. versionadded:: next

.. option:: -v, --verbose

print raw timing results; repeat for more digits precision
Expand All @@ -254,7 +264,7 @@ similarly.

If :option:`-n` is not given, a suitable number of loops is calculated by trying
increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the total
time is at least 0.2 seconds.
time is at least :option:`--target-time` seconds (default: 0.2).

:func:`default_timer` measurements can be affected by other programs running on
the same machine, so the best thing to do when accurate timing is necessary is
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1090,6 +1090,11 @@ timeit
:ref:`environment variables <using-on-controlling-color>`.
(Contributed by Yi Hong in :gh:`139374`.)

* Make the target time of :meth:`timeit.Timer.autorange` configurable
and add ``--target-time`` option to the command-line interface.
(Contributed by Alessandro Cucci and Miikka Koskinen in :gh:`140283`.)


tkinter
-------

Expand Down
9 changes: 7 additions & 2 deletions Lib/test/test_timeit.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,10 +364,10 @@ def test_main_exception_fixed_reps(self):
s = self.run_main(switches=['-n1', '1/0'])
self.assert_exc_string(error_stringio.getvalue(), 'ZeroDivisionError')

def autorange(self, seconds_per_increment=1/1024, callback=None):
def autorange(self, seconds_per_increment=1/1024, callback=None, target_time=0.2):
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)
return t.autorange(callback, target_time=target_time)

def test_autorange(self):
num_loops, time_taken = self.autorange()
Expand All @@ -379,6 +379,11 @@ def test_autorange_second(self):
self.assertEqual(num_loops, 1)
self.assertEqual(time_taken, 1.0)

def test_autorange_with_target_time(self):
num_loops, time_taken = self.autorange(target_time=1.0)
self.assertEqual(num_loops, 2000)
self.assertEqual(time_taken, 2000/1024)

def test_autorange_with_callback(self):
def callback(a, b):
print("{} {:.3f}".format(a, b))
Expand Down
31 changes: 20 additions & 11 deletions Lib/timeit.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Library usage: see the Timer class.

Command line usage:
python timeit.py [-n N] [-r N] [-s S] [-p] [-h] [--] [statement]
python timeit.py [-n N] [-r N] [-s S] [-p] [-h] [-t T] [--] [statement]

Options:
-n/--number N: how many times to execute 'statement' (default: see below)
Expand All @@ -17,6 +17,9 @@
-p/--process: use time.process_time() (default is time.perf_counter())
-v/--verbose: print raw timing results; repeat for more digits precision
-u/--unit: set the output time unit (nsec, usec, msec, or sec)
-t/--target-time T: if --number is 0 the code will run until it
takes *at least* this many seconds
(default: 0.2)
-h/--help: print this usage message and exit
--: separate options from statement, use when statement starts with -
statement: statement to be timed (default 'pass')
Expand All @@ -28,7 +31,7 @@

If -n is not given, a suitable number of loops is calculated by trying
increasing numbers from the sequence 1, 2, 5, 10, 20, 50, ... until the
total time is at least 0.2 seconds.
total time is at least --target-time seconds.

Note: there is a certain baseline overhead associated with executing a
pass statement. It differs between versions. The code here doesn't try
Expand Down Expand Up @@ -57,6 +60,7 @@
default_number = 1000000
default_repeat = 5
default_timer = time.perf_counter
default_target_time = 0.2

_globals = globals

Expand Down Expand Up @@ -212,12 +216,13 @@ def repeat(self, repeat=default_repeat, number=default_number):
r.append(t)
return r

def autorange(self, callback=None):
"""Return the number of loops and time taken so that total time >= 0.2.
def autorange(self, callback=None, target_time=default_target_time):
"""Return the number of loops and time taken so that
total time >= target_time (default is 0.2 seconds).

Calls the timeit method with increasing numbers from the sequence
1, 2, 5, 10, 20, 50, ... until the time taken is at least 0.2
second. Returns (number, time_taken).
1, 2, 5, 10, 20, 50, ... until the target_time 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)``.
Expand All @@ -229,7 +234,7 @@ def autorange(self, callback=None):
time_taken = self.timeit(number)
if callback:
callback(number, time_taken)
if time_taken >= 0.2:
if time_taken >= target_time:
return (number, time_taken)
i *= 10

Expand Down Expand Up @@ -270,9 +275,10 @@ def main(args=None, *, _wrap_timer=None):
colorize = _colorize.can_colorize()

try:
opts, args = getopt.getopt(args, "n:u:s:r:pvh",
opts, args = getopt.getopt(args, "n:u:s:r:pt:vh",
["number=", "setup=", "repeat=",
"process", "verbose", "unit=", "help"])
"process", "target-time=",
"verbose", "unit=", "help"])
except getopt.error as err:
print(err)
print("use -h/--help for command line help")
Expand All @@ -281,6 +287,7 @@ def main(args=None, *, _wrap_timer=None):
timer = default_timer
stmt = "\n".join(args) or "pass"
number = 0 # auto-determine
target_time = default_target_time
setup = []
repeat = default_repeat
verbose = 0
Expand All @@ -305,6 +312,8 @@ def main(args=None, *, _wrap_timer=None):
repeat = 1
if o in ("-p", "--process"):
timer = time.process_time
if o in ("-t", "--target-time"):
target_time = float(a)
if o in ("-v", "--verbose"):
if verbose:
precision += 1
Expand All @@ -324,7 +333,7 @@ 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
# determine number so that total time >= target_time
callback = None
if verbose:
def callback(number, time_taken):
Expand All @@ -333,7 +342,7 @@ def callback(number, time_taken):
print(msg.format(num=number, s='s' if plural else '',
secs=time_taken, prec=precision))
try:
number, _ = t.autorange(callback)
number, _ = t.autorange(callback, target_time)
except:
t.print_exc(colorize=colorize)
return 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Make the target time of :meth:`timeit.Timer.autorange` configurable
and add ``--target-time`` option to the command-line interface of
:mod:`timeit`.
Loading