为什么 ufunc 在一个轴上比另一个轴快 2 倍?

问题描述 投票:0回答:3

我测量了 ufunc 在不同轴上的性能,例如

np.cumsum

In [51]: arr = np.arange(int(1E6)).reshape(int(1E3), -1)

In [52]: %timeit arr.cumsum(axis=1)
2.27 ms ± 10.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [53]: %timeit arr.cumsum(axis=0)
4.16 ms ± 10.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

cumsum
轴 1 上的速度几乎比轴 0 上快 2 倍。幕后发生了什么?

python performance numpy numpy-ufunc numpy-ndarray
3个回答
9
投票

你有一个方阵。看起来像这样:

1 2 3
4 5 6
7 8 9

但是计算机内存是线性寻址的,所以对于计算机来说它看起来像这样:

1 2 3 4 5 6 7 8 9

或者,如果你仔细想想,它可能看起来像这样:

1 4 7 2 5 8 3 6 9

如果您尝试对

[1 2 3]
[4 5 6]
(一行)求和,第一个布局会更快。如果您尝试对
[1 4 7]
[2 5 8]
求和,则第二种布局更快。

发生这种情况是因为从内存加载数据一次发生一个“缓存行”,通常为 64 字节(NumPy 的默认 dtype 为 8 字节浮点数的 8 个值)。

您可以使用

order
参数控制 NumPy 在构造数组时使用的布局。

有关更多信息,请参阅:https://en.wikipedia.org/wiki/Row-_and_column-major_order


7
投票

数组是行优先。因此,当您对轴 1 求和时, 这些数字可以在连续的内存阵列中找到。这可以实现更好的缓存性能,从而加快内存访问速度(参见“引用位置”)。我想这就是您在这里看到的效果。


1
投票

确实,性能将取决于内存中数组的顺序:

In [36]: arr = np.arange(int(1E6)).reshape(int(1E3), -1)

In [37]: arrf = np.asfortranarray(arr) # change order

In [38]: %timeit arr.cumsum(axis=1)
1.99 ms ± 32.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [39]: %timeit arr.cumsum(axis=0)
14.6 ms ± 229 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [41]: %timeit arrf.cumsum(axis=0)
1.96 ms ± 19.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [42]: %timeit arrf.cumsum(axis=1)
14.6 ms ± 148 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

有关更多详细信息,请参阅 https://docs.scipy.org/doc/numpy-1.13.0/reference/internals.html#multiDimension-array-indexing-order-issues

© www.soinside.com 2019 - 2024. All rights reserved.