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

时间:2019-02-15 10:29:28

标签: python cython

在下面的代码中,我切片了一个数组X,从而在X上创建了一个名为X_cut的视图。然后,我在切片上使用cython内存视图,并将其作为1d数组传递给线性访问内存的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];
}

1 个答案:

答案 0 :(得分:1)

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

从理论上讲,当通过Buffer-Protocol公开数据时,导出器可以决定复制数据。但是,通常使用Buffer-Protocol以避免内存复制,因此复制不是正常情况。这就是说,您不能百分百确定在将类型化的内存视图绑定到导出缓冲区的对象时不会发生复制-您必须知道导出器的实现,但是复制的情况确实很少。这不是这里发生的事情。

在调用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的接口,因此您还可以传递非连续的内存布局-与Buffer-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]

问题是,如何从long* arr获取指针X_cut而不进行复制。

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

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

另一种方法是使用np.reshape(-1),它会创建一个新的一维视图,并且不会始终复制数据(与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