为什么x ** 3比x * x * x慢?

时间:2013-08-26 22:01:58

标签: python numpy

在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检查)。

6 个答案:

答案 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必须处理xy都是浮点数的一般情况。在数学上我们可以写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**alg aa的基数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快,但仍然比一般力量快。所以它正在利用整数幂。