【问题标题】:Why is 2**100 so much faster than math.pow(2,100)?为什么 2**100 比 math.pow(2,100) 快这么多?
【发布时间】:2015-02-17 14:01:36
【问题描述】:

在讨论问题Exponentials in python x.**y vs math.pow(x, y)时, Alfe stated 没有充分的理由在 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")

输出:

0.329639911652
0.0361258983612
0.0364260673523
0.0363788604736

(ideone-shortcut)

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


[1]math.pow**的性能相差一个数量级。

编辑:

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

【问题讨论】:

  • 这似乎是您链接到的问题的欺骗。
  • 我相信您给出的链接中的答案之一给出了解释。其中一个分解每个案例,显示调用是如何呈现的。
  • @lurker 不。这不是重复的,请查看因子 9 而不是 1.2。
  • @user694733 我确定这不是一个重复,我在自己添加答案之前阅读了问题和答案stackoverflow.com/questions/20969773/…
  • 因为你有常量,我怀疑功率是在编译时计算的。检查拆卸。如果是这种情况,那么您需要像 print timeit.timeit("2.0 ** i", setup='i=100.0') 这样的东西来进行公平比较。

标签: python performance pow


【解决方案1】:

从根本上说,幂运算符在您的示例中表现如此出色的原因是 Python 在编译时很可能有 folded the constant

import dis
dis.dis('3.0 ** 100')    
i = 100
dis.dis('3.0 ** i')

这给出了以下输出:

  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

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

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

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

print timeit.timeit("3.0 ** i", setup='i=100')

我尝试在我的计算机上使用 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))

结果:

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

所以看起来转换为浮点数会占用相当多的执行时间。

我还为math.pow 添加了一个基准,注意这个函数与内置的pow 不同,更多信息请参见:Difference between the built-in pow() and math.pow() for floats, in Python?

【讨论】:

  • 一个非常好的解释。我终于尝试timeit.timeit("pass") 看到“光”;-)
  • @wolf,是的,这正是这里发生的事情,并解释了巨大的时间差异。
  • 嗯,pass 效率更高;-) -- 我尝试了一些方法来使比较公平ideone.com/WxO0S7
  • 所以答案是:它不像第一印象建议的那样那么快得多(这只是由于没有注意到折叠)。但问题的核心仍然存在:使用** 总是比使用math.pow 或使用pow 更快。在这些情况下,pow 和特别是 math.pow 是否有任何用途(当然,除非你需要对 pow 进行模计算)?
  • @Alfe 查看ideone.com/WxO0S7 在这里您会看到math.pow 对于浮点变量 更快。正如预期的那样。如果您知道更好的答案,我很乐意接受 it ;-)
猜你喜欢
  • 2014-05-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-25
  • 2014-04-11
  • 2018-02-05
  • 2018-09-25
相关资源
最近更新 更多