【问题标题】:Binding C array to Numpy array without copying将 C 数组绑定到 Numpy 数组而不复制
【发布时间】:2015-11-02 12:52:42
【问题描述】:

我正在编写一个 Python 类,它将包装一个包含 C 结构的 C 模块。我正在使用 Cython 语言(Python 和 C 的超集语言)。 C 结构在构造函数中是 malloc 的,并且包含一个我想在 Python 中使用的数组。该数组将在 Python 中表示为 NumPy 数组,但我不想将值复制到其中。我想将 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)

如您所见,该类有其 __dealloc__ 方法,该方法将处理在 C 中分配的内存,并在没有对 MultimediaParams 实例的引用时释放它。

在这种绑定中,NumPy 不拥有数组的内存。

问题:当MultimediaParams对象被释放,数组的内存被释放时,NumPy对象仍然指向刚刚释放的内存。当 NumPy 对象尝试访问/修改已释放的内存时,这将导致段错误。

只要有一个 NumPy 对象在使用其内存,我如何确保 MultimediaParams 对象不会被释放?

据我了解,我需要做的就是让 NumPy 对象引用一个 MultimediaParams 实例,从该实例中获取内存指向。 我尝试使用ndarray.base = &lt;PyObject*&gt;self,以便 NumPy 知道它的基对象,这应该添加对 MultimediaParams 实例的另一个引用,并且只要 NumPy 数组还活着,它就不会被释放。这一行导致我的测试失败,因为 NumPy 数组的内容变成了垃圾。

澄清NumPy 数组不拥有 C 数组内存的所有权,我不希望它拥有。我希望 MultimediaParams 负责释放C 结构(包含数组数据),但只要 NumPy 对象还活着,就不要这样做。

有什么建议吗?

【问题讨论】:

  • @DavidW:它看起来不像是重复的。强制内存所有权可能会导致双重free(),这是不好的。 OP 的问题是 ndarray.base = &lt;PyObject*&gt;self doesn't work
  • 如果你在.base分配之后Py_INCREF(self)会发生什么?
  • @J.F.Sebastian 我在想 OP 真正想要解决的问题是确保内存与数组具有相同的生命周期(并试图通过将其绑定到 @987654343 的实例来解决它@)。但是他们可能有其他原因来链接这两个实例 - 在这种情况下我错了,它不是重复的......
  • @DavidW:PyArray_ENABLEFLAGS(ndarray, np.NPY_OWNDATA) 如何让MultimediaParams 实例保持活动状态?

标签: python c arrays numpy cython


【解决方案1】:

正如@J.F.Sebastian 的评论所指出的那样,问题很可能是当您正确地将指向您的MultimediaParams 实例的指针分配给NumPy 数组的base 引用时,您实际上并没有增加它的引用计数,因为赋值是在 C 中进行的,而不是在 Python 中。这可能会导致 MultimediaParams 对象的过早垃圾收集,其内存被重用并导致您在 ndarray 中遇到垃圾数据。

使用宏 Py_INCREF 手动增加 MultimediaParams 对象的引用计数应该会产生所需的行为。

【讨论】:

  • 好的,但问题是:Py_INCREF 如何知道具体的 ndarray 对象将指向 MultimediaParams 对象?
  • Python 会跟踪对象的引用次数,这称为引用计数。它不需要知道哪些引用。每当创建对对象的引用时,对象的引用计数就会增加,反之,当引用被删除时,对象的引用计数就会减少。您可以通过设置 ndarray 的基本属性来创建引用,但这是在 C 中完成的,其中 Python 不会为您增加 MultimediaParams 对象的引用计数器,因此您需要手动完成。
猜你喜欢
  • 2015-07-12
  • 2016-08-19
  • 2011-07-30
  • 2011-12-13
  • 2020-09-29
  • 1970-01-01
  • 2014-07-04
相关资源
最近更新 更多