Small experiment to see how to easily profile some pure Python and Cython-enhanced Python code, for both time and memory, directly within the Jupyter notebook.
%load_ext watermark
%watermark -v -m -g
def recip_square(i):
return 1./i**2
def approx_pi(n=10000000):
val = 0.
for k in range(1,n+1):
val += recip_square(k)
return (6 * val)**.5
%time approx_pi()
%timeit approx_pi()
# Thanks to https://nbviewer.jupyter.org/gist/minrk/7715212
from __future__ import print_function
from IPython.core import page
def myprint(s):
try:
print(s['text/plain'])
except (KeyError, TypeError):
print(s)
page.page = myprint
%prun approx_pi()
After installing line_profiler
and enabling the IPython magic, we can use it
%lprun -T /tmp/test_lprun.txt -f recip_square -f approx_pi approx_pi()
%cat /tmp/test_lprun.txt
With the memory profiler:
%load_ext memory_profiler
%mprun -T /tmp/test_mprun.txt -f recip_square -f approx_pi approx_pi()
%cat /tmp/test_mprun.txt
%memit approx_pi()
%load_ext cython
%%cython -3
# cython: profile=True
def recip_square2(int i):
return 1./i**2
def approx_pi2(int n=10000000):
cdef double val = 0.
cdef int k
for k in range(1, n + 1):
val += recip_square2(k)
return (6 * val)**.5
%timeit approx_pi()
%timeit approx_pi2()
%prun approx_pi2()
And with just a simple change, by typing the intermediate function:
%%cython -3
# cython: profile=True
cdef double recip_square3(int i):
return 1./(i**2)
def approx_pi3(int n=10000000):
cdef double val = 0.
cdef int k
for k in range(1, n + 1):
val += recip_square3(k)
return (6 * val)**.5
%timeit approx_pi3()
%prun approx_pi3()
And with an inlined function?
%%cython -3
cdef inline double recip_square4(int i):
return 1./(i**2)
def approx_pi4(int n=10000000):
cdef double val = 0.
cdef int k
for k in range(1, n + 1):
val += recip_square4(k)
return (6 * val)**.5
%timeit approx_pi4()
%prun approx_pi4()
And with just a simple change, by using i*i
instead of i**2
:
%%cython -3
# cython: profile=True
from __future__ import division
import cython
@cython.profile(False)
cdef inline double recip_square5(int i):
return 1./(i*i)
def approx_pi5(int n=10000000):
cdef double val = 0.
cdef int k
for k in range(1, n + 1):
val += recip_square5(k)
return (6 * val)**.5
%timeit approx_pi4()
%prun approx_pi4()
That's it for today.