Making Python faster

The Julia website has a set of benchmarks to show how fast it is. These are for toy functions, but serve as a nice set of examples for the basics of using cython and numba in Python.

These examples are adapted from here

In [2]:
benchmarks = pd.read_pickle('julia_benchmarks.pic')
benchmarks.ix[:, :6]
Out[2]:
Fortran Julia Python R Matlab
1 fib 0.70 2.11 77.76 533.52 26.89
2 parse_int 5.05 1.45 17.02 45.73 802.52
3 quicksort 1.31 1.15 32.89 264.54 4.92
4 mandel 0.81 0.79 15.32 53.16 7.58
5 pi_sum 1.00 1.00 21.99 9.56 1.00
6 rand_mat_stat 1.45 1.66 17.93 14.56 14.52
7 rand_mat_mul 3.48 1.02 1.14 1.57 1.12
In [145]:
import time
import random
import numpy as np
In [119]:
from numba import njit, jit
In [120]:
%load_ext cython
The cython extension is already loaded. To reload it, use:
  %reload_ext cython

Timing and reproting functions

In [121]:
def timer(f, *args, **kwargs):
    start = time.clock()
    ans = f(*args, **kwargs)
    return ans, time.clock() - start
In [130]:
def report(fs, *args, **kwargs):
    ans, t = timer(fs[0], *args, **kwargs)
    print('%s: %.1f' % (fs[0].__name__, 1.0))
    for f in fs[1:]:
        ans_, t_ = timer(f, *args, **kwargs)
        print('%s: %.1f' % (f.__name__, t/t_))

Fib

In [63]:
def fib(n):
    if n<2:
        return n
    return fib(n-1)+fib(n-2)
In [64]:
fib(20)
Out[64]:
6765
In [113]:
%timeit fib(20)
100 loops, best of 3: 5.84 ms per loop
In [68]:
from functools import lru_cache

@lru_cache()
def fib_lru(n):
    if n<2:
        return n
    return fib(n-1)+fib(n-2)
In [114]:
%timeit fib_lru(20)
The slowest run took 92.76 times longer than the fastest. This could mean that an intermediate result is being cached
1000000 loops, best of 3: 213 ns per loop
In [115]:
%%cython -a

cpdef long fib_cython(int n):
    if n<2:
        return n
    return fib_cython(n-1) + fib_cython(n-2)
Out[115]:
Cython: _cython_magic_a7383439b4d9daca4b8a6d569c53a027.pyx

Generated by Cython 0.23.4

Yellow lines hint at Python interaction.
Click on a line that starts with a "+" to see the C code that Cython generated for it.

 1: 
