Cython - 返回任意长度的TypeView MemoryView

时间:2016-11-20 21:13:25

标签: cython memoryview typed-memory-views

我编写了一个Cython函数,它以数字的列表/类型内存视图作为参数,并返回一个相同长度的Typed Memoryview:

def test(list_data):
    cdef unsigned int n = len(list_data)
    cdef unsigned int i = 0
    cdef double *results_arr = <double*>malloc(n* sizeof(double) )
    cdef double[:] results = <double[:n]>results_arr
    for i in range(n):
        results[i] = 220 - list_data[i]
    return results

在对其进行了几千次测试后,我开始收到Segmentation fault (core dumped)错误。我意识到这是一个内存管理问题,但我找不到如何管理函数返回的类型化内存视图的内存的示例。我发现的唯一有用的信息是memory allocation,它建议将result_arr的生命周期绑定到python对象并使用__dealloc__方法释放内存。

有没有办法管理memoryview垃圾收集,而不涉及创建用于释放内存的python类?

编辑: 我试过这个,似乎是以正确的方式释放内存。

def test(list_data):
    cdef unsigned int n = len(list_data)
    cdef unsigned int i = 0
    cdef double *arr = <double*>malloc(n* sizeof(double) )
    if not arr:
        raise MemoryError()
    cdef double[:] results = <double[:n]>arr
    for i in range(n):
        results[i] = 220 - list_data[i]
    free(arr)
    return results

为什么这样做并且有更好的方法来管理内存?

1 个答案:

答案 0 :(得分:1)

类型化的内存视图就像名称暗示了memorybuffer的视图一样。它不拥有那段内存,但提供了一种有效的访问方式。您引用的cython文档的一部分是将底层堆分配的c-array绑定到python垃圾收集器的方法。 如果你想像在这里一样使用c内存分配,你也必须对它负责。这是因为你现在正在进行c级工作,而且c对你免费做任何事。您的函数会查看已分配的内存,但会丢弃引用它的指针。现在,这种记忆无所事事地负责解放它。

如果您不想进入c世界,我建议您将数据读入python中的numpy数组,并将此数组传递给cython函数。 Python和numpy非常适合这种事情。

但是如果你想使用malloc,另一种选择可能是将它包装在扩展类型中。

cdef class mymemory:
    cdef:
        double *arr
        double[::1] results

    def __cinit__(self, int n):
        self.arr = <double*>malloc(n*sizeof(double))

    def __init__(self, int n):
        self.results = <double[:n]> arr

        """
        Some code for filling in the results.
        """

    def __dealloc(self):
        if self.arr != NULL:
            free(self.arr)

现在,当mymemory被垃圾收集时,底层c数组将被释放。这是一个替代方案,因为你要求它,但我仍然建议numpy这个。

在你的第二个函数上,你似乎分配内存,创建一个视图,然后再次释放它。现在这个memoryview正在查看的内存不再存在。所以你是对的,内存是正确释放的。但是现在,对于你来说,内存视图已经不再有用了。