Lug*_*ugi 7 optimization numpy addition absolute-value
我需要优化一个大量使用计算L1矢量规范的脚本.我们知道在这种情况下L1范数只是绝对值的总和.当计算numpy在这个任务中的速度有多快时,我发现了一些奇怪的东西:添加所有向量元素比获取向量的每个元素的绝对值快大约3倍.这是一个令人惊讶的结果,因为与采用绝对值相比,加法非常复杂,绝对值只需要对数据块的每第32位置零(假设为float32).
为什么这个加法比简单的按位运算快3倍?
import numpy as np
a = np.random.rand(10000000)
%timeit np.sum(a)
13.9 ms ± 87.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit np.abs(a)
41.2 ms ± 92.3 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
Run Code Online (Sandbox Code Playgroud)
小智 3
np.sum返回一个标量。np.abs返回一个相同大小的新数组。为新数组分配内存是这里花费最多时间的事情。比较
>>> timeit("np.abs(a)", "import numpy as np; a = np.random.rand(10000000)", number=100)
3.565487278989167
>>> timeit("np.abs(a, out=a)", "import numpy as np; a = np.random.rand(10000000)", number=100)
0.9392949139873963
Run Code Online (Sandbox Code Playgroud)
该参数out=a告诉 NumPy 将结果放入同一个数组中a,覆盖那里的旧数据。因此加速。
Sum 仍然稍微快一些:
>>> timeit("np.sum(a)", "import numpy as np; a = np.random.rand(10000000)", number=100)
0.6874654769926565
Run Code Online (Sandbox Code Playgroud)
但它不需要那么多的写内存访问。
如果您不想覆盖 a,abs则可以为 a 的输出提供另一个数组,假设您必须重复获取abs相同类型和大小的数组。
b = np.empty_like(a) # done once, outside the loop
np.abs(a, out=b)
np.sum(b)
Run Code Online (Sandbox Code Playgroud)
运行时间大约是np.linalg(a, 1)
作为参考,np.linalg将 L1 范数计算为
add.reduce(abs(x), axis=axis, keepdims=keepdims)
Run Code Online (Sandbox Code Playgroud)
这涉及到为新数组分配内存abs(x)。
理想情况下,有一种方法可以计算所有绝对值(或另一个“ufunc”的结果)的总和(或最大值或最小值),而无需将所有输出移至 RAM,然后检索其求和/最大值/最小值。NumPy 存储库中进行了一些讨论,最近一次是添加 max_abs ufunc,但尚未实现。
该ufunc.reduce方法适用于具有两个输入的函数,例如add或logaddexp,但没有可用于归约的 addabs函数 ( )。x, y : x+abs(y)