我正在尝试对一个简单的函数进行Cythonize,我希望能够使用nogil
语句对其进行编译。我在Jupyter笔记本中拥有的是:
%%cython -a
import numpy as np
cimport numpy as np
cimport cython
from libc.math cimport exp, pi
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cdef double[:, ::1] _gauss(Py_ssize_t yw, Py_ssize_t xw, double y0, double x0, double sy, double sx):
"""Simple normalized 2D gaussian function for rendering"""
# for this model, x and y are seperable, so we can generate
# two gaussians and take the outer product
cdef double amp = 1 / (2 * pi * sy * sx)
cdef double[:, ::1] result = np.empty((yw, xw), dtype=np.float64)
cdef Py_ssize_t x, y
for y in range(yw):
for x in range(xw):
result[y, x] = exp(-((y - y0) / sy) ** 2 / 2 - ((x - x0) / sx) ** 2 / 2) * amp
return result
def gauss(yw, xw, y0, x0, sy, sx):
return _gauss(yw, xw, y0, x0, sy, sx)
编译得很好。如果我将第一行cdef
更改为:
...
cdef double[:, ::1] _gauss(Py_ssize_t yw, Py_ssize_t xw, double y0, double x0, double sy, double sx) nogil:
...
然后编译失败,因为第一和第三行cdef
与python解释器交互,我不确定为什么(尤其是对于第一行)。
答案 0 :(得分:2)
创建类型化的memoryview对象会导致与GIL交互,因为它是一个python对象。因此,您将无法从cdef nogil function.
返回新类型的memoryview对象。但是,有几种方法可以解决GIL的这一限制。
一个选择是仅释放函数中的GIL。这可以通过使用with nogil:
块来完成,该块可以放在迭代代码周围。该功能如下所示:
@cython.boundscheck(False)
@cython.wraparound(False)
@cython.cdivision(True)
cdef double[:, ::1] _gauss(Py_ssize_t yw, Py_ssize_t xw, double y0, double x0, double sy, double sx):
#Do gil-interacting, python stuff here
cdef double amp = 1 / (2 * pi * sy * sx)
cdef double[:, ::1] result = np.empty((yw, xw), dtype=np.float64)
cdef Py_ssize_t x, y
with nogil:
#And now basically write C code
for y in range(yw):
for x in range(xw):
result[y, x] = exp(-((y - y0) / sy) ** 2 / 2 - ((x - x0) / sx) ** 2 / 2) * amp
return result
另一种选择是让用户传入类型为double[:, ::1]
的numpy数组。这样,就不会在函数本身内分配内存。使用该方法,可以将_gauss
声明为cdef nogil
。
如果您真的担心为numpy数组分配内存的开销,则可以尝试使用malloc
,calloc
,realloc
和{{ 1}}来管理您的记忆。然后可以将该指针强制转换为适当的类型化的memoryview;但是,这样做将在创建所述memoryview时调用gil。