When reviewing questions here on Code Review SE, I sometimes find my self wanting to test different functions in order to do a performance review. However since I'm inherently lazy, I wonder what is the most effective or best practice for doing timing of functions in Python?
To make this an apt question for Code Review here are my two current, working solutions, which I want reviewed for unintended performance penalties, shortness and readability of code. Will welcome better suggestions on how to improve and ease my testing regime.
The difference of the two test methods are:
# From do_timing_ver1
test_case = "from {0} import {1}; {1}({2}, {3})"
...
elapsed_time = timeit.timeit(test_case.format(__name__, test_function, A, B),
number=TEST_RUNS)
# From do_timing_ver2
timer = timeit.Timer(partial(test_function, A, B))
elapsed_time = timer.timeit(TEST_RUNS)
And here is the full code, with some dummy functions to be tested:
from functools import partial
from random import randint
import timeit
import time
def milli_sleep(milli_seconds):
time.sleep(milli_seconds / 1000.0)
def foo(a, b):
# Do stuff related to a and b, possibly calling other methods
# I.e. OPs original code
milli_sleep(800)
pass
def bar(a, b):
# Do stuff related to a and b, possibly calling other methods
# I.e. my solution
milli_sleep(200)
def baz(a, b):
# Do stuff related to a and b, possibly calling other methods
# I.e. some other solution
milli_sleep(400)
def do_timing_ver1():
A = [1, 2, 3, 4]
B = False
TEST_RUNS = 1
test_case = "from {0} import {1}; {1}({2}, {3})"
print('Timing version 1')
for test_function in ('foo',
'bar',
'baz',
):
print ('\nTesting {}'.format(test_function))
elapsed_time = timeit.timeit(test_case.format(__name__, test_function, A, B),
number=TEST_RUNS)
print(' execution time: {:,.4f} seconds'.format(elapsed_time))
def do_timing_ver2():
A = [1, 2, 3, 4]
B = False
TEST_RUNS = 1
print('\n\nTiming version 2')
for test_function in (foo,
bar,
baz,):
print('\nTesting {}'.format(test_function.__name__))
timer = timeit.Timer(partial(test_function, A, B))
elapsed_time = timer.timeit(TEST_RUNS)
print(' execution time: {:,.4f}s'.format(elapsed_time))
def main():
do_timing_ver1()
do_timing_ver2()
if __name__ == '__main__':
main()
On a sidenote
The optimal solution would be for me to include a personal library, and the do a call like:
from utilities import time_performance
time_performance(['foo', 'bar', 'baz'], A, B)
(Added) Using a module
A newly written module, utilities.py
, for the sidenote to work is:
def time_performance(test_runs, module_name, functions, *params):
"""Time execution of all the <functions> passing all parameters given"""
test_case = 'from {0} import {1}; {1}({2})'
print('\n\nTime performance')
for test_function in functions:
print ('\nTesting {}'.format(test_function))
elapsed_time = timeit.timeit(test_case.format(module_name,
test_function,
', '.join(repr(param) for param in params)),
number=test_runs)
print(' execution time: {:,.4f} seconds'.format(elapsed_time))
which can be called with:
time_performance(1, __name__, ['foo', 'bar', 'baz'], [1, 2, 3, 4], False)
In other words, a few extra options, and I really don't like depending on the repr()
function to pass arguments. But it does indeed work in my test case.
Reiteration of review issues
- Can you review either version, or suggest even better version for timing similar functions with parameters?
- Can you review or improve the module version of such a performance tester?