使用 numpy 数组进行代码优化。有更好的解决方案吗?
Posted
技术标签:
【中文标题】使用 numpy 数组进行代码优化。有更好的解决方案吗?【英文标题】:Code optimisation using numpy array. Is there a better solution? 【发布时间】:2022-01-16 12:49:06 【问题描述】:我正在尝试优化一个计算函数:a*exp(b*x)+c
我使用 numpy 数组测试了三种方法:
def model(a,b,c,x):
return a*np.exp(b*x)+c
def myFoo1(modelParam,x):
return([model(*i,x) for i in modelParam])
def myFoo2(modelParam,x):
return([i[0]*np.exp(i[1]*x)+i[2] for i in modelParam])
def myFoo3(modelParam,x):
return(np.exp(np.outer(modelParam[:,1],x))*params[:,0][:,None]+params[:,2][:,None])
当测量运行时间时:
x=np.array(np.arange(0,100,0.1))
params=np.array([[10,0.1,2],[20,0.3,4],[30,0.2,6],[15,0.2,4],[16,0.5,7]])
%time myFoo1(params,x)
%time myFoo2(params,x)
%time myFoo3(params,x)
输出是:
CPU times: user 3.58 ms, sys: 0 ns, total: 3.58 ms
Wall time: 2 ms
CPU times: user 855 µs, sys: 0 ns, total: 855 µs
Wall time: 703 µs
CPU times: user 690 µs, sys: 0 ns, total: 690 µs
Wall time: 564 µs
第一个是我的原始代码,因为它最容易编程。但是,第三个要快 4 倍。我可以再改进一下吗?
并使用 %timeit(按照评论中的建议编辑问题):
211 µs ± 663 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
199 µs ± 199 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)
164 µs ± 56.1 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
【问题讨论】:
能否请您展示一个modelParam
的小样本?
@user17242583。它是 np.array params
。 a、b、c 参数有五个示例。
我的意思是,你能展示一下它的内容样本吗?如果我看到它是什么,那么试验它会更容易。或者也许只是它和它包含的数字范围?
@usrer17242588。我正在尝试优化对为许多 (a,b,c) 参数集返回 a.exp(b.x)+c 的函数的调用。在此示例中,x 输入始终是从 0 到 100 的相同范围。
好的,谢谢。请把print(params.shape)
的结果发过来好吗?
【参考方案1】:
通过使用略有不同的广播方式,我得到了一些小改进
def myFoo4(modelParam,x):
return modelParam[:, 0:1] * np.exp(modelParam[:, 1:2] * x) + modelParam[:, 2:3]
另一个小改进是切换到np.float32
x_float32 = np.array(np.arange(0, 100, 0.1), dtype=np.float32)
params_float32 = np.array([[10, 0.1, 2], [20, 0.3, 4], [30, 0.2, 6], [15, 0.2, 4], [16, 0.5, 7]],
dtype=np.float32)
41 100 17042.0 170.4 19.7 myFoo3(params, x)
42 100 15282.0 152.8 17.6 myFoo4(params, x)
43 100 11322.0 113.2 13.1 myFoo4(params_float32, x_float32)
【讨论】:
看来np.exp
调用是我机器上计算的瓶颈,因为它占用了 80% 的时间。所以我怀疑是否有更快的解决方案(除非 OP 接受使用近似值)。仅供参考:np.exp
函数尚未在大多数机器上使用 SIMD 指令。 AFAIK,只有支持 AVX-512 的处理器(例如 Intel Icelake 处理器)才真正使用 SIMD 指令进行此操作,从而实现了巨大的加速(根据 Intel 大约是 x10)。
@JérômeRichard。你是如何描述它的?使用 %prun ?我无法做到这一点。
@JérômeRichard 你说得对,在我的机器上np.exp
也是瓶颈。谢谢你关于 SIMD 和 np.exp 的信息,不知道。至于更快的解决方案 - 我虽然可能一些并行性(numba/multiprocessing)可以加快速度。
@dankal444 如果有足够的数据需要计算,但当前输入太小而无法使用线程(创建线程的时间可能会大大增加),那么多处理可能是一个好主意。尽管如此,OP 实际上可能需要计算更大的输入,所以它可能是值得的。
@Stef1611 好的。谢谢你。性能差距的部分原因是我的机器上的频率高出 2 倍,而且该处理器似乎不支持指数计算通常使用的融合乘加指令。加快这一速度的一种方法是使用英特尔版本的 Anaconda 与 SVML 库一起可能对 AMD 的 Opterons 调用 np.exp
进行矢量化(但我不确定它是否免费)。否则,使用更大输入的更多内核或使用近似值似乎是加快速度的唯一方法。以上是关于使用 numpy 数组进行代码优化。有更好的解决方案吗?的主要内容,如果未能解决你的问题,请参考以下文章