Что такое memoryview:
Когда вы пишете в функции:
cdef double[:] a
вы в конечном итоге с объектом __Pyx_memviewslice
:
typedef struct {
struct __pyx_memoryview_obj *memview;
char *data;
Py_ssize_t shape[8];
Py_ssize_t strides[8];
Py_ssize_t suboffsets[8];
} __Pyx_memviewslice;
memoryview содержит C-указатель некоторых данных, которые он (обычно) не имеет непосредственно. Он также содержит указатель на базовый объект Python (struct __pyx_memoryview_obj *memview;
). Если данные принадлежат объекту Python, то memview
содержит ссылку на это и гарантирует, что объект Python, который хранит данные, сохраняется в живых, пока вокруг памяти.
Комбинация указателя на необработанные данные, а также информация о том, как индексироваться (shape
, strides
и suboffsets
) позволяет Cython делать индексацию с использованием сырых указателей данных и некоторой простая математики C (который является очень эффективным) , например:
x=a[0]
дает что-то вроде:
(*((double *) (/* dim=0 */ (__pyx_v_a.data + __pyx_t_2 * __pyx_v_a.strides[0]))));
В противоположность этому, если вы работаете с нетипизированных объектов и написать что-то вроде:
a = np.array([1,2,3]) # note no typedef
x = x[0]
индексация осуществляется как:
__Pyx_GetItemInt(__pyx_v_a, 0, long, 1, __Pyx_PyInt_From_long, 0, 0, 1);
который сам расширяется s на целую кучу вызовов C-api Python (так медленно). В конечном счете он вызывает метод a
__getitem__
.
По сравнению с типизированных Numpy массивов: там действительно не огромная разница. Если вы что-то вроде:
cdef np.ndarray[np.int32_t, ndim=1] new_arr
он работает практически очень похож на memoryview, с доступом к сырьевым указателям и скорость должна быть очень похожа.
Преимущество использования памяти в том, что с ними можно использовать более широкий диапазон типов массивов (например, standard library array), поэтому вы более гибко относитесь к типам, с которыми могут быть вызваны ваши функции. Это соответствует общей идее Python о «утиной печати» - что ваш код должен работать с любым параметром, который ведет себя правильно (вместо проверки типа).
Второе (небольшое) преимущество в том, что вам не нужны заголовки numpy для создания вашего модуля.
Третье (возможно, больше) преимущества заключается в том, что memoryviews может быть инициализирован без GIL, пока cdef np.ndarray
s не могу (http://docs.cython.org/src/userguide/memoryviews.html#comparison-to-the-old-buffer-support)
Незначительный недостаток memoryviews является то, что они, кажется, немного медленнее, чтобы настроить.
По сравнению с использованием только malloc
ред Int указателей:
Вы не получите никакого преимущества в скорости (но ни вы получите слишком много потери скорости). Незначительные преимущества преобразования с использованием memoryview являются:
Вы можете написать функции, которые могут быть использованы либо с Python или находящиеся внутри Cython:
cpdef do_something_useful(double[:] x):
# can be called from Python with any array type or from Cython
# with something that's already a memoryview
....
Вы можете позволить Cython обрабатывать освобождение памяти для этого типа массива, который может упростить вашу жизнь для вещей, которые имеют неизвестную жизнь. См. http://docs.cython.org/src/userguide/memoryviews.html#cython-arrays и особенно .callback_free_data
.
Вы можете передать свои данные обратно в код python python (он получит базовое поле __pyx_memoryview_obj
или что-то подобное). Будьте очень осторожны в управлении памятью здесь (см. Пункт 2!).
Другая вещь, которую вы можете сделать, это обработать такие вещи, как 2D-массивы, определенные как указатель на указатель (например, double**
). См. http://docs.cython.org/src/userguide/memoryviews.html#specifying-more-general-memory-layouts. Обычно мне не нравится этот тип массива, но если у вас уже есть C-код, который уже используется, то вы можете с ним взаимодействовать (и передать его обратно на Python, чтобы ваш код Python также мог его использовать).
Первая строка документации «Типизированные представления памяти позволяют эффективно использовать буферы памяти, такие как базовые массивы NumPy, без каких-либо накладных расходов Python. 'Мне кажется, что' memoryview' - это просто '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' 'для доступа к буферу, минуя функции 'numpy'. Это не будет быстрее, чем прямой доступ к стилю 'c', но может быть проще в использовании. – hpaulj