how to pass parameters of a function when using timeit.Timer()

PythonTimer

Python Problem Overview


This is the outline of a simple program

# some pre-defined constants
A = 1
B = 2
    
# function that does something critical
def foo(num1, num2):
    # do something
 
# main program.... do something to A and B
for i in range(20):
    # do something to A and B
    # and update A and B during each iteration

import timeit
t = timeit.Timer(stmt="foo(num1,num2)")  
print t.timeit(5)

I just keep getting "global name foo is not defined"..... Can anyone help me on this? Thanks!

Python Solutions


Solution 1 - Python

The functions can use arguments in timeit if these are created using closures, we can add this behaviours by wrapping them in another function.

def foo(num1, num2):
    def _foo():
        # do something to num1 and num2
        pass
    return _foo

A = 1
B = 2

import timeit
t = timeit.Timer(foo(A,B))  
print(t.timeit(5))

or shorter, we can use functools.partial instead of explicit closures declaration

def foo(num1, num2):
    # do something to num1 and num2
    pass

A = 1
B = 2

import timeit, functools
t = timeit.Timer(functools.partial(foo, A, B)) 
print(t.timeit(5))

EDIT using lambda, thanks @jupiterbjy

we can use lambda function without parameters instead of functools library

def foo(num1, num2):
    # do something to num1 and num2
    pass

A = 1
B = 2

import timeit
t = timeit.Timer(lambda: foo(A, B)) 
print (t.timeit(5))

Solution 2 - Python

The code snippets must be self-contained - they cannot make external references. You must define your values in the statement-string or setup-string:

import timeit

setup = """
A = 1
B = 2

def foo(num1, num2):
    pass

def mainprog():
    global A,B
    for i in range(20):
        # do something to A and B
        foo(A, B)
"""

t = timeit.Timer(stmt="mainprog()" setup=setup)
print(t.timeit(5))

Better yet, rewrite your code to not use global values.

Solution 3 - Python

Supposing that your module filename is test.py

# some pre-defined constants
A = 1
B = 2

# function that does something critical
def foo(n, m):
    pass

# main program.... do something to A and B
for i in range(20):
    pass

import timeit
t = timeit.Timer(stmt="test.foo(test.A, test.B)", setup="import test")  
print t.timeit(5)

Solution 4 - Python

I usually create an extra function:

def f(x,y):
    return x*y

v1 = 10
v2 = 20

def f_test():
    f(v1,v2)

print(timeit.timeit("f_test()", setup="from __main__ import f_test"))

Solution 5 - Python

Your function needs to be define in the setup string. A good way to do this is by setting up your code in a module, so you simple have to do

t = timeit.Timer("foo(num1, num2)", "from myfile import foo")
t.timeit(5)

Otherwise, you'll have to define all of the setup as a string inside the setup statement.

setup = """
 # some pre-defined constants
A = 1
B = 2

# function that does something critical
def foo(num1, num2):
    # do something

# main program.... do something to A and B
for i in range(20):
    # do something to A and B
    # and update A and B during each iteration
"""

t = timeit.Timer("foo(num1, num2)", setup)
t.timeit(5)

Something awesome I just found out about is a shortcut for iPython that uses cProfile.

def foo(x, y):
    print x*y

%prun foo("foo", 100)

Solution 6 - Python

Another option is to bind the function to its arguments via functools (similar to std::bind). Then you don't need to pass arguments to timeit, the callable returned by functool.partial takes care of that:

    def findMax(n):#n is an array
        m = 0
        c = 0
        for i in range(len(n)):
            c += 1
            if m < n[i]:
                m = n[i]
        return m, c


import timeit
import functools
a = [6, 2, 9, 3, 7, 4, 5]
t = timeit.Timer(functools.partial(findMax,a))
t.timeit(100)

Solution 7 - Python

There is a much simpler solution (at least for Python 3), you can cause the code to be executed within your current global namespace:

t = timeit.Timer(stmt="foo(num1,num2)", globals=globals())

https://docs.python.org/3/library/timeit.html#examples I know globals are not preferred, but if you are just making a quick script to check something I think this is the easiest implementation.

Solution 8 - Python

Here is an example of how to compartmentalize the timing routine, without calling globals

def foo(a, b):
    '''Do something to `a` and `b`'''
    return a + b

def time_foo():
    '''Create timer object simply without using global variables'''
    import timeit

    _foo = foo
    a = 1
    b = 2

    # Get `Timer` oject, alternatively just get time with `timeit.timeit()`
    t = timeit.Timer('_foo(a, b)', globals=locals())

    return t

