我想了解更多关于Cython的令人敬畏的输入内存视图和内存布局indirect_contiguous。
根据文档,在“指针列表是连续的”时使用indirect_contiguous。
还有一个例子用法:
# contiguous list of pointers to contiguous lists of ints
cdef int[::view.indirect_contiguous, ::1] b因此,如果我错了,请纠正我,但我假设“指向ints连续列表的指针的连续列表”意味着类似于下面的c++虚拟代码创建的数组:
// we want to create a 'contiguous list of pointers to contiguous lists of ints'
int** array;
// allocate row-pointers
// This is the 'contiguous list of pointers' related to the first dimension:
array = new int*[ROW_COUNT]
// allocate some rows, each row is a 'contiguous list of ints'
array[0] = new int[COL_COUNT]{1,2,3}因此,如果我正确理解了,那么在我的Cython代码中,应该可以从这样的int**获得一个内存视图:
cdef int** list_of_pointers = get_pointers()
cdef int[::view.indirect_contiguous, ::1] view = <int[:ROW_COUNT:view.indirect_contiguous,COL_COUNT:1]> list_of_pointers但我会收到编译错误:
cdef int[::view.indirect_contiguous, ::1] view = <int[:ROW_COUNT:view.indirect_contiguous,:COL_COUNT:1]> list_of_pointers
^
------------------------------------------------------------
memview_test.pyx:76:116: Pointer base type does not match cython.array base type我做错什么了?我是否遗漏了任何类型,或者我是否误解了indirect_contiguous的概念?
发布于 2018-12-27 22:32:51
让我们将记录设置为正确:类型化内存视图只能用于实现缓冲协议的对象。
原始C-指针显然不实现缓冲区协议。但是,您可能会问,为什么像下面这样的快速和肮脏的代码会工作:
%%cython
from libc.stdlib cimport calloc
def f():
cdef int* v=<int *>calloc(4, sizeof(int))
cdef int[:] b = <int[:4]>v
return b[0] # leaks memory, so what?在这里,指针(v)用于构造类型化内存视图(b)。然而,还有更多的,在引擎盖下(可以在cythonized文件中看到):
cython.view.array),它封装原始指针并通过缓冲区协议公开它。你对view.indirect_contiguous的理解是正确的--这正是你想要的。然而,问题是view.array,它无法处理这种类型的数据布局。
在协议缓冲术语中,view.indirect和view.indirect_contiguous对应于PyBUF_INDIRECT,为此,字段suboffsets必须包含一些有意义的值(即某些维度的>=0 )。但是,正如在源代码 view.array中所看到的那样,它根本没有这个成员--它根本无法表示复杂的内存布局!
它给我们留下了什么?正如@chrisb和@DavidW在另一个问题中指出的,您必须实现一个包装器,它可以通过协议缓冲区公开您的数据结构。
Python中有数据结构,它使用间接内存布局--最突出的是PIL-数组。理解suboffsets应该如何工作的一个好起点是这份文件。
void *get_item_pointer(int ndim, void *buf, Py_ssize_t *strides,
Py_ssize_t *suboffsets, Py_ssize_t *indices) {
char *pointer = (char*)buf; // A
int i;
for (i = 0; i < ndim; i++) {
pointer += strides[i] * indices[i]; // B
if (suboffsets[i] >=0 ) {
pointer = *((char**)pointer) + suboffsets[i]; // C
}
}
return (void*)pointer; // D
}在您的例子中,strides和offsets应该是
strides=[sizeof(int*), sizeof(int)] (即通常的x86_64机器上的[8,4] )offsets=[0,-1],即只有第一个维度是间接的。然后,获取元素[x,y]的地址,如下所示:
A中,pointer设置为buf,让我们假设BUF。B中,pointer变为BUF+x*8,并指向指向x行的指针的位置.suboffsets[0]>=0,我们取消了C行中指针的引用,因此它显示了地址ROW_X --x-第一行的开始。
B中,我们使用strides获得y元素的地址,即pointer=ROW_X+4*ysuboffset[1]<0发出信号),因此不需要取消引用。
pointer指向所需的地址,并以行D返回。FWIW,我实现了一个库,它可以通过缓冲区协议导出int**和类似的内存布局:缓冲器。
https://stackoverflow.com/questions/53950020
复制相似问题