在NumPy中,x * x * x比x ** 3或甚至np.power(x,3)快一个数量级。
x = np.random.rand(1e6)
%timeit x**3
100 loops, best of 3: 7.07 ms per loop
%timeit x*x*x
10000 loops, best of 3: 163 µs per loop
%timeit np.power(x, 3)
100 loops, best of 3: 7.15 ms per loop
有关此行为发生原因的任何想法?据我所知,所有三个产生相同的输出(用np.allclose检查)。
答案 0 :(得分:31)
根据this answer,这是因为取幂的实现有一些开销,而乘法则没有。然而,随着指数的增加,天真的乘法会越来越慢。实证证明:
In [3]: x = np.random.rand(1e6)
In [15]: %timeit x**2
100 loops, best of 3: 11.9 ms per loop
In [16]: %timeit x*x
100 loops, best of 3: 12.7 ms per loop
In [17]: %timeit x**3
10 loops, best of 3: 132 ms per loop
In [18]: %timeit x*x*x
10 loops, best of 3: 27.2 ms per loop
In [19]: %timeit x**4
10 loops, best of 3: 132 ms per loop
In [20]: %timeit x*x*x*x
10 loops, best of 3: 42.4 ms per loop
In [21]: %timeit x**10
10 loops, best of 3: 132 ms per loop
In [22]: %timeit x*x*x*x*x*x*x*x*x*x
10 loops, best of 3: 137 ms per loop
In [24]: %timeit x**15
10 loops, best of 3: 132 ms per loop
In [25]: %timeit x*x*x*x*x*x*x*x*x*x*x*x*x*x*x
1 loops, best of 3: 212 ms per loop
注意取幂时间或多或少保持不变,除了我怀疑是特殊情况的x**2
情况,而乘法越来越慢。看来你可以利用它来获得更快的整数取幂...例如:
In [26]: %timeit x**16
10 loops, best of 3: 132 ms per loop
In [27]: %timeit x*x*x*x*x*x*x*x*x*x*x*x*x*x*x*x
1 loops, best of 3: 225 ms per loop
In [28]: def tosixteenth(x):
....: x2 = x*x
....: x4 = x2*x2
....: x8 = x4*x4
....: x16 = x8*x8
....: return x16
....:
In [29]: %timeit tosixteenth(x)
10 loops, best of 3: 49.5 ms per loop
似乎你可以通过将任何整数分成两个幂的总和,如上所述计算每个2的幂,并求和来一般地应用这种技术:
In [93]: %paste
def smartintexp(x, exp):
result = np.ones(len(x))
curexp = np.array(x)
while True:
if exp%2 == 1:
result *= curexp
exp >>= 1
if not exp: break
curexp *= curexp
return result
## -- End pasted text --
In [94]: x
Out[94]:
array([ 0.0163407 , 0.57694587, 0.47336487, ..., 0.70255032,
0.62043303, 0.0796748 ])
In [99]: x**21
Out[99]:
array([ 3.01080670e-38, 9.63466181e-06, 1.51048544e-07, ...,
6.02873388e-04, 4.43193256e-05, 8.46721060e-24])
In [100]: smartintexp(x, 21)
Out[100]:
array([ 3.01080670e-38, 9.63466181e-06, 1.51048544e-07, ...,
6.02873388e-04, 4.43193256e-05, 8.46721060e-24])
In [101]: %timeit x**21
10 loops, best of 3: 132 ms per loop
In [102]: %timeit smartintexp(x, 21)
10 loops, best of 3: 70.7 ms per loop
对于两个小的偶数幂来说速度很快:
In [106]: %timeit x**32
10 loops, best of 3: 131 ms per loop
In [107]: %timeit smartintexp(x, 32)
10 loops, best of 3: 57.4 ms per loop
但随着指数变大,速度会变慢:
In [97]: %timeit x**63
10 loops, best of 3: 133 ms per loop
In [98]: %timeit smartintexp(x, 63)
10 loops, best of 3: 110 ms per loop
对于大型最坏情况并不快:
In [115]: %timeit x**511
10 loops, best of 3: 135 ms per loop
In [114]: %timeit smartintexp(x, 511)
10 loops, best of 3: 192 ms per loop
答案 1 :(得分:7)
如果您正在计算权力并担心速度,请注意:
x = np.random.rand(5e7)
%timeit x*x*x
1 loops, best of 3: 522 ms per loop
%timeit np.einsum('i,i,i->i',x,x,x)
1 loops, best of 3: 288 ms per loop
为什么einsum更快仍然是mine的开放性问题。虽然由于einsum
能够使用SSE2,而numpy的ufuncs直到1.8才会使用它。
到位更快:
def calc_power(arr):
for x in xrange(arr.shape[0]):
arr[x]=arr[x]*arr[x]*arr[x]
numba_power = autojit(calc_power)
%timeit numba_power(x)
10 loops, best of 3: 51.5 ms per loop
%timeit np.einsum('i,i,i->i',x,x,x,out=x)
10 loops, best of 3: 111 ms per loop
%timeit np.power(x,3,out=x)
1 loops, best of 3: 609 ms per loop
答案 2 :(得分:3)
我希望这是因为x**y
必须处理x
和y
都是浮点数的一般情况。在数学上我们可以写x**y = exp(y*log(x))
。按照你的例子我找到了
x = np.random.rand(1e6)
%timeit x**3
10 loops, best of 3: 178 ms per loop
%timeit np.exp(3*np.log(x))
10 loops, best of 3: 176 ms per loop
我没有检查过实际的numpy代码,但它必须在内部执行类似的操作。
答案 3 :(得分:1)
这是因为python中的幂是作为float操作执行的(对于numpy也是如此,因为它使用C)。
在C中,pow function提供了3种方法:
双音(双x,双y)
长powl(长双x,长双y)
浮动powf(浮动x,浮动y)
每个都是浮点运算。
答案 4 :(得分:0)
根据spec:
两个参数形式pow(x,y)相当于使用幂 operator:x ** y。
参数必须具有数字类型。对于混合操作数类型, 适用于二进制算术运算符的强制规则。
换句话说:由于x
是浮点数,因此指数从int转换为float,并执行通用浮点运算操作。在内部,这通常被重写为:
x**y = 2**(y*lg(x))
2**a
和lg a
(a
的基数2对数)是现代处理器上的单个指令,但它仍然需要比几次乘法更长的时间。
答案 5 :(得分:-1)
timeit np.multiply(np.multiply(x,x),x)
与x*x*x
相同的次数。我的猜测是np.multiply
正在使用像BLAS这样的快速Fortran线性代数包。我从另一个问题得知numpy.dot
在某些情况下使用BLAS。
我必须把它拿走。 np.dot(x,x)
比np.sum(x*x)
快3倍。因此np.multiply
的速度优势与使用BLAS不一致。
我的numpy(时间因机器和可用库而异)
np.power(x,3.1)
np.exp(3.1*np.log(x))
大概在同一时间,但
np.power(x,3)
快2倍。不如x*x*x
快,但仍然比一般力量快。所以它正在利用整数幂。