Using Cython with Jupyter

Optimizing Cython code requires substantial trial and error. Fortunately, Cython tools can be conveniently accessed through the Jupyter notebook for a more streamlined and integrated experience.

You can launch a notebook session by typing jupyter notebook in the command line and you can load the Cython magic by typing %load_ext cython in a cell.

As already mentioned earlier, the %%cython magic can be used to compile and load the Cython code inside the current session. As an example, we may copy the contents of cheb.py into a notebook cell:

    %%cython
import numpy as np

cdef int max(int a, int b):
return a if a > b else b

cdef int chebyshev(int x1, int y1, int x2, int y2):
return max(abs(x1 - x2), abs(y1 - y2))

def c_benchmark():
a = np.random.rand(1000, 2)
b = np.random.rand(1000, 2)

for x1, y1 in a:
for x2, y2 in b:
chebyshev(x1, x2, y1, y2)

A useful feature of the %%cython magic is the -a option that will compile the code and produce an annotated view (just like the command line -a option) of the source directly in the notebook, as shown in the following screenshot:

This allows you to quickly test different versions of your code and also use the other integrated tools available in Jupyter. For example, we can time and profile the code (provided that we activate the profile directive in the cell) in the same session using tools such as %prun and %timeit. For example, we can inspect the profiling results by taking advantage of the %prun magic, as shown in the following screenshot:

It is also possible to use the line_profiler tool we discussed in Chapter 1Benchmarking and Profiling, directly in the notebook. In order to support line annotations, it is necessary to do the following things:

  • Enable the linetrace=True and binding=True compiler directives
  • Enable the CYTHON_TRACE=1 flag at compile time

This can be easily accomplished by adding the respective arguments to the %%cython magic, and by setting the compiler directives, as shown in the following code:

    %%cython -a -f -c=-DCYTHON_TRACE=1
# cython: linetrace=True
# cython: binding=True

import numpy as np

cdef int max(int a, int b):
return a if a > b else b

def chebyshev(int x1, int y1, int x2, int y2):
return max(abs(x1 - x2), abs(y1 - y2))

def c_benchmark():
a = np.random.rand(1000, 2)
b = np.random.rand(1000, 2)

for x1, y1 in a:
for x2, y2 in b:
chebyshev(x1, x2, y1, y2)

Once the code is instrumented, we can profile using the %lprun magic:

%lprun -f c_benchmark c_benchmark()
# Output:
Timer unit: 1e-06 s

Total time: 2.322 s
File: /home/gabriele/.cache/ipython/cython/_cython_magic_18ad8204e9d29650f3b09feb48ab0f44.pyx
Function: c_benchmark at line 11

Line # Hits Time Per Hit % Time Line Contents
==============================================================
11 def c_benchmark():
12 1 226 226.0 0.0 a = np.random.rand...
13 1 67 67.0 0.0 b = np.random.rand...
14
15 1001 1715 1.7 0.1 for x1, y1 in a:
16 1001000 1299792 1.3 56.0 for x2, y2 in b:
17 1000000 1020203 1.0 43.9 chebyshev...

As you can see, a good chunk of time is actually spent in line 16, which is a pure Python loop and a good candidate for further optimization.

The tools available in Jupyter notebook allow for a fast edit-compile-test cycle so that you can quickly prototype and save time when testing different solutions.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.76.204