10

在 Cython 代码中,我可以分配一些内存并将其包装在内存视图中,例如:

cdef double* ptr
cdef double[::1] view
ptr = <double*> PyMem_Malloc(N*sizeof('double'))
view = <double[:N]> ptr

如果我现在使用 释放内存PyMem_Free(ptr),则尝试访问类似的元素ptr[i]会引发错误,这是应该的。但是,我可以安全地尝试访问view[i](虽然它不会返回原始数据)。

我的问题是:释放指针总是安全的吗?内存视图对象是否以某种方式通知内存被释放,或者我应该以某种方式手动删除视图?此外,即使内存视图引用了内存,是否也能保证释放内存?

4

1 回答 1

6

它需要深入研究 C 代码才能显示这一点,但是:

该行view = <double[:N]> ptr实际上生成一个__pyx_array_obj. 这与文档中详述的“Cython 数组”和 cimportable ascython.view.array相同的类型。Cython 数组确实有一个名为的可选成员callback_free_data,它可以充当析构函数。

该行翻译为:

struct __pyx_array_obj *__pyx_t_1 = NULL;
# ...
__pyx_t_1 = __pyx_array_new(__pyx_t_2, sizeof(double), PyBytes_AS_STRING(__pyx_t_3), (char *) "c", (char *) __pyx_v_ptr);

__pyx_t_2并且__pyx_t_3只是分别存储大小和格式的临时对象)。如果我们查看内部,__pyx_array_new我们首先会看到数组的data成员直接分配给作为传递的值__pyx_v_ptr

__pyx_v_result->data = __pyx_v_buf;

(即没有制作副本),其次callback_free_data是没有设置。旁注: C 代码cython.view.array实际上是从Cython 代码生成的,因此如果您想进一步调查,它可能比生成的 C 更容易阅读。


本质上,memoryview 拥有cython.view.array一个指向原始数据的指针,但没有callback_free_data设置。当 memoryview 死掉的析构函数cython.view.array被调用。这清理了一些内部结构,但对释放它指向的数据没有任何作用(因为它没有指示如何这样做)。

因此,在调用后访问 memoryview是不安全PyMem_Free的。你似乎侥幸逃脱的事实是运气。但是,如果您不访问它,memoryview 保持存在是安全的。像这样的功能:

def good():
    cdef double* ptr
    cdef double[::1] view
    ptr = <double*> PyMem_Malloc(N*sizeof('double'))
    try:
        view = <double[:N]> ptr
        # some other stuff
    finally:
        PyMem_Free(ptr)
    # some other stuff not involving ptr or view

会好的。像这样的功能:

def bad():
    cdef double* ptr
    cdef double[::1] view
    ptr = <double*> PyMem_Malloc(N*sizeof('double'))
    try:
        view = <double[:N]> ptr
        # some other stuff
    finally:
        PyMem_Free(ptr)
    view[0] = 0
    return view

这将是一个坏主意,因为它传回了一个不指向任何东西的内存视图,并view在它查看的数据被释放后访问。

你一定要确保PyMem_Free在某个时候调用,否则你有内存泄漏。如果view被传递,因此很难跟踪生命周期,一种方法是手动创建一个cython.view.arraywith callback_free_dataset:

cdef view.array my_array = view.array((N,), allocate_buffer=False)
my_array.data = <char *> ptr
my_array.callback_free_data = PyMem_Free
view = my_array

如果生命周期很明显,那么view您可以照常打电话PyMem_Freeptr

于 2019-11-06T13:54:06.317 回答