Массив просмотров памяти в Cython - PullRequest
0 голосов
/ 21 мая 2018

Опираясь на этот ответ на мои предыдущие вопросы, я хотел бы сделать массивы просмотров памяти.

Задача 1

Создание двумерного массива видов памяти с фиксированной длиной, например

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

Создание произвольного длинного трехмерного массива, в основном спискаиз вышеупомянутых объектов 2D-массива. Этот другой ответ на аналогичный вопрос и Документы Cython о выделении памяти сделали меня немного ближе (я считаю, что я должен использовать malloc и указатели, я бы не хотел вводитьC ++, если не нужно), но я все еще застрял с проблемой # 1.Любые предложения приветствуются!


Редактировать (проблема № 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 ]

0 голосов
/ 21 мая 2018

Примечание: Даже близко не к полному решению (по крайней мере, на данный момент!)

Я согласен с @DavidW, что, вероятно, будет лучше, если у одного непрерывного типа с типом памяти, напечатанного на Cython, естьвсе данные и данные копируются в него из ваших обзоров памяти python.Это верно, особенно если вы планируете создать гигантский вид памяти с типом cython только один раз, но планируете повторять его много раз.

Однако, вы могли бы получить указатель на содержимое вашего представления памяти python, используя PyMemoryView_GET_BUFFER, чтобы получить базовый буфер, принадлежащий этому представлению памяти.Затем вы можете либо memcpy данные в большую структуру данных (для более быстрого копирования), либо просто отслеживать массив указателей, причем каждый элемент указывает на данные просмотра памяти (который медленнее во время итерации, поскольку вы будетепереход по памяти из указателя буфера памяти в указатель буфера памяти).

Вот способ получить указатель на базовые данные объекта памяти Python.В папке cpython на cython github нет упоминания о 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'

Обновление 1:

Не был уверен на 100%, как должна выглядеть структура трехмерного массива, поэтому я просто беру двумерный случай.Поскольку вы сказали, что не хотите вводить 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 голосов
/ 21 мая 2018

Похоже, что это решает проблему # 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 которая является константой.

...