Ответ 1
Что такое память:
Когда вы пишете в функции:
cdef double[:] a
вы __Pyx_memviewslice
объект __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);
который сам расширяется до целых кучей вызовов C-api Python (так медленно). В конечном итоге это вызывает a
__getitem__
метод.
По сравнению с типизированными массивами numpy: на самом деле нет большой разницы. Если вы сделаете что-то вроде:
cdef np.ndarray[np.int32_t, ndim=1] new_arr
он работает практически так же, как memoryview, с доступом к исходным указателям, и скорость должна быть очень схожей.
Преимущество использования памяти - то, что вы можете использовать с ними более широкий диапазон типов массивов (например, стандартный библиотечный массив), поэтому вы более гибко относитесь к типам, с которыми вы можете звонить. Это соответствует общей идее Python о "утиной печати" - что ваш код должен работать с любым параметром, который ведет себя правильно (вместо проверки типа).
Второе (небольшое) преимущество заключается в том, что вам не нужны заголовки numpy для создания вашего модуля.
Третьим (возможно большим) преимуществом является то, что память может быть инициализирована без GIL, а cdef np.ndarray
не может (http://docs.cython.org/src/userguide/memoryviews.html#comparison-to-the-old -буфер-поддержка)
Небольшой недостаток памяти - это то, что они кажутся немного более медленными для настройки.
По сравнению с использованием malloc
ed int указателей:
Вы не получите каких-либо преимуществ по скорости (но вы тоже не получите слишком большую скорость). Небольшими преимуществами преобразования с использованием памяти являются:
-
Вы можете писать функции, которые могут использоваться либо с Python, либо внутри Cython:
cpdef do_something_useful(double[:] x): # can be called from Python with any array type or from Cython # with something that 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 также мог его использовать).