Кажется, numpy.transpose только сохранить шаги, и на самом деле транспонировать лениво в соответствии с этим
Итак, когда действительно произошло перемещение данных и как двигаться? использовать много много memcpy
? или какой-то другой трюк?
Я следую по пути: array_reshape
, PyArray_Newshape
, PyArray_NewCopy
, PyArray_NewLikeArray
, PyArray_NewFromDescr
, PyArray_NewFromDescrAndBase
, PyArray_NewFromDescr_int
но не вижу ничего о перестановке оси. Когда это действительно произошло?
Трассировка через numpy
код C является медленным и трудоемким процессом. Я предпочитаю выводить модели поведения из времени.
Сделайте образец массива и его транспонируйте:
In [168]: A = np.random.rand(1000,1000)
In [169]: At = A.T
Сначала быстрый просмотр - без копирования буфера данных:
In [171]: timeit B = A.ravel()
262 ns ± 4.39 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
Быстрое копирование (предположительно использует некоторое быстрое копирование блочной памяти):
In [172]: timeit B = A.copy()
2.2 ms ± 26.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Медленное копирование (предположительно требует обхода источника в его пошаговом порядке, а цели - в своем собственном порядке):
In [173]: timeit B = A.copy(order='F')
6.29 ms ± 2.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Копирование At
без необходимости вносить изменения в порядок - быстро:
In [174]: timeit B = At.copy(order='F')
2.23 ms ± 51.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Как [173], но переходя от 'F' к 'C':
In [175]: timeit B = At.copy(order='C')
6.29 ms ± 4.16 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [176]: timeit B = At.ravel()
6.54 ms ± 214 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Копии с более простым пошаговым переупорядочением попадают где-то посередине:
In [177]: timeit B = A[::-1,::-1].copy()
3.75 ms ± 4.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [178]: timeit B = A[::-1].copy()
3.73 ms ± 6.48 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [179]: timeit B = At[::-1].copy(order='K')
3.98 ms ± 212 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
Этот astype
также требует более медленного копирования:
In [182]: timeit B = A.astype('float128')
6.7 ms ± 8.12 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
PyArray_NewFromDescr_int
описывается как Generic new array creation routine.
Хотя я не могу понять, куда он копирует данные из источника в цель, он явно проверяет order
strides
и dtype
. Предположительно он обрабатывает все случаи, когда требуется общая копия. Перестановка осей не является частным случаем.
Ответ на ваш вопрос: Numpy
не перемещает данные.
Вы видели PyArray_Transpose
в строке 688 ваших ссылок выше? В этой функции есть перестановка,
n = permute->len;
axes = permute->ptr;
...
for (i = 0; i < n; i++) {
int axis = axes[i];
...
permutation[i] = axis;
}
Любая форма массива - это чисто метаданные, используемые Numpy
для понимания того, как обрабатывать данные, поскольку память всегда хранится линейно и непрерывно. Поэтому нет причин перемещать или переупорядочивать какие-либо данные из документов здесь,
Другие операции, такие как транспонирование, не перемещают элементы данных в массиве, а изменяют информацию о форме и шагах, так что индексирование массива изменяется, но данные в не перемещаются. Обычно эти новые версии метаданных массива, но тот же буфер данных, являются новыми "представлениями" в буфере данных. Существует другой объект ndarray, но он использует тот же буфер данных. Вот почему необходимо принудительно создавать копии с помощью метода .copy(), если кто-то действительно хочет создать новую и независимую копию буфера данных.
Единственная причина для копирования может быть максимизировать эффективность кэширования, хотя Numpy
уже учитывает это,
Как выясняется, numpy достаточно умен при работе с ufuncs, чтобы определить, какой индекс является наиболее быстро меняющимся в памяти, и использует его для внутреннего цикла.
np.transpose
вызов не будет перемещать данные, но reshape
или copy
после будет перемещать данные на самом деле. так, где код перемещает данные, когда я вызываю reshape
?
alloc.c
, в строке 221 result = malloc(size);
( github.com/numpy/numpy/blob/master/numpy/core/src/multiarray/… ). Слово «перемещение» здесь немного странно, я предполагаю, что Numpy
выделит новый массив данных в np.copy
с помощью PyDataMem_NEW
а затем заполнит его скопированными значениями массива, прежде чем устанавливать / копировать метаданные формы перестановки / массива.