In one of the previous articles (Measure Time in Python – time.time() vs time.clock()), we learned how to use the module timeit to benchmark a program. Since the program we timed in that article includes only raw statements instead of functions, we’re going to explore how to actually time a function in Python.

Time a Python Function Without Arguments

The module function timeit.timeit(stmt, setup, timer, number) accepts four arguments:

  • stmt which is the statement you want to measure; it defaults to ‘pass’.
  • setup which is the code that you run before running the stmt; it defaults to ‘pass’.
  • timer which a timeit.Timer object; it usually has a sensible default value so you don’t have to worry about it.
  • number which is the number of executions you’d like to run the stmt.

Where the timeit.timeit() function returns the number of seconds it took to execute the code.

Now suppose you want to measure a function costly_func implemented like this:

def costly_func():
   return map(lambda x: x^2, range(10))

You can measure its execution time using timeit:

>>> import timeit
>>> def costly_func():
...     return map(lambda x: x^2, range(10))
...
>>> # Measure it since costly_func is a callable without argument
>>> timeit.timeit(costly_func)
2.547558069229126 
>>> # Measure it using raw statements
>>> timeit.timeit('map(lambda x: x^2, range(10))')
2.3258371353149414

Notice that we used two ways to measure this function. The first way passed in the Python callable costly_func while the second way passed in the raw Python statements of costly_func. Although the first way’s timing overhead is a little larger than the second way, we usually prefer the first one since it’s more readable and easier to maintain.

Time a Python Function with Arguments

We can use decorators to measure functions with arguments. Suppose our costly_func is defined in the following way:

def costly_func(lst):
    return map(lambda x: x^2, lst)

You could measure it using a decorator defined like this:

def wrapper(func, *args, **kwargs):
    def wrapped():
        return func(*args, **kwargs)
    return wrapped

Now you use this decorator to wrap costly_func with arguments into a function without arguments in order to pass it into timeit.timeit.

>>> def wrapper(func, *args, **kwargs):
...     def wrapped():
...         return func(*args, **kwargs)
...     return wrapped
...
>>> def costly_func(lst): 
...     return map(lambda x: x^2, lst) 
... 
>>> short_list = range(10) 
>>> wrapped = wrapper(costly_func, short_list)
>>> timeit.timeit(wrapped, number=1000)
0.0032510757446289062
>>> long_list = range(1000)
>>> wrapped = wrapper(costly_func, long_list)
>>> timeit.timeit(wrapped, number=1000)
0.14835596084594727

Time a Python Function From Another Module

Suppose you have the function costly_func defined in another module mymodule, how could you measure its time since it’s not locally accessible? Well, you could import it into the local namespace or use the setup argument.

# mymodule.py
def costly_func():
    return map(lambda x: x^2, range(1000))
>>> timeit.timeit('costly_func()', setup='from mymodule import costly_func', number=1000)
0.15768003463745117
# OR just import it in the local namespace
>>> from mymodule import costly_func
>>> timeit.timeit(costly_func, number=1000)
0.15358710289001465

Summary and Tips

In this article, we learned how to measure a Python function’s execution time using timeit.timeit. Usually, we prefer importing and passing the Python function as a callable object into timeit.timeit since it’s more maintainable. In addition, remember that the default number of executions is 1000000 which could increase the total execution time a lot for certain complex functions.

Leave a Reply

Your email address will not be published. Required fields are marked *