也许我做了一些奇怪的事情,但是在使用numpy时可能会发现令人惊讶的性能损失,无论使用的功率如何都显得一致。例如,当x是随机的100x100阵列
时x = numpy.power(x,3)
比
慢约60倍x = x*x*x
各种阵列尺寸的加速图显示了一个最佳尺寸10k左右的阵列和其他尺寸的5-10倍速度。
在自己的机器上测试下面的代码(有点乱):
import numpy as np
from matplotlib import pyplot as plt
from time import time
ratios = []
sizes = []
for n in np.logspace(1,3,20).astype(int):
a = np.random.randn(n,n)
inline_times = []
for i in range(100):
t = time()
b = a*a*a
inline_times.append(time()-t)
inline_time = np.mean(inline_times)
pow_times = []
for i in range(100):
t = time()
b = np.power(a,3)
pow_times.append(time()-t)
pow_time = np.mean(pow_times)
sizes.append(a.size)
ratios.append(pow_time/inline_time)
plt.plot(sizes,ratios)
plt.title('Performance of inline vs numpy.power')
plt.ylabel('Nx speed-up using inline')
plt.xlabel('Array size')
plt.xscale('log')
plt.show()
有人有解释吗?
答案 0 :(得分:18)
众所周知,处理器可以以非常快速的方式进行的双倍乘法非常非常快。 pow
明显变慢了。
Some performance guides甚至建议人们为此做好计划,甚至可能在某些方面有时可能有点过于热心。
numpy特殊情况平直以确保它不会太慢,但它会立即将立方体发送到您的libc的pow
,这几乎不是那么快作为几个乘法。
答案 1 :(得分:5)
我怀疑问题是np.power
总是浮点运算,并且它不知道如何在你的平台(或者可能是大多数/所有平台)上优化或矢量化,而乘法很容易投入SSE,即使你不这样做也很快。
即使np.power
足够智能分别进行整数取幂,除非将小值展开到重复乘法中,否则它仍然不会那么快。
通过比较int-to-int,int-to-float,float-to-int和float-to-float功率与小数组乘法的时间,你可以很容易地验证这一点。 int-to-int的速度是其他的5倍 - 但仍然比乘法慢4倍(尽管我使用PyPy测试了自定义的NumPy,所以对于那些在CPython上安装了正常NumPy的人来说,它可能会更好地给出真正的结果......)
答案 2 :(得分:5)
numpys幂函数的性能与指数非常非线性地成比例。用天真的方法来做到这一点。无论矩阵大小如何,都应存在相同类型的缩放。基本上,除非指数足够大,否则你不会看到任何实际的好处。
import matplotlib.pyplot as plt
import numpy as np
import functools
import time
def timeit(func):
@functools.wraps(func)
def newfunc(*args, **kwargs):
startTime = time.time()
res = func(*args, **kwargs)
elapsedTime = time.time() - startTime
return (res, elapsedTime)
return newfunc
@timeit
def naive_power(m, n):
m = np.asarray(m)
res = m.copy()
for i in xrange(1,n):
res *= m
return res
@timeit
def fast_power(m, n):
# elementwise power
return np.power(m, n)
m = np.random.random((100,100))
n = 400
rs1 = []
ts1 = []
ts2 = []
for i in xrange(1, n):
r1, t1 = naive_power(m, i)
ts1.append(t1)
for i in xrange(1, n):
r2, t2 = fast_power(m, i)
ts2.append(t2)
plt.plot(ts1, label='naive')
plt.plot(ts2, label='numpy')
plt.xlabel('exponent')
plt.ylabel('time')
plt.legend(loc='upper left')