Как получить список просмотров памяти в Cython?

Моя функция принимает список массивов разного размера:

def function1(list list_of_numpy_arrays):

Прямо сейчас я делаю:

cdef int[:] a_view = list_of_numpy_arrays[index]

Проблема в том, что мне приходится индексировать список большое количество раз, поэтому накладные расходы значительно увеличивают время (в 10 раз). Я ищу что-то вроде cdef int[:] a[5], где у меня может быть массив представлений памяти, чтобы я мог избежать накладных расходов на индексирование списков python.

Я также могу передать список списков, если для этого есть решение.

def function2(list list_of_lists):


person Hyrial    schedule 06.07.2019    source источник
comment
Некоторые возможные вопросы для рассмотрения. 1) Существует ли фиксированное или максимальное количество массивов numpy, которые могут быть в списке? 2) Можно ли сделать так, чтобы все массивы numpy имели одинаковую форму, или это слишком большая трата памяти? 3) Должны ли массивы быть доступны как массивы numpy в python, или данные обрабатываются только на стороне cython после этого вызова function1? 4) Нужно ли вам использовать синтаксис [] для получения каждого массива numpy или достаточно вызова функции?   -  person CodeSurgeon    schedule 09.07.2019
comment
1) Я могу сделать максимальное число равным 20, но оно может быть и меньше. 2) Каждый массив будет вдвое меньше предыдущего. 3) Мне не нужно индексировать массивы в Python, но нужна ссылка на них, чтобы снова вызвать их в Cython. 4) Я не уверен, что вы имеете в виду, но мне нужно будет получить каждый массив numpy по индексу.   -  person Hyrial    schedule 09.07.2019
comment
Если у вас есть такие предсказуемые размеры массива (каждая половина размера предыдущего), я бы подумал об использовании большого массива 1d для всего этого и просто вычислении начальной точки в нем для каждого подмассива - это, вероятно, имело бы больше смысла чем мой ответ...   -  person DavidW    schedule 09.07.2019
comment
Правда, в этом больше смысла. Спасибо за предложение.   -  person Hyrial    schedule 10.07.2019


Ответы (1)


То, что вам нужно, на самом деле невозможно в Cython. Если вам нужно что-то, что работает хорошо, я бы, вероятно, создал структуру C, содержащую соответствующую информацию из представления памяти, а затем использовал ее вместо этого. Это не очень элегантное решение, но оно даст производительность, аналогичную использованию memoryviews; Я бы не рекомендовал делать это общим шаблоном, но если у вас есть разовая проблема, когда ваши данные требуются, тогда все в порядке.

cdef struct FakeMemoryView:
    int* data
    int stride
    int length

Если бы вы были готовы форсировать C смежных воспоминаний (int[::1]), вы могли бы отказаться от stride, так как было бы известно, что это один. Данные можно индексировать с помощью var.data[i*var.stride]. В начале вашей функции вы просматриваете свой список Python, чтобы создать массив этих FakeMemoryView, а затем с этого момента вы просто используете этот массив:

def function1(list list_of_numpy_arrays):
    assert len(list_of_numpy_arrays) == 5

    cdef FakeMemoryView new_list[5]

    # initialize the list
    cdef int[:] mview
    for i in range(5):
        mview = list_of_numpy_arrays[i]
        new_list[i].data = &mview[0]
        new_list[i].stride = mview.strides[0]
        new_list[i].length = mview.shape[0]

    # example access - zero the first lot of data
    for i in range(new_list[0].length):
        new_list[0].data[i*new_list[0].stride] = 0

Если вы не знаете длину списка заранее, вам нужно самостоятельно обрабатывать память для него с помощью malloc и free.

Это решение не обрабатывает подсчет ссылок на массивы Numpy, поэтому вы не должны разрешать освобождение массивов Numpy, удерживая FakeMemoryViews. Не храните свой массив более чем для одного вызова функции и не начинайте удалять массивы из входного списка.

person DavidW    schedule 09.07.2019