# IPython + jit decorator

Let’s say that we have this kind of code:

:

import numpy as np

:

image = np.random.rand(1024, 1024)

:

def laplace(image):
"""Laplace operator in NumPy for 2D images."""
laplacian = (
image[:-2, 1:-1] + image[2:, 1:-1] + image[1:-1, :-2] + image[1:-1, 2:]
- 4*image[1:-1, 1:-1]
)
thresh = np.abs(laplacian) > 0.05
return thresh


We can define a jitted function in IPython (and Jupyter, which uses IPython). The only limitation is that everything needed for the function (even np) has to be (re)defined in the same cell!

:

import numpy as np
from transonic import jit

@jit
def laplace_pythran(image):
"""Laplace operator in NumPy for 2D images."""
laplacian = (
image[:-2, 1:-1] + image[2:, 1:-1] + image[1:-1, :-2] + image[1:-1, 2:]
- 4*image[1:-1, 1:-1]
)
thresh = np.abs(laplacian) > 0.05
return thresh


We first call the function to launch the compilation (“warmup the jit”):

:

result = laplace_pythran(image)


Since the compilation is quit long, we explicitelly wait for the extension to benchmark the compiled version.

:

from transonic import wait_for_all_extensions
wait_for_all_extensions()

Pythranizing /home/pierre/.transonic/pythran/__jit__/__ipython__25970b035d85f309561968e246899d70/laplace_pythran.py


Now, let’s benchmark the 2 functions:

:

from transonic.util import timeit_verbose

namespace = {"laplace": laplace, "laplace_pythran": laplace_pythran, "image": image}

norm = timeit_verbose("laplace(image)", globals=namespace)
time_pythran = timeit_verbose("laplace_pythran(image)", globals=namespace, norm=norm)
print(f"\nIt corresponds to a speedup of ~ {norm/time_pythran:.1f}!")

laplace                          :     1 * norm
norm = 0.00831 s
laplace_pythran                  : 0.131 * norm

It corresponds to a speedup of ~ 7.6!