在下面的代码中,我对数组 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/