首页 文章

Cython中的内存视图数组

提问于
浏览
1

在我之前的问题回答this回答的基础上,我想制作一些记忆视图 .

问题1

使用固定长度构建具有固定长度的2D存储器视图阵列,例如

mv1 = memoryview(b'1234')
mv2 = memoryview(b'abcd')
cdef const unsigned char[:,:] tmv = (mv1, mv2)

有了这个我得到:

TypeError: a bytes-like object is required, not 'tuple'

我尝试使用C指针数组:

ctypedef const unsigned char[:] k_t
cdef unsigned char* mva[2]
mv1 = memoryview(b'1234')
mv2 = memoryview(b'abcd')
cdef k_t mvk1 = mv1
cdef k_t mvk2 = mv2
mva = (&mvk1, &mvk2)

但这也不起作用:

Cannot take address of memoryview slice

问题2

构建一个任意长的3D数组,基本上是上面的2D数组对象的列表 . This对类似问题的其他答案和分配内存的Cython docs让我更接近(我相信我应该使用 malloc 和指针,如果没有必要我不想介绍C)但我仍然坚持问题#1 . 欢迎任何建议!


Edit (problem #1) :即使在混合中抛出一个Cython数组也会出现同样的错误:

from cython cimport view
mv1 = memoryview(b'1234')
mv2 = memoryview(b'abcd')
cvarr = view.array(shape=(2,1), itemsize=sizeof(char), format='B')
cvarr = (mv1, mv2)
print(cvarr[0][1])
# So far so good... this prints `50` as expected.
cdef const unsigned char[:,:] cvw = cvarr
# Adding this last line throws `a bytes-like object is required, not 'tuple'`

现在我真的很困惑 . 为什么元组对Cython数组有好处而对memview不好?

2 回答

  • 1

    Note: Not even close to a complete solution (at least at the moment!)

    我同意@DavidW,如果一个连续的cython类型的memoryview拥有所有的数据并且数据从你的python内存视图中复制到它中可能会更好 . 这是真的,特别是如果你计划只创建一次巨型cython类型的内存视图,但计划多次迭代它 .

    但是,您可以使用 PyMemoryView_GET_BUFFER 获取指向python内存视图内容的指针,以获取属于该内存视图的底层缓冲区 . 然后,您可以将数据转换为更大的数据结构(以便更快地复制)或者只是跟踪指针数组,每个元素都指向一个内存视图的数据(由于您将跳转,因此在迭代期间速度较慢)内存从memoryview缓冲区指针到memoryview缓冲区指针) .

    这是一种获取指向python memoryview对象的基础数据的指针的方法 . 从cython github的cpython folder,没有提到 PyMemoryView ,所以我不得不手动包装它:

    from cpython.object cimport PyObject
    
    cdef extern from "Python.h":
         Py_buffer* PyMemoryView_GET_BUFFER(PyObject *mview)
    
    cdef object mv1 = memoryview(b'1234')
    cdef Py_buffer* buf = PyMemoryView_GET_BUFFER(<PyObject*>mv1)
    cdef char* buf_ptr = <char*>buf.buf
    print(buf_ptr)#prints b'1234'
    

    Update 1:

    不是100%确定3D阵列结构应该是什么样子,所以我只是采用2D情况 . 既然你说你不想引入C,我就创建了这个行为类似于向量的 array_t 数据类型(好吧,指向一堆 void* 的指针) . 很多丑陋的样板,但在这里:

    from cpython.object cimport PyObject
    from libc.stdlib cimport malloc, calloc, realloc, free
    from libc.string cimport memcpy, memmove
    
    cdef extern from "Python.h":
        Py_buffer* PyMemoryView_GET_BUFFER(PyObject *mview)
    
    cdef char* get_view_ptr(object view):
        cdef Py_buffer* py_buf = PyMemoryView_GET_BUFFER(<PyObject*>view)
        cdef char* ptr = <char*>py_buf.buf
        return ptr
    
    ctypedef struct array_t:
        void** data
        int max_items
        int num_items
    
    cdef void array_init(array_t* array):
        array.data = NULL
        array.max_items = 0
        array.num_items = 0
    
    cdef void array_add(array_t* array, void* item):
        if array.max_items == 0:
            array.max_items = 10
            array.num_items = 0
            array.data = <void**>calloc(array.max_items, sizeof(void*))
        if array.max_items == array.num_items:
            array.max_items *= 2
            array.data = <void**>realloc(array.data, array.max_items * sizeof(void*))
        array.data[array.num_items] = item
        array.num_items += 1
    
    cdef void array_set(array_t* array, int index, void *item):
        if index < 0 or index >= array.max_items:
            return
        array.data[index] = item
    
    cdef void* array_get(array_t* array, int index):
        if index < 0 or index >= array.max_items:
            return NULL
        return array.data[index]
    
    cdef void array_remove(array_t* array, int index):
        cdef:
            void* src
            void* dest
        if index < 0 or index >= array.max_items:
            return
        array.data[index] = NULL
        if index+1 != array.max_items:
            src = &array.data[index+1]
            dest = &array.data[index]
            memmove(dest, src, (array.max_items - index) * sizeof(void*))
        array.num_items -= 1
    
    cdef void array_free(array_t* array):
        free(array.data)
    
    cdef int i
    cdef array_t a
    cdef object mv1 = memoryview(b'12345')
    cdef object mv2 = memoryview(b'67890')
    cdef object mv3 = memoryview(b'abcde')
    cdef object mv4 = memoryview(b'!@#$%')
    
    array_init(&a)
    array_add(&a, get_view_ptr(mv1))
    array_add(&a, get_view_ptr(mv2))
    array_add(&a, get_view_ptr(mv3))
    array_add(&a, get_view_ptr(mv4))
    
    for i in range(a.num_items):
        print(i, <char*>array_get(&a, i))
    
  • 0

    这似乎解决了问题#1:

    mv1 = memoryview(b'1234')
    mv2 = memoryview(b'abcd')
    cdef unsigned char mva[2][4]
    mva  = (mv1, mv2)
    cdef const unsigned char[:,:] cvw = mva
    

    然而,它在第4行发出两个警告

    Obtaining 'unsigned char [4]' from externally modifiable global Python value
    

    我想我可以忽略那些警告,因为我实际上正在使用 cvw 这是一个常数 .

相关问题