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 thestmt
; 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 thestmt
.
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:
[python]
def costly_func():
return map(lambda x: x^2, range(10))
[/python]
You can measure its execution time using timeit:
[python]
>>> 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
[/python]
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:
[python]
def costly_func(lst):
return map(lambda x: x^2, lst)
[/python]
You could measure it using a decorator defined like this:
[python]
def wrapper(func, *args, **kwargs):
def wrapped():
return func(*args, **kwargs)
return wrapped
[/python]
Now you use this decorator to wrap costly_func
with arguments into a function without arguments in order to pass it into timeit.timeit
.
[python]
>>> 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
[/python]
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.
[python]
# mymodule.py
def costly_func():
return map(lambda x: x^2, range(1000))
[/python]
[python]
>>> 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
[/python]
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.