首页 > 解决方案 > Cython 中动态数组的 2D MemoryView

问题描述

我知道这个问题,但我一直在寻找一种更简单的方法来从 C 数组生成 2d 内存视图。由于我是 C 和 Cython noobie,有人可以解释一下为什么像

cdef int[:, :] get_zeros(int d):
    # get 2-row array of zeros with d as second dimension
    cdef int i
    cdef int *arr = <int *> malloc(sizeof(int) * d)
    for i in range(d):
        arr[i] = 0
    cdef int[:, :] arr_view
    arr_view[0, :] = <int[:d]>arr
    arr_view[1, :] = <int[:d]>arr
    return arr_view

不行吗?

编译时我得到Cannot assign type 'int[::1]' to 'int'错误。这是否意味着 2d memview 被第一个 assign 语句折叠到 1d 或者是因为 memoryviews 需要连续块等?

标签: pythonmemorymemory-managementcythonmemoryview

解决方案


显然很难“解释为什么某些东西 [...] 不起作用”,因为最终这只是一个设计决策,可以采取不同的方式。但:

Cython 内存视图被设计为非常愚蠢。他们所做的只是提供一些很好的语法来访问实现Python 缓冲区协议的东西的内存,然后有一点额外的语法让你做一些事情,比如获取指针的 1D 内存视图。

此外,内存视图作为一个整体包装了一些东西。当你创建cdef int[:, :] arr_view它是无效的,直到你这样做arr_view = something。尝试分配它的一部分是无稽之谈,因为(a)它会将分配委托给它使用缓冲区协议包装的东西,并且(b)分配的确切工作方式取决于您包装的缓冲区协议的格式. 如果包装一个“间接”缓冲区协议对象,你所做的可能是有效的,但如果包装一个连续的数组则没有意义。由于arr_view可以包装 Cython 编译器必须将其视为错误。

您链接到的问题实现了缓冲区协议,因此是实现这种数组的正确方法。您正在尝试做的是采用额外的语法,从指针中提供 1D 内存视图,并将其强制转换为 2D 内存视图的一部分,模糊地希望这可能会起作用。这需要很多逻辑,远远超出了 Cython 内存视图的设计范围。


可能还有几点值得说明:

  • 指针的内存视图不处理指针的释放(因为它们几乎不可能事后猜测你想要什么)。你必须处理这个逻辑。如果有效,您当前的设计会泄漏内存。在您链接到包装类的设计中,可以实现这一点__dealloc__(尽管该答案中没有显示),因此效果更好。

  • 我个人的观点是“参差不齐的数组”(指向指针的二维数组)很糟糕。它们需要大量的分配和释放。有很多机会对它们进行半初始化。访问它们需要几个间接级别,因此速度很慢。唯一对他们有用的是它们arr[idx1][idx2]在 C 中提供了一种语法。总的来说,我更喜欢 Numpy 分配一维数组并使用形状/步幅来确定索引位置的方法。(显然,如果您要包装现有库,那么您可能不是您的选择......)


推荐阅读