python - 将 numpy 数组的切片 View 传递给 cython - 复制发生在哪里?

标签 python cython

在下面的代码中,我对数组 X 进行切片,从而在 X 上创建一个名为 X_cut 的 View 。然后,我在切片上使用 cython 内存 View ,并将其作为一维数组传递给线性访问内存的 c 函数。

我能否确定传递给 C 代码的指针实际上引用的是 6x6 线性化矩阵?

如果是这样,复制操作在哪里发生?是在X_cut.ravel()中吗?

%%cython

import numpy as np

cdef extern from "/some/path/to/get_5_5.c":
    long get5_5(long* arr, int M, int N)

M = 6
N = 8

X = np.arange(M*N).reshape(M, N)

N_cut = 6
X_cut = X[:, :N_cut]

cdef long[::1] arr = X_cut.ravel()

print(get5_5(&arr[0], M, N_cut))

/some/path/to/get_5_5.c

long get5_5(long* arr, int M, int N) {
  return arr[5*N + 5];
}

最佳答案

Cython 的类型化内存 View 使用 Buffer-Protocol访问数据,这意味着它们与数据复制无关。

理论上,导出器在通过缓冲区协议(protocol)公开数据时可以决定复制数据。然而,通常使用Buffer-Protocol是为了避免内存复制,因此复制不是正常情况。这意味着,您不能 100% 确定,将类型化内存 View 绑定(bind)到导出缓冲区的对象时不会发生复制 - 您必须知道导出器的实现,但复制的情况确实很少见。但这不是这里发生的事情。

当调用 X_cut.ravel() 时,必须进行复制 - resulting memory must be contiguous ,但 X_cut 中的底层内存不是(请参阅 X_cut.flags),因为它仍然与 X 共享内存并切断最后一个每行的元素会导致内存中出现“漏洞”。

以下是内存布局(为简单起见,M=2、N=3、N_cut=2):

  X:               X00,X01,X02,X10,X11,X12
  X_cut:           X00,X01,...,X10,X11        # ... - hole in memory, no copying
  X_cut.ravel():   X00,X01,X10,X11            # memory contiguous, copying needed
<小时/>

它会把你留在哪里?您要么必须接受内存的复制,要么扩展 get5_5 的接口(interface),这样您还可以传递非连续的内存布局 - 与缓冲区协议(protocol)没有什么不同。

例如,要传递 X_cut 而不进行复制,您不仅需要指定形状,还需要指定沿尺寸的步幅,即

#only stride along the the first dimension is needed:
cdef long get5_5(long* arr, int stride_row, int stride_col):
     return arr[stride_row*5+5]

问题是,如何在不复制的情况下从X_cut获取指针long* arr

一种可能性是使用 2D 内存 View (我会选择此选项):

cdef long[:,::1] arr = X_cut
get5_5(&arr[0][0], M, N) # and not M, N_cut

另一种方法是使用np.reshape(-1),它创建一个新的一维 View 并且不会总是复制数据(与np.ravel()):

cdef long[::1] arr = X.reshape(-1) # X_cut.reshape(-1) would copy the data!
get5_5(&arr[0], M, N)  # and not M, N_cut

关于python - 将 numpy 数组的切片 View 传递给 cython - 复制发生在哪里?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54707333/

相关文章:

python - Django - 我应该在生产环境中为管理用户设置 show_toolbar 吗?

python - 导入 Cython 生成的 .so 文件时,这个 ImportError 是什么意思?

python - 在 OS X 上启动并运行 Cython

pointers - 在 Python 中访问 memoryview 类型的数据缓冲区

c++ - Cython 包装 operator<< 来自多个命名空间

没有eval的Python变量变量?

python存储在自变量中与传递给def

python - 扩展 Cython 类时,__cinit__() 恰好需要 2 个位置参数

Python Pandas : Pivot table : aggfunc concatenate instead of np. 大小或 np.sum

python - 将颜色应用于特定节点网络x