Numba и Cython не улучшают производительность по сравнению с CPython, возможно, я использую его неправильно?
БОЛЬШОЙ РЕДАКТИРОВАНИЕ:
================
Для ясности я удаляю старые результаты и заменяю их на более свежие результаты. Вопрос все тот же: правильно ли я использую Cython и Numba, и какие улучшения в коде могут быть сделаны? (У меня есть новый и более голой временный IPython-ноутбук со всем кодом и результатами здесь)
1)
Думаю, я понял, почему из Cython, Numba и CPython изначально не было никакой разницы: это было потому, что я накормил их
массивы numpy в качестве входных данных:
x = np.asarray([x_i*np.random.randint(8,12)/10 for x_i in range(n)])
вместо списков:
x = [x_i*random.randint(8,12)/10 for x_i in range(n)]
Тест с использованием массивов Numpy в качестве ввода данных
![enter image description here]()
Тест с использованием списков Python в качестве входных данных
![enter image description here]()
2)
Я заменил функцию zip()
на явные петли, однако это не сильно изменило ситуацию. Код будет выглядеть следующим образом:
CPython
def py_lstsqr(x, y):
""" Computes the least-squares solution to a linear matrix equation. """
len_x = len(x)
x_avg = sum(x)/len_x
y_avg = sum(y)/len(y)
var_x = 0
cov_xy = 0
for i in range(len_x):
temp = (x[i] - x_avg)
var_x += temp**2
cov_xy += temp*(y[i] - y_avg)
slope = cov_xy / var_x
y_interc = y_avg - slope*x_avg
return (slope, y_interc)
Cython
%load_ext cythonmagic
%%cython
def cy_lstsqr(x, y):
""" Computes the least-squares solution to a linear matrix equation. """
cdef double x_avg, y_avg, var_x, cov_xy,\
slope, y_interc, x_i, y_i
cdef int len_x
len_x = len(x)
x_avg = sum(x)/len_x
y_avg = sum(y)/len(y)
var_x = 0
cov_xy = 0
for i in range(len_x):
temp = (x[i] - x_avg)
var_x += temp**2
cov_xy += temp*(y[i] - y_avg)
slope = cov_xy / var_x
y_interc = y_avg - slope*x_avg
return (slope, y_interc)
Numba
from numba import jit
@jit
def numba_lstsqr(x, y):
""" Computes the least-squares solution to a linear matrix equation. """
len_x = len(x)
x_avg = sum(x)/len_x
y_avg = sum(y)/len(y)
var_x = 0
cov_xy = 0
for i in range(len_x):
temp = (x[i] - x_avg)
var_x += temp**2
cov_xy += temp*(y[i] - y_avg)
slope = cov_xy / var_x
y_interc = y_avg - slope*x_avg
return (slope, y_interc)
Ответы
Ответ 1
Здесь я думаю, что это происходит с Numba:
Numba работает с массивами Numpy
. Ничего больше. Все остальное не имеет ничего общего с Numba
.
zip
возвращает итератор произвольных элементов, которые Numba не видит. Таким образом, Numba не может много компилировать.
Зацикливание по индексам с помощью for i in range(...)
, вероятно, приведет к значительно лучшему результату и позволит значительно усилить вывод типа.
Ответ 2
Использование встроенной суммы() может вызвать проблемы.
Здесь приведен код линейной регрессии, который будет работать быстрее в Numba:
@numba.jit
def ols(x, y):
"""Simple OLS for two data sets."""
M = x.size
x_sum = 0.
y_sum = 0.
x_sq_sum = 0.
x_y_sum = 0.
for i in range(M):
x_sum += x[i]
y_sum += y[i]
x_sq_sum += x[i] ** 2
x_y_sum += x[i] * y[i]
slope = (M * x_y_sum - x_sum * y_sum) / (M * x_sq_sum - x_sum**2)
intercept = (y_sum - slope * x_sum) / M
return slope, intercept