Alcançando o desempenho do Numba com o Cython
Normalmente, sou capaz de igualar o desempenho do Numba ao usar o Cython. No entanto, neste exemplo, falhei em fazê-lo - o Numba é cerca de 4 vezes mais rápido que a versão do meu Cython.
Aqui a versão do Cython:
%%cython -c=-march=native -c=-O3
cimport numpy as np
import numpy as np
cimport cython
@cython.boundscheck(False)
@cython.wraparound(False)
def cy_where(double[::1] df):
cdef int i
cdef int n = len(df)
cdef np.ndarray[dtype=double] output = np.empty(n, dtype=np.float64)
for i in range(n):
if df[i]>0.5:
output[i] = 2.0*df[i]
else:
output[i] = df[i]
return output
E aqui está a versão Numba:
import numba as nb
@nb.njit
def nb_where(df):
n = len(df)
output = np.empty(n, dtype=np.float64)
for i in range(n):
if df[i]>0.5:
output[i] = 2.0*df[i]
else:
output[i] = df[i]
return output
Quando testada, a versão do Cython é parecida com a do numpywhere
, mas é claramente inferior ao Numba:
#Python3.6 + Cython 0.28.3 + gcc-7.2
import numpy
np.random.seed(0)
n = 10000000
data = np.random.random(n)
assert (cy_where(data)==nb_where(data)).all()
assert (np.where(data>0.5,2*data, data)==nb_where(data)).all()
%timeit cy_where(data) # 179ms
%timeit nb_where(data) # 49ms (!!)
%timeit np.where(data>0.5,2*data, data) # 278 ms
Qual é a razão do desempenho do Numba e como ele pode ser correspondido ao usar o Cython?
Conforme sugerido por @ max9111, eliminando a passada usando a visualização de memória contínua, o que não melhora muito o desempenho:
@cython.boundscheck(False)
@cython.wraparound(False)
def cy_where_cont(double[::1] df):
cdef int i
cdef int n = len(df)
cdef np.ndarray[dtype=double] output = np.empty(n, dtype=np.float64)
cdef double[::1] view = output # view as continuous!
for i in range(n):
if df[i]>0.5:
view[i] = 2.0*df[i]
else:
view[i] = df[i]
return output
%timeit cy_where_cont(data) # 165 ms