Подтвердить что ты не робот

Связывание массива C с массивом Numpy без копирования

Я пишу класс Python, который обернет модуль C, содержащий структуру C. Я использую язык Cython (супер-заданный язык Python и C). C struct является malloc'd в конструкторе и содержит массив, который я хочу использовать в Python. Массив будет представлен в Python как массив NumPy, но я не хочу копировать значения в него. Я хочу связать массив NumPy напрямую с памятью malloc'd. Для этой задачи я использую NumPy Array API и, в частности, эту функцию:

PyObject* PyArray_SimpleNewFromData (int nd, npy_intp* dims, int typenum, void* data)

Мне удалось привязать массив NumPy к массиву C-структуры, используя этот код в Cython, и он работает хорошо, пока массив NumPy и объект MultimediaParams имеют одинаковое время жизни:

cdef class MultimediaParams:
    def __init__(self, **kwargs):
        self._mm_np = < mm_np *> malloc(sizeof(mm_np))
        #some code...

    def as_ndarray(self): #TODO: what if self deallocated but numpy array still exists(segfault?)
        cdef numpy.npy_intp shape[1]
        cdef int arr_size = sizeof(self._mm_np[0].n2) / sizeof(self._mm_np[0].n2[0])
        shape[0] = < numpy.npy_intp > arr_size
        cdef numpy.ndarray ndarray
        ndarray = numpy.PyArray_SimpleNewFromData(1, shape, numpy.NPY_DOUBLE, self._mm_np[0].n2)

        return ndarray

    def __dealloc__(self):
        free(self._mm_np)

Как вы можете видеть, класс имеет свой метод __dealloc__, который позаботится о памяти, выделенной на C, и освободит ее, если нет ссылок на экземпляр MultimediaParams.

В этом типе привязки NumPy не владеет памятью массива.

Проблема: когда объект MultimediaParams освобождается и память массива освобождается, объект NumPy все еще указывает на только что освобожденную память. Это вызовет segfault, когда объект NumPy попытается получить доступ/изменить освобожденную память.

Как я могу убедиться, что объект MultimediaParams не освобождается, если существует объект NumPy с его памятью?

Как я понимаю, все, что мне нужно сделать, - это сделать объект NumPy референтом экземпляра MultimediaParams, из которого он получил память. Я попытался использовать ndarray.base = <PyObject*>self, так что NumPy будет знать свой базовый объект, это должно добавить другую ссылку на экземпляр MultimediaParams и не приведет к его освобождению до тех пор, пока массив NumPy будет жив. Эта строка заставляет мои тесты терпеть неудачу, потому что содержимое массива NumPy превращается в мусор.

CLARIFICATION: Массив NumPy не получает права владения памятью массива C, и я не хочу этого. Я хочу, чтобы MultimediaParams отвечал за освобождение C-структуру (которая содержит данные массива), но не делать этого, пока объект NumPy жив.

Любые предложения?

4b9b3361

Ответ 1

Как указывает комментарий @JFSebastian, проблема в том, что, хотя вы правильно назначили указатель на экземпляр MultimediaParams на ссылку base массива NumPy, вы фактически не увеличиваете его количество ссылок, потому что назначение выполняется в C, а не в Python. Вероятно, это приводит к преждевременной сборке мусора объекта MultimediaParams, память которого используется повторно и вызывает то, что вы испытываете как данные мусора в ndarray.

Вручное увеличение счетчика ссылок объекта MultimediaParams с использованием макроса Py_INCREF должно давать желаемое поведение.