为什么2**100比math.pow(2,100)快得多?

Wol*_*olf 7 python performance pow

当讨论python x.**y与math.pow(x,y)中的问题Exponentials时, Alfe表示在python中使用math.pow而不是内置**运算符是没有充分理由的.

timeit显示math.pow在所有情况下都比**慢.math.pow()究竟是什么好事?有人知道它可以有什么优势吗?

我们试图用一些timeit论点说服对方他到目前为止是赢家;-) - 至少以下timeit结果,似乎验证了这一点math.pow is slower than ** in all cases.

import timeit
print timeit.timeit("math.pow(2, 100)",setup='import math')
print timeit.timeit("2.0 ** 100.0")
print timeit.timeit("2 ** 100")
print timeit.timeit("2.01 ** 100.01")
Run Code Online (Sandbox Code Playgroud)

输出:

0.329639911652
0.0361258983612
0.0364260673523
0.0363788604736
Run Code Online (Sandbox Code Playgroud)

(ideone-shortcut)

对于我们观察到的差异[1],有一个简单的解释吗?


[1]表现math.pow**相差一个数量级.

编辑:

  • 文字参数而不是标题中的变量
  • 脚注明确指出差异的大小

shu*_*e87 8

从本质上讲,幂运算符看起来在你的例子中做得很好是因为Python很可能在编译时折叠了常量.

import dis
dis.dis('3.0 ** 100')    
i = 100
dis.dis('3.0 ** i')
Run Code Online (Sandbox Code Playgroud)

这给出了以下输出:

  1           0 LOAD_CONST               2 (5.153775207320113e+47)
              3 RETURN_VALUE
  1           0 LOAD_CONST               0 (3.0)
              3 LOAD_NAME                0 (i)
              6 BINARY_POWER
              7 RETURN_VALUE
Run Code Online (Sandbox Code Playgroud)

你可以在这里看到这个:http://ideone.com/5Ari8o

因此,在这种情况下,您可以看到它实际上没有对幂运算符的性能进行公平比较,math.pow因为结果已经预先计算然后缓存.当你进行3.0 ** 100没有执行计算时,结果就是返回.这可能比在运行时执行的任何取幂操作快得多.这最终解释了您的结果.

为了更公平的比较,您需要通过使用变量强制计算在运行时发生:

print timeit.timeit("3.0 ** i", setup='i=100')
Run Code Online (Sandbox Code Playgroud)

我尝试使用我的计算机上的python 3.4.1为此做一个快速的基准测试:

import timeit
trials = 1000000
print("Integer exponent:")
print("pow(2, 100)")
print(timeit.timeit(stmt="pow(2, 100)", number=trials))
print("math.pow(2, 100)")
print(timeit.timeit(stmt="m_pow(2, 100)", setup='import math; m_pow=math.pow', number=trials))
print("2 ** 100")
print(timeit.timeit(stmt="2 ** i", setup='i=100', number=trials))
print("2.0 ** 100")
print(timeit.timeit(stmt="2.0 ** i", setup='i=100', number=trials))
print("Float exponent:")
print("pow(2.0, 100.0)")
print(timeit.timeit(stmt="pow(2.0, 100.0)", number=trials))
print("math.pow(2, 100.0)")
print(timeit.timeit(stmt="m_pow(2, 100.0)", setup='import math; m_pow=math.pow', number=trials))
print("2.0 ** 100.0")
print(timeit.timeit(stmt="2.0 ** i", setup='i=100.0', number=trials))
print("2.01 ** 100.01")
print(timeit.timeit(stmt="2.01 ** i", setup='i=100.01', number=trials))
Run Code Online (Sandbox Code Playgroud)

结果:

Integer exponent:
pow(2, 100)
0.7596459520525322
math.pow(2, 100)
0.5203307256717318
2 ** 100
0.7334983742808263
2.0 ** 100
0.30665244505310607
Float exponent:
pow(2.0, 100.0)
0.26179656874310275
math.pow(2, 100.0)
0.34543158098034743
2.0 ** 100.0
0.1768205988074767
2.01 ** 100.01
0.18460920008178894
Run Code Online (Sandbox Code Playgroud)

所以看起来转换为float会占用相当多的执行时间.

我还添加了一个基准,math.pow注意这个函数与内置函数不同,pow请参阅更多内容:Python中内置的pow()和math.pow()之间的差异?

  • 好吧,`pass`效率更高;-) - 我尝试了一些比较公平的方法http://ideone.com/WxO0S7 (4认同)