Measuring code speed
What is the fastest way of doing things?
We already know how to measure the execution speed of a piece of code (or block of code) with %timeit
.
%timeit
is a magic function from IPython -
the only catch is that it must be used from an IPython interpreter.
However, the major advantage of using %timeit
is that you don’t have to import anything,
and you’ll never accidentally choose the wrong number of iterations to time
(and get stuck waiting forever for your timing results to come back).
To review:
%timeit
- times a single line.
%timeit [1] * 10000
10000 loops, best of 3: 35.1 µs per loop
%%timeit
- times an IPython cell.
%%timeit
last = 0
for i in range(10000):
last += i
1000 loops, best of 3: 866 µs per loop
Profiling code
Though the above technique tells you which of two pieces of code is faster, it does not identify which portion of code is the slowest. Profiling code allows us to identify areas for improvement.
Python ships with a profling module called cProfile
.
cProfile
lets us see exactly how long was used to run each line of code as well as all internal calls made by a function.
Slower code will have longer execution times,
and is an area of an improvement for us to focus our efforts on.
To profile a block of code, we use the cProfile.run()
function
(the code to profile is input as a string).
cProfile.run(
'''
test = np.arange(10000000)
np.sin(test)
'''
)
4 function calls in 0.519 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.468 0.468 0.519 0.519 <string>:2(<module>)
1 0.000 0.000 0.519 0.519 {built-in method builtins.exec}
1 0.050 0.050 0.050 0.050 {built-in method numpy.core.multiarray.arange}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
Profiling script execution
We can also profile scripts from the command-line using the following syntax
(the -s cumtime
sorts code by cumulative time spent in each function):
python3 -m cProfile -s cumtime some_script.py
However, this is not super useful, especially if the call stack is huge (it can be really tough to interpret the results!) There is a nice visualizer called “Snakeviz” we can use to get around this.
For this demo, make sure you’ve got the requests
and snakeviz
modules installed:
pip install --user requests snakeviz
Now create a script that looks like the following (called “iss.py”?):
import requests
r = requests.get('http://api.open-notify.org/iss-now.json')
server_response = r.json()
iss_location = server_response['iss_position']
print('The International Space Station is currently at {}, {}'.
format(iss_location['latitude'], iss_location['longitude']))
We can create a profile of our program with the following commands:
python3 -m cProfile -o iss.profile iss.py
snakeviz iss.profile
You now have a way to visualize our code execution.
Looking at the code profile, the vast majority of time was actually spent importing the requests module,
with a small amount of time spent performing the HTTP request (requests.get()
).