custom timeit, from __main__ import everything necessary
Asked Answered
T

1

1

The magic command %timeit is not made to be used in a script.

In Use Python's `timeit` from a program but functioning the same way as the command line?, the following function was proposed by the user Unutbu:

import timeit

def timeit_auto(stmt="pass", setup="pass", repeat=3):
    """
    https://mcmap.net/q/110010/-use-python-39-s-timeit-from-a-program-but-functioning-the-same-way-as-the-command-line/190597 (endolith)
    Imitate default behavior when timeit is run as a script.

    Runs enough loops so that total execution time is greater than 0.2 sec,
    and then repeats that 3 times and keeps the lowest value.

    Returns the number of loops and the time for each loop in microseconds
    """
    t = timeit.Timer(stmt, setup)

    # determine number so that 0.2 <= total time < 2.0
    for i in range(1, 10):
        number = 10**i
        x = t.timeit(number) # seconds
        if x >= 0.2:
            break
    r = t.repeat(repeat, number)
    best = min(r)
    usec = best * 1e6 / number
    return number, usec

Which can be used as follows:

import timeit
import utils_timeit as UT

def foo():
    total = 0
    for i in range(10000):
        total += i**3
    return total

num, timing = UT.timeit_auto(setup='from __main__ import foo', stmt='foo()')
print(num, timing)

Which is a very smart and handy solution. However, I am looking for a even better way. I don't want to give a setup statement to the function, I think it is very obnoxious. If I have a dataframe df and a column name column_name_a, I don't want to have to explicitly state setup='from __main__ import df, column_name_a' whenever I want to time an operation to df[column_name_a].

Is there a nice way to fix it? I did it myself by adding this to the function, such that all the variables that are required are added to the setup statement in a loop by catching errors:

condition = True

setup = 'from __main__ import _'

while condition:
    try:
        t = timeit.Timer(stmt, setup)
        t.repeat(repeat, number)
        condition = False
    except NameError as err:
        str_err = str(err)
        extra_import = str_err.split(sep="'")[1]
        setup = setup + ', ' + extra_import

this is ofcourse not a very subtle solution. Does anyone have a better approach?

Furthermore, in the same thread (Use Python's `timeit` from a program but functioning the same way as the command line?), someone proposed to use the Timer.autorange method. I am not sure how to include this in the function. Can anyone help me with that?

Towney answered 23/8, 2021 at 19:54 Comment(0)
J
1

timeit.Timer objects have accepted a globals paramter since python 3.5, which allows you to use your module namespace directly. The autorange method was added in python 3.6, and is the official version of Untubu's solution.

Here is how you can put it all together:

>>> from timeit import Timer
>>> def foo(): ...
>>> num, timing = Timer(stmt='foo()', globals=globals()).autorange()

You can use timeit.Timer objects directly for this exact application as of python 3.6. If you have version 3.5, you can modify Untubu's solution as follows:

def timeit_auto(stmt="pass", setup="pass", repeat=3, globals=None):
    t = timeit.Timer(stmt, setup, globals=globals())
    ...
Jared answered 25/8, 2021 at 20:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.