根据this的说法,numpy.transpose似乎只节省了步幅,实际上是懒惰地进行转置
那么,数据移动实际上是什么时候发生的,如何移动呢?用多多memcpy?或者其他的把戏?
我遵循以下路径:array_reshape、PyArray_Newshape、PyArray_NewCopy、PyArray_NewLikeArray、PyArray_NewFromDescr、PyArray_NewFromDescrAndBase、PyArray_NewFromDescr_int,但对轴置换一无所知。它到底是什么时候发生的?
更新2021/1/19
感谢您的回答,numpy数组拷贝转置算法是here,它使用一个通用的macro来实现,该算法非常本地,并且它没有考虑任何simd加速和缓存友好性
发布于 2019-01-05 01:12:03
跟踪整个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。据推测,它可以处理所有需要通用副本的情况。轴排列并不是特例。
发布于 2019-01-04 21:49:04
你的问题的答案是: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了这一点,
事实证明,
在处理ufuncs时足够聪明,可以确定哪个索引在内存中变化最快,并将其用于最内层的循环。
https://stackoverflow.com/questions/54039225
复制相似问题