在不复制的情况下将 C 数组绑定到 Numpy 数组
Binding C array to Numpy array without copying
我正在编写一个 Python class,它将包装一个包含 C 结构的 C 模块。我使用的是 Cython 语言(Python 和 C 的超集语言)。 C 结构在构造函数中被 malloc,并包含一个我想在 Python 中使用的数组。该数组将在 Python 中表示为 NumPy 数组,但我不想将值复制到它。我想 link NumPy 数组直接到 malloc 内存。对于此任务,我使用 NumPy Array API,特别是此函数:
PyObject*
PyArray_SimpleNewFromData
(int nd, npy_intp* dims, int typenum, void* data)
我设法在 Cython 中使用这段代码将 NumPy 数组绑定到 C 结构的数组,只要 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)
如您所见,class 有其 __dealloc__
方法,它将处理 C 中分配的内存,并在没有对 MultimediaParams
实例的引用时释放它。
在这种绑定中,NumPy 不拥有数组的内存。
问题:当MultimediaParams
对象被释放,数组的内存被释放时,NumPy对象仍然指向刚刚被释放的内存。当 NumPy 对象尝试 access/modify 已释放的内存时,这将导致段错误。
只要有一个 NumPy 对象在使用它的内存,我怎样才能确保 MultimediaParams
对象不被释放?
据我所知,我需要做的就是让 NumPy 对象引用一个 MultimediaParams
实例,它从中获得要指向的内存。
我尝试使用 ndarray.base = <PyObject*>self
这样 NumPy 就会知道它的基础对象,这应该会添加另一个对 MultimediaParams
实例的引用,并且只要 NumPy 数组还活着就会导致它不会被释放。这一行导致我的测试失败,因为 NumPy 数组的内容变成了垃圾。
澄清:NumPy 数组不拥有 C 数组内存,我不希望它拥有。我想要MultimediaParams
负责释放 C 结构(包含数组数据),但只要 NumPy 对象还活着就不会这样做。
有什么建议吗?
正如@J.F.Sebastian 的评论所指出的,问题很可能是当您正确地将指向 MultimediaParams
实例的指针分配给 NumPy 数组的 base
引用时,您实际上并没有增加它的引用计数,因为赋值是在 C 中进行的,而不是在 Python 中进行的。这可能会导致 MultimediaParams
对象的过早垃圾收集,其内存被重用并导致您在 ndarray 中遇到垃圾数据。
使用宏 Py_INCREF
手动增加 MultimediaParams
对象的引用计数应该会产生所需的行为。
我正在编写一个 Python class,它将包装一个包含 C 结构的 C 模块。我使用的是 Cython 语言(Python 和 C 的超集语言)。 C 结构在构造函数中被 malloc,并包含一个我想在 Python 中使用的数组。该数组将在 Python 中表示为 NumPy 数组,但我不想将值复制到它。我想 link NumPy 数组直接到 malloc 内存。对于此任务,我使用 NumPy Array API,特别是此函数:
PyObject*
PyArray_SimpleNewFromData
(int nd, npy_intp* dims, int typenum, void* data)
我设法在 Cython 中使用这段代码将 NumPy 数组绑定到 C 结构的数组,只要 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)
如您所见,class 有其 __dealloc__
方法,它将处理 C 中分配的内存,并在没有对 MultimediaParams
实例的引用时释放它。
在这种绑定中,NumPy 不拥有数组的内存。
问题:当MultimediaParams
对象被释放,数组的内存被释放时,NumPy对象仍然指向刚刚被释放的内存。当 NumPy 对象尝试 access/modify 已释放的内存时,这将导致段错误。
只要有一个 NumPy 对象在使用它的内存,我怎样才能确保 MultimediaParams
对象不被释放?
据我所知,我需要做的就是让 NumPy 对象引用一个 MultimediaParams
实例,它从中获得要指向的内存。
我尝试使用 ndarray.base = <PyObject*>self
这样 NumPy 就会知道它的基础对象,这应该会添加另一个对 MultimediaParams
实例的引用,并且只要 NumPy 数组还活着就会导致它不会被释放。这一行导致我的测试失败,因为 NumPy 数组的内容变成了垃圾。
澄清:NumPy 数组不拥有 C 数组内存,我不希望它拥有。我想要MultimediaParams
负责释放 C 结构(包含数组数据),但只要 NumPy 对象还活着就不会这样做。
有什么建议吗?
正如@J.F.Sebastian 的评论所指出的,问题很可能是当您正确地将指向 MultimediaParams
实例的指针分配给 NumPy 数组的 base
引用时,您实际上并没有增加它的引用计数,因为赋值是在 C 中进行的,而不是在 Python 中进行的。这可能会导致 MultimediaParams
对象的过早垃圾收集,其内存被重用并导致您在 ndarray 中遇到垃圾数据。
使用宏 Py_INCREF
手动增加 MultimediaParams
对象的引用计数应该会产生所需的行为。