似乎numpy.transpose仅保存步幅,并且实际上确实根据this进行了变位
那么,什么时候实际发生数据移动以及如何移动?使用许多memcpy
吗?或其他技巧?
我遵循的路径:
array_reshape
,
PyArray_Newshape
,
PyArray_NewCopy
,
PyArray_NewLikeArray
,
PyArray_NewFromDescr
,
PyArray_NewFromDescrAndBase
,
PyArray_NewFromDescr_int
但对轴置换一无所知。确实何时发生?
答案 0 :(得分:0)
您的问题的答案是:Numpy
不会移动数据。
您在上述链接的第688行上看到PyArray_Transpose
吗?此功能有一个替代品,
n = permute->len;
axes = permute->ptr;
...
for (i = 0; i < n; i++) {
int axis = axes[i];
...
permutation[i] = axis;
}
任何数组形状都是纯粹的元数据,Numpy
使用它来理解如何处理数据,因为内存始终是线性且连续存储的。因此,没有理由从文档here
其他操作(例如转置)不会移动数据元素 在数组中四处移动,而是更改有关形状和步幅的信息,以使数组的索引发生变化,但是中的数据不会移动。 通常,这些新版本的数组元数据和相同的数据缓冲区是 新的“视图”进入数据缓冲区。有一个不同的ndarray对象,但是它 使用相同的数据缓冲区。这就是为什么有必要强制复印通过 如果真的想制作一个新的独立文件,请使用.copy()方法 数据缓冲区的副本。
复制的唯一原因可能是最大化cache efficiency,尽管Numpy
已经considers,
事实证明,numpy在处理ufunc时足够聪明,可以确定哪个索引是内存中变化最快的索引并将其用于最内层循环。
答案 1 :(得分:0)
通过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
。大概可以处理需要通用副本的所有情况。轴排列不是特殊情况。