基于数值的差异,NumPy在性能上有何不同?

8
我发现在Numpy中计算表达式时存在奇怪的性能差异。
我执行了以下代码:
import numpy as np
myarr = np.random.uniform(-1,1,[1100,1100])

然后。
%timeit np.exp( - 0.5 * (myarr / 0.001)**2 )
>> 184 ms ± 301 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

并且

%timeit np.exp( - 0.5 * (myarr / 0.1)**2 )
>> 12.3 ms ± 34.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

在第二种情况下,计算速度快了近15倍!请注意,唯一的区别是因子是0.1还是0.001。

这种行为的原因是什么?我能改变什么使得第一次计算和第二次一样快吗?


1
好的,在 Windows 上,NumPy 1.14.3、Python 3.6.0 下,我看到了 97.7ms 对比 47.7ms。 - jpp
3
在我的系统中,大(负)数的 exp 运算速度较慢:exp(-1) 的速度比 exp(-1000) 要快。这可能是由于 exp 算法在处理大数时收敛速度较慢导致的。 - Brenlla
1
合理的解释,但是不对。exp(1)仍然比exp(1000)快得多。 - Brenlla
2
我的第一个猜测(基于标题)是涉及到一些非规范化数字-请参见 https://dev59.com/F1oV5IYBdhLWcg3wCrEn ,我没有为特定的numpy / python设置深入验证,但它们可能会非常缓慢... - Marco13
2
@Marco13,实际上,exp(-708)是一个普通的浮点数,而exp(-709)是非规格化数,这就是我在Mac OS X上看到执行时间大幅增加的地方。直到约exp(-746)才会下溢为零。 - Warren Weckesser
显示剩余14条评论
2个回答

1

使用Intel SVML

我没有使用Intel SVML的numexpr,但是使用工作正常的SVML的numexpr应该和Numba表现一样好。在没有SVML的情况下,Numba基准测试显示出相同的行为,但是在有SVML的情况下表现更好。

代码

import numpy as np
import numba as nb

myarr = np.random.uniform(-1,1,[1100,1100])

@nb.njit(error_model="numpy",parallel=True)
def func(arr,div):
  return np.exp( - 0.5 * (myarr / div)**2 )

时间安排
#Core i7 4771
#Windows 7 x64
#Anaconda Python 3.5.5
#Numba 0.41 (compilation overhead excluded)
func(myarr,0.1)                      -> 3.6ms
func(myarr,0.001)                    -> 3.8ms

#Numba (set NUMBA_DISABLE_INTEL_SVML=1), parallel=True
func(myarr,0.1)                      -> 5.19ms
func(myarr,0.001)                    -> 12.0ms

#Numba (set NUMBA_DISABLE_INTEL_SVML=1), parallel=False
func(myarr,0.1)                      -> 16.7ms
func(myarr,0.001)                    -> 63.2ms

#Numpy (1.13.3), set OMP_NUM_THREADS=4
np.exp( - 0.5 * (myarr / 0.001)**2 ) -> 70.82ms
np.exp( - 0.5 * (myarr / 0.1)**2 )   -> 12.58ms

#Numpy (1.13.3), set OMP_NUM_THREADS=1
np.exp( - 0.5 * (myarr / 0.001)**2 ) -> 189.4ms
np.exp( - 0.5 * (myarr / 0.1)**2 )   -> 17.4ms

#Numexpr (2.6.8), no SVML, parallel
ne.evaluate("exp( - 0.5 * (myarr / 0.001)**2 )") ->17.2ms
ne.evaluate("exp( - 0.5 * (myarr / 0.1)**2 )")   ->4.38ms

#Numexpr (2.6.8), no SVML, single threaded
ne.evaluate("exp( - 0.5 * (myarr / 0.001)**2 )") ->50.85ms
ne.evaluate("exp( - 0.5 * (myarr / 0.1)**2 )")   ->13.9ms

1
这可能会产生非规范化数字,从而减缓计算速度。
您可以使用 daz 库禁用非规范化数字:
import daz
daz.set_daz()

更多信息:IA-32中的x87和SSE浮点辅助:Flush-To-Zero(FTZ)和Denormals-Are-Zero(DAZ)

为避免因非规格化数和下溢而导致的串行化和性能问题,使用SSE和SSE2指令在硬件内设置Flush-to-Zero和Denormals-Are-Zero模式,以实现浮点应用程序的最高性能。

请注意,在64位模式下,浮点计算使用SSE指令,而不是x87。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接