I am using Spyder (Python 3.9) and I am writing some scripts to show the importance of coding efficiently, by comparing the execution time of different lines of code that do the same thing with different ways.
Suppose we have some symbolic function whose graph we want to plot. We could use scipy.plot directly, or we could lambdify the function to obtain a numpy function f, generate an array x and use plot(x,f(x)) from matplotlib.pyplot. I'd like to compare the two options.
I investigated the proper ways to measure execution time in Python and I found the timeit module, which I used for other comparisons. Seems to me that is better than time.perf_counter() since it executes the code several times and gives more reliable results.
When I use it, seems that the symbolic is way faster, but I think this is not really the case. Here is my attemp at measuring the two methods. When I use time.perf_counter I obtain that the numeric method can be way quickier. So what did I do wrong with timeit?
import timeit
setup = '''
import numpy as np
import matplotlib.pyplot as plt
import sympy as sy
t = sy.Symbol('t')
expression = sy.exp(t)'''
code_sym = '''
sy.plot(expression, xlim = [0,5], ylim = [0,150], show = False)
'''
code_num = '''
f= sy.lambdify(t, expression, 'numpy')
x = np.linspace(0,5,100)
plt.plot(x,f(x))
'''
num_exec = 10
time1 = timeit.timeit(stmt=code_sym, setup = setup, number = num_exec)
print('Time w/ symbolic:' , time1, ' sec')
time2 = timeit.timeit(stmt = code_num, setup = setup, number = num_exec)
print('Time w/ numeric:' , time2, ' sec')
There may be an issue with "warm-up" time that results in apparently longer time for the numeric approach. To ensure it does not weight on the results, you should check if the average time relation between the two approaches changes significantly when you increase the number of iterations.
To check that I added the following line at the end of your code:
For 10 iterations I got:
For 100:
For 1000:
For 10000:
As you can see the ratios between the timings are declining with the growing number of iterations (rounded: 191, 45, 22, 16) which confirms the suggestion that the warm-up code eats up a significant portion of time and the potential (we do not know if the eventual results would confirm it) advantage of numerical vs. symbolic approach would justify using the numeric even for very large numbers of iterations.
This is why it is important to measure. Our intuitions can be grossly wrong.