1

我知道这个问题,但我一直在寻找一种更简单的方法来从 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 需要连续块等?

4

2 回答 2

2

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

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

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

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


可能还有几点值得说明:

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

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

于 2019-10-31T11:01:53.607 回答
2

除了@DavidW 提供的精彩答案之外,我还想添加更多信息。在您包含的代码中,我看到您正在分配一个整数数组,然后在 for 循环中将内容清零。实现这一点的更方便的方法是改用 C 的 calloc 函数,它保证指向零内存的指针,并且之后不需要 for 循环。

此外,您可以创建一个指向数据“数组”的单个 int *,该数据“数组”调用的总大小为 2 * d * sizeof(int)。这将确保数据的两个“行”彼此相邻,而不是分开且参差不齐。然后可以将其直接转换为 2d memoryview。

正如评论中所承诺的,转换代码如下所示(包括使用 calloc):

cdef int[:, :] get_zeros(int d):    
    cdef int *arr = <int *>calloc(2 * d, sizeof(int))
    cdef int[:, :] arr_view = <int[:2, :d]>arr
    return arr_view

如果您想尝试一下,根据文档,python c-api 中似乎也有一个calloc 等效项。但是,它似乎没有包含在 cython 的 mem.pxd 模块中,这就是您可能无法找到它的原因。您可以在代码中声明一个类似的extern块来包装它,就像该链接中包含的其他函数一样。

如果您想了解更多关于编写分配器以从大块中分配内存的信息,如果您走预分配路线(即 PyMem_* 函数可能在幕后执行的操作,但在您控制您的特定用例)。

于 2019-10-31T21:21:50.433 回答