当我需要 numpy 数组的大小时,Cython 放弃 gil
Cython letting go of gil when I need size of numpy array
当我尝试编译以下代码时,出现错误
return sign_match.sum() / y_true.shape[0]
^
Converting to Python object not allowed without gil
有没有简单的方法可以克服这个问题?我能想到的最可行的解决方案是将数组的长度作为另一个参数传递。我正在使用 python 3.3.5.
cimport cython
cimport numpy as np
# Returns negative mean-squared error
cdef double negative_mse(np.ndarray[double, ndim=1] y_true,
np.ndarray[double, ndim=1] y_pred) nogil:
cdef np.ndarray[double, ndim=1] err
err = y_true - y_pred
return -(err * err).sum() / y_true.shape[0]
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 表明您可以在 numpy 数组的内存视图上使用 nogil
。
根据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
这个也可以这样调用。这些时序显示了 2 倍的加速。 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
当我尝试编译以下代码时,出现错误
return sign_match.sum() / y_true.shape[0] ^
Converting to Python object not allowed without gil
有没有简单的方法可以克服这个问题?我能想到的最可行的解决方案是将数组的长度作为另一个参数传递。我正在使用 python 3.3.5.
cimport cython
cimport numpy as np
# Returns negative mean-squared error
cdef double negative_mse(np.ndarray[double, ndim=1] y_true,
np.ndarray[double, ndim=1] y_pred) nogil:
cdef np.ndarray[double, ndim=1] err
err = y_true - y_pred
return -(err * err).sum() / y_true.shape[0]
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 表明您可以在 numpy 数组的内存视图上使用 nogil
。
根据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
这个也可以这样调用。这些时序显示了 2 倍的加速。 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