Cython不使用python为数组分配内存

时间:2019-07-11 21:37:26

标签: python cython

我正在尝试对一个简单的函数进行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解释器交互,我不确定为什么(尤其是对于第一行)。

1 个答案:

答案 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数组分配内存的开销,则可以尝试使用malloccallocrealloc和{{ 1}}来管理您的记忆。然后可以将该指针强制转换为适当的类型化的memoryview;但是,这样做将在创建所述memoryview时调用gil。