y_true
и y_pred
массивы, и, таким образом, объекты Python. Поэтому для любой операции, использующей их, потребуется gil
, а не только форма.
Попробуйте компилировать без nogil
и посмотрите на -a
html. Какие линии темно-желтые, со многими ссылками на объекты Python?
+11: return -(err * err).sum()/y_true.shape[0]
__pyx_t_7 = PyNumber_Multiply(((PyObject *)__pyx_v_err), ((PyObject *)__pyx_v_err)); if (unlikely(!__pyx_t_7)) __PYX_ERR(0, 11, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_7);
__pyx_t_8 = __Pyx_PyObject_GetAttrStr(__pyx_t_7, __pyx_n_s_sum); if (unlikely(!__pyx_t_8)) __PYX_ERR(0, 11, __pyx_L1_error)
__Pyx_GOTREF(__pyx_t_8);
__Pyx_DECREF(__pyx_t_7); __pyx_t_7 = 0;
....
Только часть расширенного кода C для вашего файла. См. Все звонки Pyx..
. Все они требуют gil
.
http://docs.cython.org/en/latest/src/userguide/memoryviews.html показывает, что вы можете использовать nogil
по памяти для массива numpy.
Рисунок из руководства memoryview я написал эту альтернативную функцию
cpdef double neg_mse_view(double[:] y_true, double[:] y_pred):
cdef double x, res
cdef int I
I = y_true.shape[0]
res = 0
for i in range(I):
x = y_true[i]-y_pred[i]
res += -(x*x)
res = res/I
return res
Это можно назвать таким же образом. Эти тайминги показывают 2x ускорение. nogil
работает, но не имеет значения.
In [10]: a=np.arange(1000000.)
In [11]: timeit negmse.negative_mse(a,a-10)
10 loops, best of 3: 16.9 ms per loop
In [12]: timeit negmse.neg_mse_view(a,a-10)
100 loops, best of 3: 7.17 ms per loop
In [13]: timeit negmse.neg_mse_nogil(a,a-10)
100 loops, best of 3: 7.19 ms per loop
Для функции это простой, чистый NumPy версия, в основном, как хорошо:
In [20]: timeit ((a-(a-10))**2).sum()/a.shape[0]
100 loops, best of 3: 16.8 ms per loop
ти ти! haha Я бы подумал, что numpy будет так же быстро. Я хотел использовать функцию nogil (небольшую часть большего процесса) внутри цикла. Мне просто нужен простой пример для начала. Это очень полезно. – itzjustricky