我在比较numpy数组就地操作和常规操作。
下面是我所做的(python version 3.7.3):
a1, a2 = np.random.random((10,10)), np.random.random((10,10))
为了进行比较:
def func1(a1, a2):
a1 = a1 + a2
def func2(a1, a2):
a1 += a2
%timeit func1(a1, a2)
%timeit func2(a1, a2)
因为就地操作避免了每个循环的内存分配。我原以为
func1
比func2
慢。但是我得到了这个:
In [10]: %timeit func1(a1, a2)
595 ns ± 14.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [11]: %timeit func2(a1, a2)
1.38 µs ± 7.87 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [12]: np.__version__
Out[12]: '1.16.2'
这表明
func1
仅为func2
所花费的时间的1/2。有人能解释一下为什么会这样吗?
最佳答案
因为您忽略了向量化操作和小矩阵预取的影响。
注意矩阵的大小(10×10)很小,所以分配临时存储所需的时间不是那么重要,但对于具有大缓存大小的处理器来说,这些小矩阵可能仍然完全适合于L1缓存,因此,对这些小矩阵执行自举操作等的速度增益,将弥补分配临时矩阵的时间损失和将直接添加到分配内存位置之一的速度增益。
但是当你增加矩阵的大小时,情况就不同了
In [41]: k = 100
In [42]: a1, a2 = np.random.random((k, k)), np.random.random((k, k))
In [43]: %timeit func2(a1, a2)
4.41 µs ± 3.01 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [44]: %timeit func1(a1, a2)
6.36 µs ± 4.18 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [45]: k = 1000
In [46]: a1, a2 = np.random.random((k, k)), np.random.random((k, k))
In [47]: %timeit func2(a1, a2)
1.13 ms ± 1.49 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [48]: %timeit func1(a1, a2)
1.59 ms ± 2.06 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [49]: k = 5000
In [50]: a1, a2 = np.random.random((k, k)), np.random.random((k, k))
In [51]: %timeit func2(a1, a2)
30.3 ms ± 122 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [52]: %timeit func1(a1, a2)
94.4 ms ± 58.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
编辑:这是为了让
k = 10
显示您对小矩阵的观察在我的机器上也是正确的。In [56]: k = 10
In [57]: a1, a2 = np.random.random((k, k)), np.random.random((k, k))
In [58]: %timeit func2(a1, a2)
1.06 µs ± 10.7 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
In [59]: %timeit func1(a1, a2)
500 ns ± 0.149 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)