+2: cpdef long fib_cython(int n):
static PyObject *__pyx_pw_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_1fib_cython(PyObject *__pyx_self, PyObject *__pyx_arg_n); /*proto*/
static long __pyx_f_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_fib_cython(int __pyx_v_n, CYTHON_UNUSED int __pyx_skip_dispatch) {
  long __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("fib_cython", 0);
/* … */
  /* function exit code */
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_1fib_cython(PyObject *__pyx_self, PyObject *__pyx_arg_n); /*proto*/
static PyObject *__pyx_pw_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_1fib_cython(PyObject *__pyx_self, PyObject *__pyx_arg_n) {
  int __pyx_v_n;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("fib_cython (wrapper)", 0);
  assert(__pyx_arg_n); {
    __pyx_v_n = __Pyx_PyInt_As_int(__pyx_arg_n); if (unlikely((__pyx_v_n == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L3_error:;
  __Pyx_AddTraceback("_cython_magic_a7383439b4d9daca4b8a6d569c53a027.fib_cython", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  __pyx_r = __pyx_pf_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_fib_cython(__pyx_self, ((int)__pyx_v_n));
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_fib_cython(CYTHON_UNUSED PyObject *__pyx_self, int __pyx_v_n) {
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("fib_cython", 0);
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_1 = __Pyx_PyInt_From_long(__pyx_f_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_fib_cython(__pyx_v_n, 0)); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_r = __pyx_t_1;
  __pyx_t_1 = 0;
  goto __pyx_L0;

  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_AddTraceback("_cython_magic_a7383439b4d9daca4b8a6d569c53a027.fib_cython", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
+3:     if n<2:
  __pyx_t_1 = ((__pyx_v_n < 2) != 0);
  if (__pyx_t_1) {
/* … */
  }
+4:         return n
    __pyx_r = __pyx_v_n;
    goto __pyx_L0;
+5:     return fib_cython(n-1) + fib_cython(n-2)
  __pyx_r = (__pyx_f_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_fib_cython((__pyx_v_n - 1), 0) + __pyx_f_46_cython_magic_a7383439b4d9daca4b8a6d569c53a027_fib_cython((__pyx_v_n - 2), 0));
  goto __pyx_L0;
In [116]:
%timeit fib_cython(20)
10000 loops, best of 3: 129 µs per loop
In [82]:
@njit
def fib_numba(n):
    a, b = 0, 1
    for i in range(n-1):
        a, b = b, a + b
    return b
In [83]:
%timeit -r3 -n3 fib_numba(20)
The slowest run took 49263.70 times longer than the fastest. This could mean that an intermediate result is being cached
3 loops, best of 3: 501 ns per loop
In [84]:
%load_ext cython
In [123]:
%%cython -a

def fib_cython_seq(int n):
    cdef int i
    cdef long a, b, tmp
    a = 0
    b = 1
    for i in range(n-1):
        tmp = a
        a = b
        b += tmp
    return b
Out[123]:
Cython: _cython_magic_106e06d591bbaf06dba1a9e2a3411245.pyx

Generated by Cython 0.23.4

Yellow lines hint at Python interaction.
Click on a line that starts with a "+" to see the C code that Cython generated for it.

 01: 
+02: def fib_cython_seq(int n):
/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_106e06d591bbaf06dba1a9e2a3411245_1fib_cython_seq(PyObject *__pyx_self, PyObject *__pyx_arg_n); /*proto*/
static PyMethodDef __pyx_mdef_46_cython_magic_106e06d591bbaf06dba1a9e2a3411245_1fib_cython_seq = {"fib_cython_seq", (PyCFunction)__pyx_pw_46_cython_magic_106e06d591bbaf06dba1a9e2a3411245_1fib_cython_seq, METH_O, 0};
static PyObject *__pyx_pw_46_cython_magic_106e06d591bbaf06dba1a9e2a3411245_1fib_cython_seq(PyObject *__pyx_self, PyObject *__pyx_arg_n) {
  int __pyx_v_n;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("fib_cython_seq (wrapper)", 0);
  assert(__pyx_arg_n); {
    __pyx_v_n = __Pyx_PyInt_As_int(__pyx_arg_n); if (unlikely((__pyx_v_n == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L3_error:;
  __Pyx_AddTraceback("_cython_magic_106e06d591bbaf06dba1a9e2a3411245.fib_cython_seq", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  __pyx_r = __pyx_pf_46_cython_magic_106e06d591bbaf06dba1a9e2a3411245_fib_cython_seq(__pyx_self, ((int)__pyx_v_n));
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_46_cython_magic_106e06d591bbaf06dba1a9e2a3411245_fib_cython_seq(CYTHON_UNUSED PyObject *__pyx_self, int __pyx_v_n) {
  CYTHON_UNUSED int __pyx_v_i;
  long __pyx_v_a;
  long __pyx_v_b;
  long __pyx_v_tmp;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("fib_cython_seq", 0);
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_AddTraceback("_cython_magic_106e06d591bbaf06dba1a9e2a3411245.fib_cython_seq", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple_ = PyTuple_Pack(6, __pyx_n_s_n, __pyx_n_s_n, __pyx_n_s_i, __pyx_n_s_a, __pyx_n_s_b, __pyx_n_s_tmp); if (unlikely(!__pyx_tuple_)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple_);
  __Pyx_GIVEREF(__pyx_tuple_);
/* … */
  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_106e06d591bbaf06dba1a9e2a3411245_1fib_cython_seq, NULL, __pyx_n_s_cython_magic_106e06d591bbaf06db); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_fib_cython_seq, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 03:     cdef int i
 04:     cdef long a, b, tmp
+05:     a = 0
  __pyx_v_a = 0;
+06:     b = 1
  __pyx_v_b = 1;
+07:     for i in range(n-1):
  __pyx_t_1 = (__pyx_v_n - 1);
  for (__pyx_t_2 = 0; __pyx_t_2 < __pyx_t_1; __pyx_t_2+=1) {
    __pyx_v_i = __pyx_t_2;
+08:         tmp = a
    __pyx_v_tmp = __pyx_v_a;
+09:         a = b
    __pyx_v_a = __pyx_v_b;
+10:         b += tmp
    __pyx_v_b = (__pyx_v_b + __pyx_v_tmp);
  }
+11:     return b
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_3 = __Pyx_PyInt_From_long(__pyx_v_b); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_3);
  __pyx_r = __pyx_t_3;
  __pyx_t_3 = 0;
  goto __pyx_L0;
In [124]:
%timeit fib_cython_seq(20)
The slowest run took 15.42 times longer than the fastest. This could mean that an intermediate result is being cached
10000000 loops, best of 3: 122 ns per loop
In [131]:
report([fib, fib_lru, fib_cython, fib_numba, fib_cython_seq], 20)
fib: 1.0
fib_lru: 1151.4
fib_cython: 26.0
fib_numba: 639.7
fib_cython_seq: 1919.0
In [442]:
fs = fib, fib_lru, fib_cython, fib_numba, fib_cython_seq
for f in fs:
    print(f(20))
6765
6765
6765
6765
6765

Parse_int

In [158]:
def parse_int():
    for i in range(1,1000):
        n = random.randint(0,2**31-1)
        s = hex(n)
#         if s[-1]=='L':
#             s = s[0:-1]
        m = int(s,16)
        assert m == n
In [142]:
%timeit parse_int()
100 loops, best of 3: 4.56 ms per loop
In [159]:
def parse_int_numpy():
    for i in range(1,1000):
        n = np.random.randint(0,2**31-1)
        s = hex(n)
        m = int(s,16)
        assert m == n
In [160]:
%timeit parse_int_numpy()
1000 loops, best of 3: 1.51 ms per loop
In [169]:
%%cython -a

import cython
import numpy as np
cimport numpy as np

def parse_int_cython():
    cdef int i, n, m
    for i in range(1,1000):
        n = np.random.randint(0,2**31-1)
        m = int(hex(n),16)
        assert m == n
Out[169]:
Cython: _cython_magic_2d3f37e15c7a44b97af38354a036490d.pyx

Generated by Cython 0.23.4

Yellow lines hint at Python interaction.
Click on a line that starts with a "+" to see the C code that Cython generated for it.

 01: 
+02: import cython
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+03: import numpy as np
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 04: cimport numpy as np
 05: 
+06: def parse_int_cython():
/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_2d3f37e15c7a44b97af38354a036490d_1parse_int_cython(PyObject *__pyx_self, CYTHON_UNUSED PyObject *unused); /*proto*/
static PyMethodDef __pyx_mdef_46_cython_magic_2d3f37e15c7a44b97af38354a036490d_1parse_int_cython = {"parse_int_cython", (PyCFunction)__pyx_pw_46_cython_magic_2d3f37e15c7a44b97af38354a036490d_1parse_int_cython, METH_NOARGS, 0};
static PyObject *__pyx_pw_46_cython_magic_2d3f37e15c7a44b97af38354a036490d_1parse_int_cython(PyObject *__pyx_self, CYTHON_UNUSED PyObject *unused) {
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("parse_int_cython (wrapper)", 0);
  __pyx_r = __pyx_pf_46_cython_magic_2d3f37e15c7a44b97af38354a036490d_parse_int_cython(__pyx_self);

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_46_cython_magic_2d3f37e15c7a44b97af38354a036490d_parse_int_cython(CYTHON_UNUSED PyObject *__pyx_self) {
  CYTHON_UNUSED int __pyx_v_i;
  int __pyx_v_n;
  int __pyx_v_m;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("parse_int_cython", 0);
/* … */
  /* function exit code */
  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_2);
  __Pyx_XDECREF(__pyx_t_3);
  __Pyx_AddTraceback("_cython_magic_2d3f37e15c7a44b97af38354a036490d.parse_int_cython", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple__8 = PyTuple_Pack(3, __pyx_n_s_i, __pyx_n_s_n, __pyx_n_s_m); if (unlikely(!__pyx_tuple__8)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 6; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple__8);
  __Pyx_GIVEREF(__pyx_tuple__8);
/* … */
  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_2d3f37e15c7a44b97af38354a036490d_1parse_int_cython, NULL, __pyx_n_s_cython_magic_2d3f37e15c7a44b97a); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 6; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_parse_int_cython, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 6; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 07:     cdef int i, n, m
+08:     for i in range(1,1000):
  for (__pyx_t_1 = 1; __pyx_t_1 < 0x3E8; __pyx_t_1+=1) {
    __pyx_v_i = __pyx_t_1;
+09:         n = np.random.randint(0,2**31-1)
    __pyx_t_2 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    __pyx_t_3 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_random); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_3);
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
    __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_3, __pyx_n_s_randint); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
    __pyx_t_3 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_tuple_, NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_3);
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
    __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_t_3); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
    __pyx_v_n = __pyx_t_4;
/* … */
  __pyx_tuple_ = PyTuple_Pack(2, __pyx_int_0, __pyx_int_2147483647); if (unlikely(!__pyx_tuple_)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 9; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple_);
  __Pyx_GIVEREF(__pyx_tuple_);
+10:         m = int(hex(n),16)
    __pyx_t_3 = __Pyx_PyInt_From_int(__pyx_v_n); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_3);
    __pyx_t_2 = PyTuple_New(1); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_GIVEREF(__pyx_t_3);
    PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3);
    __pyx_t_3 = 0;
    __pyx_t_3 = __Pyx_PyObject_Call(__pyx_builtin_hex, __pyx_t_2, NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_3);
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
    __pyx_t_2 = PyTuple_New(2); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_2);
    __Pyx_GIVEREF(__pyx_t_3);
    PyTuple_SET_ITEM(__pyx_t_2, 0, __pyx_t_3);
    __Pyx_INCREF(__pyx_int_16);
    __Pyx_GIVEREF(__pyx_int_16);
    PyTuple_SET_ITEM(__pyx_t_2, 1, __pyx_int_16);
    __pyx_t_3 = 0;
    __pyx_t_3 = __Pyx_PyObject_Call(((PyObject *)(&PyInt_Type)), __pyx_t_2, NULL); if (unlikely(!__pyx_t_3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_GOTREF(__pyx_t_3);
    __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
    __pyx_t_4 = __Pyx_PyInt_As_int(__pyx_t_3); if (unlikely((__pyx_t_4 == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 10; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
    __Pyx_DECREF(__pyx_t_3); __pyx_t_3 = 0;
    __pyx_v_m = __pyx_t_4;
+11:         assert m == n
    #ifndef CYTHON_WITHOUT_ASSERTIONS
    if (unlikely(!Py_OptimizeFlag)) {
      if (unlikely(!((__pyx_v_m == __pyx_v_n) != 0))) {
        PyErr_SetNone(PyExc_AssertionError);
        {__pyx_filename = __pyx_f[0]; __pyx_lineno = 11; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      }
    }
    #endif
  }
In [170]:
%timeit parse_int_cython()
1000 loops, best of 3: 1.13 ms per loop
In [175]:
report([parse_int, parse_int_numpy, parse_int_cython])
parse_int: 1.0
parse_int_numpy: 2.7
parse_int_cython: 3.2

Quicksort

In [171]:
def qsort_kernel(a, lo, hi):
    i = lo
    j = hi
    while i < hi:
        pivot = a[(lo+hi) // 2]
        while i <= j:
            while a[i] < pivot:
                i += 1
            while a[j] > pivot:
                j -= 1
            if i <= j:
                a[i], a[j] = a[j], a[i]
                i += 1
                j -= 1
        if lo < j:
            qsort_kernel(a, lo, j)
        lo = i
        j = hi
    return a
In [190]:
def benchmark_qsort():
    lst = [ random.random() for i in range(1,5000) ]
    qsort_kernel(lst, 0, len(lst)-1)
In [191]:
%timeit benchmark_qsort()
10 loops, best of 3: 23.8 ms per loop
In [214]:
%%cython -a

import cython
import numpy as np

@cython.boundscheck(False)
@cython.wraparound(False)
cdef double[:] qsort_kernel_cython(double[:] a, int lo, int hi):
    cdef int i, j
    cdef double pivot

    i = lo
    j = hi
    while i < hi:
        pivot = a[(lo+hi) // 2]
        while i <= j:
            while a[i] < pivot:
                i += 1
            while a[j] > pivot:
                j -= 1
            if i <= j:
                a[i], a[j] = a[j], a[i]
                i += 1
                j -= 1
        if lo < j:
            qsort_kernel_cython(a, lo, j)
        lo = i
        j = hi
    return a

def benchmark_qsort_cython():
    lst = np.random.random(5000)
    qsort_kernel_cython(lst, 0, len(lst)-1)
Out[214]:
Cython: _cython_magic_2e0923d1649b2de5c2ff7f8deca79258.pyx

Generated by Cython 0.23.4

Yellow lines hint at Python interaction.
Click on a line that starts with a "+" to see the C code that Cython generated for it.

 01: 
+02: import cython
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+03: import numpy as np
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 04: 
 05: @cython.boundscheck(False)
 06: @cython.wraparound(False)
+07: cdef double[:] qsort_kernel_cython(double[:] a, int lo, int hi):
static __Pyx_memviewslice __pyx_f_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_qsort_kernel_cython(__Pyx_memviewslice __pyx_v_a, int __pyx_v_lo, int __pyx_v_hi) {
  int __pyx_v_i;
  int __pyx_v_j;
  double __pyx_v_pivot;
  __Pyx_memviewslice __pyx_r = { 0, 0, { 0 }, { 0 }, { 0 } };
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("qsort_kernel_cython", 0);
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __PYX_XDEC_MEMVIEW(&__pyx_t_11, 1);
  __pyx_r.data = NULL;
  __pyx_r.memview = NULL;
  __Pyx_AddTraceback("_cython_magic_2e0923d1649b2de5c2ff7f8deca79258.qsort_kernel_cython", __pyx_clineno, __pyx_lineno, __pyx_filename);

  goto __pyx_L2;
  __pyx_L0:;
  if (unlikely(!__pyx_r.memview)) {
    PyErr_SetString(PyExc_TypeError,"Memoryview return value is not initialized");
  }
  __pyx_L2:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
 08:     cdef int i, j
 09:     cdef double pivot
 10: 
+11:     i = lo
  __pyx_v_i = __pyx_v_lo;
+12:     j = hi
  __pyx_v_j = __pyx_v_hi;
+13:     while i < hi:
  while (1) {
    __pyx_t_1 = ((__pyx_v_i < __pyx_v_hi) != 0);
    if (!__pyx_t_1) break;
+14:         pivot = a[(lo+hi) // 2]
    __pyx_t_2 = __Pyx_div_long((__pyx_v_lo + __pyx_v_hi), 2);
    __pyx_v_pivot = (*((double *) ( /* dim=0 */ (__pyx_v_a.data + __pyx_t_2 * __pyx_v_a.strides[0]) )));
+15:         while i <= j:
    while (1) {
      __pyx_t_1 = ((__pyx_v_i <= __pyx_v_j) != 0);
      if (!__pyx_t_1) break;
+16:             while a[i] < pivot:
      while (1) {
        __pyx_t_3 = __pyx_v_i;
        __pyx_t_1 = (((*((double *) ( /* dim=0 */ (__pyx_v_a.data + __pyx_t_3 * __pyx_v_a.strides[0]) ))) < __pyx_v_pivot) != 0);
        if (!__pyx_t_1) break;
+17:                 i += 1
        __pyx_v_i = (__pyx_v_i + 1);
      }
+18:             while a[j] > pivot:
      while (1) {
        __pyx_t_4 = __pyx_v_j;
        __pyx_t_1 = (((*((double *) ( /* dim=0 */ (__pyx_v_a.data + __pyx_t_4 * __pyx_v_a.strides[0]) ))) > __pyx_v_pivot) != 0);
        if (!__pyx_t_1) break;
+19:                 j -= 1
        __pyx_v_j = (__pyx_v_j - 1);
      }
+20:             if i <= j:
      __pyx_t_1 = ((__pyx_v_i <= __pyx_v_j) != 0);
      if (__pyx_t_1) {
/* … */
      }
    }
+21:                 a[i], a[j] = a[j], a[i]
        __pyx_t_5 = __pyx_v_j;
        __pyx_t_6 = (*((double *) ( /* dim=0 */ (__pyx_v_a.data + __pyx_t_5 * __pyx_v_a.strides[0]) )));
        __pyx_t_7 = __pyx_v_i;
        __pyx_t_8 = (*((double *) ( /* dim=0 */ (__pyx_v_a.data + __pyx_t_7 * __pyx_v_a.strides[0]) )));
        __pyx_t_9 = __pyx_v_i;
        *((double *) ( /* dim=0 */ (__pyx_v_a.data + __pyx_t_9 * __pyx_v_a.strides[0]) )) = __pyx_t_6;
        __pyx_t_10 = __pyx_v_j;
        *((double *) ( /* dim=0 */ (__pyx_v_a.data + __pyx_t_10 * __pyx_v_a.strides[0]) )) = __pyx_t_8;
+22:                 i += 1
        __pyx_v_i = (__pyx_v_i + 1);
+23:                 j -= 1
        __pyx_v_j = (__pyx_v_j - 1);
+24:         if lo < j:
    __pyx_t_1 = ((__pyx_v_lo < __pyx_v_j) != 0);
    if (__pyx_t_1) {
/* … */
    }
+25:             qsort_kernel_cython(a, lo, j)
      __pyx_t_11 = __pyx_f_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_qsort_kernel_cython(__pyx_v_a, __pyx_v_lo, __pyx_v_j); if (unlikely(!__pyx_t_11.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 25; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
      __PYX_XDEC_MEMVIEW(&__pyx_t_11, 1);
+26:         lo = i
    __pyx_v_lo = __pyx_v_i;
+27:         j = hi
    __pyx_v_j = __pyx_v_hi;
  }
+28:     return a
  __PYX_INC_MEMVIEW(&__pyx_v_a, 0);
  __pyx_r = __pyx_v_a;
  goto __pyx_L0;
 29: 
+30: def benchmark_qsort_cython():
/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_1benchmark_qsort_cython(PyObject *__pyx_self, CYTHON_UNUSED PyObject *unused); /*proto*/
static PyMethodDef __pyx_mdef_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_1benchmark_qsort_cython = {"benchmark_qsort_cython", (PyCFunction)__pyx_pw_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_1benchmark_qsort_cython, METH_NOARGS, 0};
static PyObject *__pyx_pw_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_1benchmark_qsort_cython(PyObject *__pyx_self, CYTHON_UNUSED PyObject *unused) {
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("benchmark_qsort_cython (wrapper)", 0);
  __pyx_r = __pyx_pf_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_benchmark_qsort_cython(__pyx_self);

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_benchmark_qsort_cython(CYTHON_UNUSED PyObject *__pyx_self) {
  PyObject *__pyx_v_lst = NULL;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("benchmark_qsort_cython", 0);
/* … */
  /* function exit code */
  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
  goto __pyx_L0;
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __PYX_XDEC_MEMVIEW(&__pyx_t_3, 1);
  __PYX_XDEC_MEMVIEW(&__pyx_t_5, 1);
  __Pyx_AddTraceback("_cython_magic_2e0923d1649b2de5c2ff7f8deca79258.benchmark_qsort_cython", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XDECREF(__pyx_v_lst);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple__15 = PyTuple_Pack(1, __pyx_n_s_lst); if (unlikely(!__pyx_tuple__15)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple__15);
  __Pyx_GIVEREF(__pyx_tuple__15);
/* … */
  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_1benchmark_qsort_cython, NULL, __pyx_n_s_cython_magic_2e0923d1649b2de5c2); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_benchmark_qsort_cython, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_codeobj__16 = (PyObject*)__Pyx_PyCode_New(0, 0, 1, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__15, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_Users_cliburn_ipython_cython__c, __pyx_n_s_benchmark_qsort_cython, 30, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__16)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 30; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
+31:     lst = np.random.random(5000)
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_random); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_PyObject_GetAttrStr(__pyx_t_2, __pyx_n_s_random); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_2 = __Pyx_PyObject_Call(__pyx_t_1, __pyx_tuple_, NULL); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_v_lst = __pyx_t_2;
  __pyx_t_2 = 0;
/* … */
  __pyx_tuple_ = PyTuple_Pack(1, __pyx_int_5000); if (unlikely(!__pyx_tuple_)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 31; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple_);
  __Pyx_GIVEREF(__pyx_tuple_);
+32:     qsort_kernel_cython(lst, 0, len(lst)-1)
  __pyx_t_3 = __Pyx_PyObject_to_MemoryviewSlice_ds_double(__pyx_v_lst);
  if (unlikely(!__pyx_t_3.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_4 = PyObject_Length(__pyx_v_lst); if (unlikely(__pyx_t_4 == -1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __pyx_t_5 = __pyx_f_46_cython_magic_2e0923d1649b2de5c2ff7f8deca79258_qsort_kernel_cython(__pyx_t_3, 0, (__pyx_t_4 - 1)); if (unlikely(!__pyx_t_5.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 32; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __PYX_XDEC_MEMVIEW(&__pyx_t_3, 1);
  __PYX_XDEC_MEMVIEW(&__pyx_t_5, 1);
In [215]:
%timeit benchmark_qsort_cython()
The slowest run took 4.02 times longer than the fastest. This could mean that an intermediate result is being cached
1000 loops, best of 3: 735 µs per loop
In [216]:
report([benchmark_qsort, benchmark_qsort_cython])
benchmark_qsort: 1.0
benchmark_qsort_cython: 25.6

Mandel

In [217]:
def mandel(z):
    maxiter = 80
    c = z
    for n in range(maxiter):
        if abs(z) > 2:
            return n
        z = z*z + c
    return maxiter

def mandelperf():
    r1 = np.linspace(-2.0, 0.5, 26)
    r2 = np.linspace(-1.0, 1.0, 21)
    return [mandel(complex(r, i)) for r in r1 for i in r2]
In [219]:
%timeit mandelperf()
100 loops, best of 3: 6.9 ms per loop
In [259]:
@jit
def mandel_numba(z):
    maxiter = 80
    c = z
    for n in range(maxiter):
        if abs(z) > 2:
            return n
        z = z*z + c
    return maxiter

@jit
def mandelperf_numba():
    r1 = np.linspace(-2.0, 0.5, 26)
    r2 = np.linspace(-1.0, 1.0, 21)
    res = np.empty((26, 21), dtype='int')
    for i in range(26):
        for j in range(21):
            res[i, j] = mandel_numba(complex(r1[i], r2[j]))
    return res
In [260]:
%timeit mandelperf_numba()
The slowest run took 2568.37 times longer than the fastest. This could mean that an intermediate result is being cached
1 loops, best of 3: 156 µs per loop
In [268]:
%%cython -a

cimport cython
import numpy as np
cimport numpy as np

cdef extern from "complex.h":
    double cabs(double complex)

cdef int mandel_cython(double complex z):
    cdef int n
    cdef int max_iter
    cdef double complex c
    maxiter = 80
    c = z
    for n in range(maxiter):
        if cabs(z) > 2:
            return n
        z = z*z + c
    return maxiter

@cython.boundscheck(False)
@cython.wraparound(False)
def mandelperf_cython():
    cdef int i, j

    cdef double[:] r1 = np.linspace(-2.0, 0.5, 26)
    cdef double[:] r2 = np.linspace(-1.0, 1.0, 21)
    cdef int[:,:] res = np.empty((26,21), dtype=np.int32)

    for i in range(26):
        for j in range(21):
            res[i,j] = mandel_cython(r1[i] + r2[j]*1j)
    return res
Out[268]:
Cython: _cython_magic_49b54927ec0b1cfc50dc062249bda68a.pyx

Generated by Cython 0.23.4

Yellow lines hint at Python interaction.
Click on a line that starts with a "+" to see the C code that Cython generated for it.

 01: 
+02: cimport cython
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 2; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+03: import numpy as np
  __pyx_t_1 = __Pyx_Import(__pyx_n_s_numpy, 0, -1); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_np, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 3; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 04: cimport numpy as np
 05: 
 06: cdef extern from "complex.h":
 07:     double cabs(double complex)
 08: 
+09: cdef int mandel_cython(double complex z):
static int __pyx_f_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_mandel_cython(__pyx_t_double_complex __pyx_v_z) {
  int __pyx_v_n;
  __pyx_t_double_complex __pyx_v_c;
  long __pyx_v_maxiter;
  int __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("mandel_cython", 0);
/* … */
  /* function exit code */
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
 10:     cdef int n
 11:     cdef int max_iter
 12:     cdef double complex c
+13:     maxiter = 80
  __pyx_v_maxiter = 80;
+14:     c = z
  __pyx_v_c = __pyx_v_z;
+15:     for n in range(maxiter):
  __pyx_t_1 = __pyx_v_maxiter;
  for (__pyx_t_2 = 0; __pyx_t_2 < __pyx_t_1; __pyx_t_2+=1) {
    __pyx_v_n = __pyx_t_2;
+16:         if cabs(z) > 2:
    __pyx_t_3 = ((cabs(__pyx_v_z) > 2.0) != 0);
    if (__pyx_t_3) {
/* … */
    }
+17:             return n
      __pyx_r = __pyx_v_n;
      goto __pyx_L0;
+18:         z = z*z + c
    __pyx_v_z = __Pyx_c_sum(__Pyx_c_prod(__pyx_v_z, __pyx_v_z), __pyx_v_c);
  }
+19:     return maxiter
  __pyx_r = __pyx_v_maxiter;
  goto __pyx_L0;
 20: 
 21: @cython.boundscheck(False)
 22: @cython.wraparound(False)
+23: def mandelperf_cython():
/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_1mandelperf_cython(PyObject *__pyx_self, CYTHON_UNUSED PyObject *unused); /*proto*/
static PyMethodDef __pyx_mdef_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_1mandelperf_cython = {"mandelperf_cython", (PyCFunction)__pyx_pw_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_1mandelperf_cython, METH_NOARGS, 0};
static PyObject *__pyx_pw_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_1mandelperf_cython(PyObject *__pyx_self, CYTHON_UNUSED PyObject *unused) {
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("mandelperf_cython (wrapper)", 0);
  __pyx_r = __pyx_pf_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_mandelperf_cython(__pyx_self);

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_mandelperf_cython(CYTHON_UNUSED PyObject *__pyx_self) {
  int __pyx_v_i;
  int __pyx_v_j;
  __Pyx_memviewslice __pyx_v_r1 = { 0, 0, { 0 }, { 0 }, { 0 } };
  __Pyx_memviewslice __pyx_v_r2 = { 0, 0, { 0 }, { 0 }, { 0 } };
  __Pyx_memviewslice __pyx_v_res = { 0, 0, { 0 }, { 0 }, { 0 } };
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("mandelperf_cython", 0);
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_1);
  __Pyx_XDECREF(__pyx_t_2);
  __PYX_XDEC_MEMVIEW(&__pyx_t_3, 1);
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_XDECREF(__pyx_t_5);
  __PYX_XDEC_MEMVIEW(&__pyx_t_6, 1);
  __Pyx_AddTraceback("_cython_magic_49b54927ec0b1cfc50dc062249bda68a.mandelperf_cython", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __PYX_XDEC_MEMVIEW(&__pyx_v_r1, 1);
  __PYX_XDEC_MEMVIEW(&__pyx_v_r2, 1);
  __PYX_XDEC_MEMVIEW(&__pyx_v_res, 1);
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple__24 = PyTuple_Pack(5, __pyx_n_s_i, __pyx_n_s_j, __pyx_n_s_r1, __pyx_n_s_r2, __pyx_n_s_res); if (unlikely(!__pyx_tuple__24)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple__24);
  __Pyx_GIVEREF(__pyx_tuple__24);
/* … */
  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_1mandelperf_cython, NULL, __pyx_n_s_cython_magic_49b54927ec0b1cfc50); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_mandelperf_cython, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_codeobj__25 = (PyObject*)__Pyx_PyCode_New(0, 0, 5, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__24, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_Users_cliburn_ipython_cython__c, __pyx_n_s_mandelperf_cython, 23, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__25)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 23; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
 24:     cdef int i, j
 25: 
+26:     cdef double[:] r1 = np.linspace(-2.0, 0.5, 26)
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_linspace); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_tuple_, NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_3 = __Pyx_PyObject_to_MemoryviewSlice_ds_double(__pyx_t_1);
  if (unlikely(!__pyx_t_3.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_v_r1 = __pyx_t_3;
  __pyx_t_3.memview = NULL;
  __pyx_t_3.data = NULL;
/* … */
  __pyx_tuple_ = PyTuple_Pack(3, __pyx_float_neg_2_0, __pyx_float_0_5, __pyx_int_26); if (unlikely(!__pyx_tuple_)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 26; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple_);
  __Pyx_GIVEREF(__pyx_tuple_);
+27:     cdef double[:] r2 = np.linspace(-1.0, 1.0, 21)
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_linspace); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_1 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_tuple__2, NULL); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __pyx_t_3 = __Pyx_PyObject_to_MemoryviewSlice_ds_double(__pyx_t_1);
  if (unlikely(!__pyx_t_3.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_v_r2 = __pyx_t_3;
  __pyx_t_3.memview = NULL;
  __pyx_t_3.data = NULL;
/* … */
  __pyx_tuple__2 = PyTuple_Pack(3, __pyx_float_neg_1_0, __pyx_float_1_0, __pyx_int_21); if (unlikely(!__pyx_tuple__2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 27; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple__2);
  __Pyx_GIVEREF(__pyx_tuple__2);
+28:     cdef int[:,:] res = np.empty((26,21), dtype=np.int32)
  __pyx_t_1 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_2 = __Pyx_PyObject_GetAttrStr(__pyx_t_1, __pyx_n_s_empty); if (unlikely(!__pyx_t_2)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_2);
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
/* … */
  __pyx_tuple__3 = PyTuple_Pack(2, __pyx_int_26, __pyx_int_21); if (unlikely(!__pyx_tuple__3)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple__3);
  __Pyx_GIVEREF(__pyx_tuple__3);
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  __pyx_t_4 = __Pyx_GetModuleGlobalName(__pyx_n_s_np); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_t_5 = __Pyx_PyObject_GetAttrStr(__pyx_t_4, __pyx_n_s_int32); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_4); __pyx_t_4 = 0;
  if (PyDict_SetItem(__pyx_t_1, __pyx_n_s_dtype, __pyx_t_5) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __pyx_t_5 = __Pyx_PyObject_Call(__pyx_t_2, __pyx_tuple__4, __pyx_t_1); if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __Pyx_DECREF(__pyx_t_2); __pyx_t_2 = 0;
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
  __pyx_t_6 = __Pyx_PyObject_to_MemoryviewSlice_dsds_int(__pyx_t_5);
  if (unlikely(!__pyx_t_6.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_5); __pyx_t_5 = 0;
  __pyx_v_res = __pyx_t_6;
  __pyx_t_6.memview = NULL;
  __pyx_t_6.data = NULL;
  __pyx_tuple__4 = PyTuple_Pack(1, __pyx_tuple__3); if (unlikely(!__pyx_tuple__4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 28; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple__4);
  __Pyx_GIVEREF(__pyx_tuple__4);
 29: 
+30:     for i in range(26):
  for (__pyx_t_7 = 0; __pyx_t_7 < 26; __pyx_t_7+=1) {
    __pyx_v_i = __pyx_t_7;
+31:         for j in range(21):
    for (__pyx_t_8 = 0; __pyx_t_8 < 21; __pyx_t_8+=1) {
      __pyx_v_j = __pyx_t_8;
+32:             res[i,j] = mandel_cython(r1[i] + r2[j]*1j)
      __pyx_t_9 = __pyx_v_i;
      __pyx_t_10 = __pyx_v_j;
      __pyx_t_11 = __pyx_v_i;
      __pyx_t_12 = __pyx_v_j;
      *((int *) ( /* dim=1 */ (( /* dim=0 */ (__pyx_v_res.data + __pyx_t_11 * __pyx_v_res.strides[0]) ) + __pyx_t_12 * __pyx_v_res.strides[1]) )) = __pyx_f_46_cython_magic_49b54927ec0b1cfc50dc062249bda68a_mandel_cython(__Pyx_c_sum(__pyx_t_double_complex_from_parts((*((double *) ( /* dim=0 */ (__pyx_v_r1.data + __pyx_t_9 * __pyx_v_r1.strides[0]) ))), 0), __Pyx_c_prod(__pyx_t_double_complex_from_parts((*((double *) ( /* dim=0 */ (__pyx_v_r2.data + __pyx_t_10 * __pyx_v_r2.strides[0]) ))), 0), __pyx_t_double_complex_from_parts(0, 1.0))));
    }
  }
+33:     return res
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_5 = __pyx_memoryview_fromslice(__pyx_v_res, 2, (PyObject *(*)(char *)) __pyx_memview_get_int, (int (*)(char *, PyObject *)) __pyx_memview_set_int, 0);; if (unlikely(!__pyx_t_5)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 33; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_5);
  __pyx_r = __pyx_t_5;
  __pyx_t_5 = 0;
  goto __pyx_L0;
In [269]:
%timeit mandelperf_cython()
1000 loops, best of 3: 222 µs per loop
In [271]:
report([mandelperf, mandelperf_numba, mandelperf_cython])
mandelperf: 1.0
mandelperf_numba: 19.7
mandelperf_cython: 15.4
In [454]:
for f in mandelperf, mandelperf_numba, mandelperf_cython:
    print(np.reshape(f(), (26,21)), "\n")
[[ 0  0  0  0  0  0  0  0  0  0 80  0  0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  1  1  2  2  3  3 80  3  3  2  2  1  1  0  0  0  0]
 [ 0  0  1  1  2  2  2  3  3  3 80  3  3  3  2  2  2  1  1  0  0]
 [ 1  1  2  2  2  2  2  3  3  5 80  5  3  3  2  2  2  2  2  1  1]
 [ 1  2  2  2  2  2  2  3  4  5 80  5  4  3  2  2  2  2  2  2  1]
 [ 1  2  2  2  2  2  3  4  4  6 80  6  4  4  3  2  2  2  2  2  1]
 [ 2  2  2  2  2  2  4  4  5 11 80 11  5  4  4  2  2  2  2  2  2]
 [ 2  2  2  2  2  3  6  6  7 13 80 13  7  6  6  3  2  2  2  2  2]
 [ 2  2  2  2  2  4  6 15 17 80 80 80 17 15  6  4  2  2  2  2  2]
 [ 2  2  2  2  3  4  6 11 80 80 80 80 80 11  6  4  3  2  2  2  2]
 [ 2  2  2  3  3  4  6 34 80 80 80 80 80 34  6  4  3  3  2  2  2]
 [ 2  2  3  3  4  4  6 10 80 80 80 80 80 10  6  4  4  3  3  2  2]
 [ 2  3  3  3  4  5  6  8 14 80 80 80 14  8  6  5  4  3  3  3  2]
 [ 2  3  3  4  5  7 10 23 80 80 80 80 80 23 10  7  5  4  3  3  2]
 [ 3  3  3  6 11 11 80 80 80 80 80 80 80 80 80 11 11  6  3  3  3]
 [ 3  3  4  7 80 80 80 80 80 80 80 80 80 80 80 80 80  7  4  3  3]
 [ 3  4  5  7 25 80 80 80 80 80 80 80 80 80 80 80 25  7  5  4  3]
 [ 4  5  7  9 80 80 80 80 80 80 80 80 80 80 80 80 80  9  7  5  4]
 [ 6  8 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80  8  6]
 [ 8 23 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 23  8]
 [80  7 17 12 80 80 80 80 80 80 80 80 80 80 80 80 80 12 17  7 80]
 [ 3  4  5  8 80 80 80 80 80 80 80 80 80 80 80 80 80  8  5  4  3]
 [ 3  3  4  5 11 80 80 80 80 80 80 80 80 80 80 80 11  5  4  3  3]
 [ 2  3  4  5 14 80 80 80 80 80 11 80 80 80 80 80 14  5  4  3  2]
 [ 2  2  3  4 14  6  8 14 30  7  6  7 30 14  8  6 14  4  3  2  2]
 [ 1  2  2  3  3  4  4  5  4  4  4  4  4  5  4  4  3  3  2  2  1]]

[[ 0  0  0  0  0  0  0  0  0  0 80  0  0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  1  1  2  2  3  3 80  3  3  2  2  1  1  0  0  0  0]
 [ 0  0  1  1  2  2  2  3  3  3 80  3  3  3  2  2  2  1  1  0  0]
 [ 1  1  2  2  2  2  2  3  3  5 80  5  3  3  2  2  2  2  2  1  1]
 [ 1  2  2  2  2  2  2  3  4  5 80  5  4  3  2  2  2  2  2  2  1]
 [ 1  2  2  2  2  2  3  4  4  6 80  6  4  4  3  2  2  2  2  2  1]
 [ 2  2  2  2  2  2  4  4  5 11 80 11  5  4  4  2  2  2  2  2  2]
 [ 2  2  2  2  2  3  6  6  7 13 80 13  7  6  6  3  2  2  2  2  2]
 [ 2  2  2  2  2  4  6 15 17 80 80 80 17 15  6  4  2  2  2  2  2]
 [ 2  2  2  2  3  4  6 11 80 80 80 80 80 11  6  4  3  2  2  2  2]
 [ 2  2  2  3  3  4  6 34 80 80 80 80 80 34  6  4  3  3  2  2  2]
 [ 2  2  3  3  4  4  6 10 80 80 80 80 80 10  6  4  4  3  3  2  2]
 [ 2  3  3  3  4  5  6  8 14 80 80 80 14  8  6  5  4  3  3  3  2]
 [ 2  3  3  4  5  7 10 23 80 80 80 80 80 23 10  7  5  4  3  3  2]
 [ 3  3  3  6 11 11 80 80 80 80 80 80 80 80 80 11 11  6  3  3  3]
 [ 3  3  4  7 80 80 80 80 80 80 80 80 80 80 80 80 80  7  4  3  3]
 [ 3  4  5  7 25 80 80 80 80 80 80 80 80 80 80 80 25  7  5  4  3]
 [ 4  5  7  9 80 80 80 80 80 80 80 80 80 80 80 80 80  9  7  5  4]
 [ 6  8 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80  8  6]
 [ 8 23 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 23  8]
 [80  7 17 12 80 80 80 80 80 80 80 80 80 80 80 80 80 12 17  7 80]
 [ 3  4  5  8 80 80 80 80 80 80 80 80 80 80 80 80 80  8  5  4  3]
 [ 3  3  4  5 11 80 80 80 80 80 80 80 80 80 80 80 11  5  4  3  3]
 [ 2  3  4  5 14 80 80 80 80 80 11 80 80 80 80 80 14  5  4  3  2]
 [ 2  2  3  4 14  6  8 14 30  7  6  7 30 14  8  6 14  4  3  2  2]
 [ 1  2  2  3  3  4  4  5  4  4  4  4  4  5  4  4  3  3  2  2  1]]

[[ 0  0  0  0  0  0  0  0  0  0 80  0  0  0  0  0  0  0  0  0  0]
 [ 0  0  0  0  1  1  2  2  3  3 80  3  3  2  2  1  1  0  0  0  0]
 [ 0  0  1  1  2  2  2  3  3  3 80  3  3  3  2  2  2  1  1  0  0]
 [ 1  1  2  2  2  2  2  3  3  5 80  5  3  3  2  2  2  2  2  1  1]
 [ 1  2  2  2  2  2  2  3  4  5 80  5  4  3  2  2  2  2  2  2  1]
 [ 1  2  2  2  2  2  3  4  4  6 80  6  4  4  3  2  2  2  2  2  1]
 [ 2  2  2  2  2  2  4  4  5 11 80 11  5  4  4  2  2  2  2  2  2]
 [ 2  2  2  2  2  3  6  6  7 13 80 13  7  6  6  3  2  2  2  2  2]
 [ 2  2  2  2  2  4  6 15 17 80 80 80 17 15  6  4  2  2  2  2  2]
 [ 2  2  2  2  3  4  6 11 80 80 80 80 80 11  6  4  3  2  2  2  2]
 [ 2  2  2  3  3  4  6 34 80 80 80 80 80 34  6  4  3  3  2  2  2]
 [ 2  2  3  3  4  4  6 10 80 80 80 80 80 10  6  4  4  3  3  2  2]
 [ 2  3  3  3  4  5  6  8 14 80 80 80 14  8  6  5  4  3  3  3  2]
 [ 2  3  3  4  5  7 10 23 80 80 80 80 80 23 10  7  5  4  3  3  2]
 [ 3  3  3  6 11 11 80 80 80 80 80 80 80 80 80 11 11  6  3  3  3]
 [ 3  3  4  7 80 80 80 80 80 80 80 80 80 80 80 80 80  7  4  3  3]
 [ 3  4  5  7 25 80 80 80 80 80 80 80 80 80 80 80 25  7  5  4  3]
 [ 4  5  7  9 80 80 80 80 80 80 80 80 80 80 80 80 80  9  7  5  4]
 [ 6  8 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80  8  6]
 [ 8 23 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 23  8]
 [80  7 17 12 80 80 80 80 80 80 80 80 80 80 80 80 80 12 17  7 80]
 [ 3  4  5  8 80 80 80 80 80 80 80 80 80 80 80 80 80  8  5  4  3]
 [ 3  3  4  5 11 80 80 80 80 80 80 80 80 80 80 80 11  5  4  3  3]
 [ 2  3  4  5 14 80 80 80 80 80 11 80 80 80 80 80 14  5  4  3  2]
 [ 2  2  3  4 14  6  8 14 30  7  6  7 30 14  8  6 14  4  3  2  2]
 [ 1  2  2  3  3  4  4  5  4  4  4  4  4  5  4  4  3  3  2  2  1]]

Pi_sum

In [272]:
def pisum(t):
    sum = 0.0
    for j in range(1, 501):
        sum = 0.0
        for k in range(1, t+1):
            sum += 1.0/(k*k)
    return sum
In [273]:
%timeit pisum(10000)
1 loops, best of 3: 1.33 s per loop
In [282]:
@jit
def pisum_numba(t):
    sum = 0.0
    for j in range(1, 501):
        sum = 0.0
        for k in range(1, t+1):
            sum += 1.0/(k*k)
    return sum
In [283]:
%timeit pisum_numba(10000)
10 loops, best of 3: 35.8 ms per loop
In [298]:
%%cython -a

cimport cython

@cython.cdivision(True)
def pisum_cython(int t):
    cdef double sum
    cdef int j, k

    for j in range(1, 501):
        sum = 0.0
        for k in range(1, t+1):
            sum += 1.0/(k*k)
    return sum
Out[298]:
Cython: _cython_magic_39bd6be55fc470028b8a8a158c919aa6.pyx

Generated by Cython 0.23.4

Yellow lines hint at Python interaction.
Click on a line that starts with a "+" to see the C code that Cython generated for it.

 01: 
 02: cimport cython
 03: 
 04: @cython.cdivision(True)
+05: def pisum_cython(int t):
/* Python wrapper */
static PyObject *__pyx_pw_46_cython_magic_39bd6be55fc470028b8a8a158c919aa6_1pisum_cython(PyObject *__pyx_self, PyObject *__pyx_arg_t); /*proto*/
static PyMethodDef __pyx_mdef_46_cython_magic_39bd6be55fc470028b8a8a158c919aa6_1pisum_cython = {"pisum_cython", (PyCFunction)__pyx_pw_46_cython_magic_39bd6be55fc470028b8a8a158c919aa6_1pisum_cython, METH_O, 0};
static PyObject *__pyx_pw_46_cython_magic_39bd6be55fc470028b8a8a158c919aa6_1pisum_cython(PyObject *__pyx_self, PyObject *__pyx_arg_t) {
  int __pyx_v_t;
  PyObject *__pyx_r = 0;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("pisum_cython (wrapper)", 0);
  assert(__pyx_arg_t); {
    __pyx_v_t = __Pyx_PyInt_As_int(__pyx_arg_t); if (unlikely((__pyx_v_t == (int)-1) && PyErr_Occurred())) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 5; __pyx_clineno = __LINE__; goto __pyx_L3_error;}
  }
  goto __pyx_L4_argument_unpacking_done;
  __pyx_L3_error:;
  __Pyx_AddTraceback("_cython_magic_39bd6be55fc470028b8a8a158c919aa6.pisum_cython", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __Pyx_RefNannyFinishContext();
  return NULL;
  __pyx_L4_argument_unpacking_done:;
  __pyx_r = __pyx_pf_46_cython_magic_39bd6be55fc470028b8a8a158c919aa6_pisum_cython(__pyx_self, ((int)__pyx_v_t));
  int __pyx_lineno = 0;
  const char *__pyx_filename = NULL;
  int __pyx_clineno = 0;

  /* function exit code */
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}

static PyObject *__pyx_pf_46_cython_magic_39bd6be55fc470028b8a8a158c919aa6_pisum_cython(CYTHON_UNUSED PyObject *__pyx_self, int __pyx_v_t) {
  double __pyx_v_sum;
  CYTHON_UNUSED int __pyx_v_j;
  int __pyx_v_k;
  PyObject *__pyx_r = NULL;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("pisum_cython", 0);
/* … */
  /* function exit code */
  __pyx_L1_error:;
  __Pyx_XDECREF(__pyx_t_4);
  __Pyx_AddTraceback("_cython_magic_39bd6be55fc470028b8a8a158c919aa6.pisum_cython", __pyx_clineno, __pyx_lineno, __pyx_filename);
  __pyx_r = NULL;
  __pyx_L0:;
  __Pyx_XGIVEREF(__pyx_r);
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
/* … */
  __pyx_tuple_ = PyTuple_Pack(5, __pyx_n_s_t, __pyx_n_s_t, __pyx_n_s_sum, __pyx_n_s_j, __pyx_n_s_k); if (unlikely(!__pyx_tuple_)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 5; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_tuple_);
  __Pyx_GIVEREF(__pyx_tuple_);
/* … */
  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_39bd6be55fc470028b8a8a158c919aa6_1pisum_cython, NULL, __pyx_n_s_cython_magic_39bd6be55fc470028b); if (unlikely(!__pyx_t_1)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 5; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_1);
  if (PyDict_SetItem(__pyx_d, __pyx_n_s_pisum_cython, __pyx_t_1) < 0) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 5; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
 06:     cdef double sum
 07:     cdef int j, k
 08: 
+09:     for j in range(1, 501):
  for (__pyx_t_1 = 1; __pyx_t_1 < 0x1F5; __pyx_t_1+=1) {
    __pyx_v_j = __pyx_t_1;
+10:         sum = 0.0
    __pyx_v_sum = 0.0;
+11:         for k in range(1, t+1):
    __pyx_t_2 = (__pyx_v_t + 1);
    for (__pyx_t_3 = 1; __pyx_t_3 < __pyx_t_2; __pyx_t_3+=1) {
      __pyx_v_k = __pyx_t_3;
+12:             sum += 1.0/(k*k)
      __pyx_v_sum = (__pyx_v_sum + (1.0 / (__pyx_v_k * __pyx_v_k)));
    }
  }
+13:     return sum
  __Pyx_XDECREF(__pyx_r);
  __pyx_t_4 = PyFloat_FromDouble(__pyx_v_sum); if (unlikely(!__pyx_t_4)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 13; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
  __Pyx_GOTREF(__pyx_t_4);
  __pyx_r = __pyx_t_4;
  __pyx_t_4 = 0;
  goto __pyx_L0;
In [299]:
%timeit pisum_cython(10000)
10 loops, best of 3: 35.8 ms per loop
In [286]:
report([pisum, pisum_numba, pisum_cython], 10000)
pisum: 1.0
pisum_numba: 35.6
pisum_cython: 35.6
In [455]:
for f in pisum, pisum_numba, pisum_cython:
    print(f(10000))
1.6448340718480652
1.6448340718480652
1.6448340718480652

Rand_mat_stat

In [287]:
def randmatstat(t):
    n = 5
    v = np.zeros(t)
    w = np.zeros(t)
    for i in range(1,t):
        a = np.random.randn(n, n)
        b = np.random.randn(n, n)
        c = np.random.randn(n, n)
        d = np.random.randn(n, n)
        P = np.matrix(np.hstack((a, b, c, d)))
        Q = np.matrix(np.vstack((np.hstack((a, b)), np.hstack((c, d)))))
        v[i] = np.trace(np.linalg.matrix_power(np.transpose(P)*P, 4))
        w[i] = np.trace(np.linalg.matrix_power(np.transpose(Q)*Q, 4))
    return (np.std(v)/np.mean(v), np.std(w)/np.mean(w))
In [288]:
%timeit randmatstat(1000)
1 loops, best of 3: 260 ms per loop
In [391]:
def randmatstat_numba(t):
    n = 5
    v = np.zeros(t)
    w = np.zeros(t)
    for i in range(1,t):
        a, b, c, d = np.random.randn(4, n, n)
        P = np.hstack((a, b, c, d))
        Q = np.vstack((np.hstack((a, b)), np.hstack((c, d))))

        PTP = P.T.dot(P)
        QTQ = Q.T.dot(Q)

        v[i] = np.trace(PTP @ PTP @ PTP @ PTP)
        w[i] = np.trace(QTQ @ QTQ @ QTQ @ QTQ)
    return (np.std(v)/np.mean(v), np.std(w)/np.mean(w))
In [393]:
def randmatstat_alt(t):
    n = 5
    v = np.zeros(t)
    w = np.zeros(t)

    for i in range(1,t):
        a, b, c, d = np.random.randn(4, n, n)
        P = np.hstack((a, b, c, d))
        Q = np.vstack((np.hstack((a, b)), np.hstack((c, d))))

        P1 = P.T @ P
        P2 = P1 @ P1
        P4 = P2 @ P2

        Q1 = Q.T @ Q
        Q2 = Q1 @ Q1
        Q4 = Q2 @ Q2

        v[i] = np.trace(P4)
        w[i] = np.trace(Q4)

    return (np.std(v)/np.mean(v), np.std(w)/np.mean(w))
In [394]:
%timeit randmatstat_alt(1000)
10 loops, best of 3: 131 ms per loop
In [396]:
report([randmatstat, randmatstat_alt], 1000)
randmatstat: 1.0
randmatstat_alt: 2.1
In [458]:
for f in randmatstat, randmatstat_alt:
    np.random.seed(123)
    print(f(1000))
(0.72398346943567571, 0.7713214961017032)
(0.72398346943567571, 0.7713214961017032)

Rand_mat_mul

In [289]:
def randmatmul(n):
    A = np.random.rand(n,n)
    B = np.random.rand(n,n)
    return np.dot(A,B)
In [290]:
%timeit randmatmul(1000)
10 loops, best of 3: 81 ms per loop

Comparison

These benchmarks don’t mean very much - they were constructed by the Julia team presumably to show off Julia’s strengths, and are used here just to illustrate basic Python optimization techniques. In some case (quicksort, mandel, pi_sum), this gives dramatic performance improvements but in other cases (parse_int, rand_mat_stat) there is less improvement. For fib, the speed depends on the algorithm used for calculation, and whether caching is enabled in the recursive case. The final test rand_mat_mul basically depends on the linear algebra library installed (blas, mkl, atlas) and is not really a comparison across languages.

Julia certainly does look promising, and we might include mix in some Julia code in future courses.

In [459]:
benchmarks['P2J'] = benchmarks.Python / benchmarks.Julia
benchmarks['SU'] = [1919.0, 3.2, 25.6, 19.7, 35.6, 2.1, 1.0]
pd.options.display.float_format = '{:.2f}'.format
benchmarks.ix[:, [0,2,3,-2,-1]]
Out[459]:
Julia Python P2J SU
1 fib 2.11 77.76 36.85 1919.00
2 parse_int 1.45 17.02 11.74 3.20
3 quicksort 1.15 32.89 28.60 25.60
4 mandel 0.79 15.32 19.39 19.70
5 pi_sum 1.00 21.99 21.99 35.60
6 rand_mat_stat 1.66 17.93 10.80 2.10
7 rand_mat_mul 1.02 1.14 1.12 1.00
In [ ]: