首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Cython:理解带有indirect_contignuous内存布局的类型化内存视图

Cython:理解带有indirect_contignuous内存布局的类型化内存视图
EN

Stack Overflow用户
提问于 2018-12-27 19:37:25
回答 1查看 1K关注 0票数 3

我想了解更多关于Cython的令人敬畏的输入内存视图和内存布局indirect_contiguous

根据文档,在“指针列表是连续的”时使用indirect_contiguous

还有一个例子用法:

代码语言:javascript
复制
# contiguous list of pointers to contiguous lists of ints
cdef int[::view.indirect_contiguous, ::1] b

因此,如果我错了,请纠正我,但我假设“指向ints连续列表的指针的连续列表”意味着类似于下面的c++虚拟代码创建的数组:

代码语言:javascript
复制
// 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**获得一个内存视图:

代码语言:javascript
复制
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

但我会收到编译错误:

代码语言:javascript
复制
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的概念?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-12-27 22:32:51

让我们将记录设置为正确:类型化内存视图只能用于实现缓冲协议的对象。

原始C-指针显然不实现缓冲区协议。但是,您可能会问,为什么像下面这样的快速和肮脏的代码会工作:

代码语言:javascript
复制
%%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阵列 (即cython.view.array),它封装原始指针并通过缓冲区协议公开它。
  • 此数组用于创建类型化内存视图。

你对view.indirect_contiguous的理解是正确的--这正是你想要的。然而,问题是view.array,它无法处理这种类型的数据布局。

在协议缓冲术语中,view.indirectview.indirect_contiguous对应于PyBUF_INDIRECT,为此,字段suboffsets必须包含一些有意义的值(即某些维度的>=0 )。但是,正如在源代码 view.array中所看到的那样,它根本没有这个成员--它根本无法表示复杂的内存布局!

它给我们留下了什么?正如@chrisb和@DavidW在另一个问题中指出的,您必须实现一个包装器,它可以通过协议缓冲区公开您的数据结构。

Python中有数据结构,它使用间接内存布局--最突出的是PIL-数组。理解suboffsets应该如何工作的一个好起点是这份文件

代码语言:javascript
复制
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
}

在您的例子中,stridesoffsets应该是

  • 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*y
    • 第二维度是直接的(由suboffset[1]<0发出信号),因此不需要取消引用。

  • 我们完成了,pointer指向所需的地址,并以行D返回。

FWIW,我实现了一个库,它可以通过缓冲区协议导出int**和类似的内存布局:缓冲器

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53950020

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档