Cython выделяет память для массива без использования python - PullRequest
1 голос
/ 12 июля 2019

Я пытаюсь 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 Ответ

2 голосов
/ 12 июля 2019

Создание типизированного объекта в виде памяти приводит к взаимодействию с GIL, поскольку является объектом python.По этой причине вы не сможете вернуть новый типизированный объект в виде памяти из cdef nogil function.. Однако есть несколько способов обойти это ограничение с помощью 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].Таким образом, память не выделяется внутри самой функции.Используя этот подход, _gauss может быть объявлено cdef nogil.

Если вы действительно обеспокоены накладными расходами выделения памяти для массива numpy, вы можете попробовать использовать функции в стиле C, такие как malloc,calloc, realloc и free для управления вашей памятью.Этот указатель затем может быть приведен к соответствующему типизированному виду памяти;однако, это вызовет gil при создании указанного вида памяти.

...