Связывание массива 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 жив.
Любые предложения?
Ответы
Ответ 1
Как указывает комментарий @JFSebastian, проблема в том, что, хотя вы правильно назначили указатель на экземпляр MultimediaParams
на ссылку base
массива NumPy, вы фактически не увеличиваете его количество ссылок, потому что назначение выполняется в C, а не в Python. Вероятно, это приводит к преждевременной сборке мусора объекта MultimediaParams
, память которого используется повторно и вызывает то, что вы испытываете как данные мусора в ndarray.
Вручное увеличение счетчика ссылок объекта MultimediaParams
с использованием макроса Py_INCREF
должно давать желаемое поведение.