5

我正在做一些计算,并测量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 倍cumsum为什么会这样,幕后发生了什么?很高兴能清楚地了解其背后的原因。谢谢!


更新:经过一番研究,我意识到如果有人正在构建一个应用程序,他们总是只对某个轴求和,那么数组应该以适当的顺序初始化:即轴 = 1 总和的C 顺序或Fortran 顺序对于axis = 0 sums,以节省CPU时间。

另外:这个关于连续数组和非连续数组之间差异的优秀答案有很大帮助!

4

3 回答 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]or [2 5 8],则第二种布局更快。

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

您可以使用参数来控制 NumPy 在构造数组时使用的布局order

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

于 2018-01-27T02:00:48.040 回答
7

数组是row-major。因此,当您在轴 1 上求和时,可以在连续的内存数组中找到这些数字。这允许更好的缓存性能,因此更快的内存访问(参见“引用位置”)。我认为这就是您在这里看到的效果。

于 2018-01-27T01:58:06.013 回答
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#multidimensional-array-indexing-order-issues

于 2018-01-27T02:04:48.117 回答