You could even generalize this if you wanted to use the same timeit function to time other functions. Here is an example with your example main() routine:

def foo1(a, b):
    '''Add `a` and `b`'''
    return a + b

def foo2(a, b):
    '''More math on `a` and `b`'''
    return (a**2 * b)**2

def time_foo(func, **kwargs):
    '''Create timer object simply without using global variables'''
    import timeit
    return timeit.timeit('func(**kwargs)', globals=locals())

def run():
    '''Modify inputs to foo and see affect on execution time'''

    a = 1
    b = 2
    for i in range(10):
        # Update `a` and `b`
        a += 1
        b += 2
        # Pass args to foo as **kwargs dict
        print('foo1 time: ', time_foo(foo1, **{'a':a, 'b':b}))
        print('foo2 time: ', time_foo(foo2, **{'a':a, 'b':b}))

    return None

Solution 9 - Python

This should work:

import timeit

def f(x,y):
    return x*y

x = 5
y = 7

print(timeit.timeit(stmt='f(x,y)',
                    setup='from __main__ import f, x, y',
                    number=1000))

Solution 10 - Python

I prefer creating a static class with all the Data ready to be picked up prior of running the timer. >Another note, it is better to do test runs in function rather then in the global space, as the global space isn't taking advantage of FAST_LOAD https://stackoverflow.com/questions/11241523/why-does-python-code-run-faster-in-a-function

class Data(object):
	"""Data Creation"""
	x = [i for i in range(0, 10000)]
	y = tuple([i for i in range(0, 10000)])
	def __init__(self):
		pass

import timeit

def testIterator(x):
	for i in range(10000):
		z = i


print timeit.timeit("testIterator(Data.x)", setup="from __main__ import testIterator, Data", number=50)
print timeit.timeit("testIterator(Data.y)", setup="from __main__ import testIterator, Data", number=50)

Solution 11 - Python

I was playing around with timing in Python 3.7 today and trying to pass functions and variables into the timer. This is what I came up with.

import re

text = "This         is      a  test of the      emergency broadcast       system"

def regex(text):
    return re.sub(r"(\s)\1{1,}", r"\1", text)
    
def loop_while(text):
    if "  " in text:
        while "  " in text:
            text = text.replace("  ", " ")

    return text

if __name__ == "__main__":
    import timeit

    callable_functions = [item for item in locals().items() if callable(item[1])]

    for func_name, func in callable_functions:
        elapsed_time = timeit.timeit(f"{func_name}(text)", globals=globals(), number=100000)
        print(f"{func_name}: {elapsed_time} \n{func(text)}\n")

This outputs:

> regex: 1.378352418
> This is a test of the emergency broadcast system > > loop_while: 0.15858950299999997
> This is a test of the emergency > broadcast system

Then all it takes to test a new version is adding in a new function. Something like:

def split_join(text):
    return " ".join(text.split())

Now it outputs:

> regex: 1.378352418
> This is a test of the emergency broadcast system > > loop_while: 0.15858950299999997
> This is a test of the emergency broadcast system > > split_join: 0.05700970800000005
> This is a test of the emergency broadcast system

Solution 12 - Python

You have to create the variable within the setup string. Here I import the function, and create one of the variables that i pass to it. I also set one of the variables by casting it to the stmt string

SETUP = '''
from __main__ import policy_iteration
from environments.gridworld import GridworldEnv

env = GridworldEnv()
'''

discount = 5
timeit.timeit("policy_iteration(env,discount_factor="+str(discount)+")",
                          setup= SETUP,
                          number=10))

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionCppLearnerView Question on Stackoverflow
Solution 1 - PythonJose Ricardo Bustos M.View Answer on Stackoverflow
Solution 2 - PythonHugh BothwellView Answer on Stackoverflow
Solution 3 - PythonLucas S.View Answer on Stackoverflow
Solution 4 - PythonVinicius VeronezeView Answer on Stackoverflow
Solution 5 - PythonYuji 'Tomita' TomitaView Answer on Stackoverflow
Solution 6 - PythonMahi KumarView Answer on Stackoverflow
Solution 7 - PythonRustyTomsView Answer on Stackoverflow
Solution 8 - PythonryanjdillonView Answer on Stackoverflow
Solution 9 - PythonFooBar167View Answer on Stackoverflow
Solution 10 - Pythonuser1767754View Answer on Stackoverflow
Solution 11 - PythonGollyJerView Answer on Stackoverflow
Solution 12 - PythonPigSpiderView Answer on Stackoverflow