【问题标题】:Why does IDLE 3.4 take so long on this program?为什么 IDLE 3.4 在这个程序上花了这么长时间?
【发布时间】:2014-06-14 04:47:20
【问题描述】:

编辑:我正在完全重做这个问题。该问题与 time.time() 无关

这是一个程序:

import time
start=time.time()
a=9<<(1<<26)             # The line that makes it take a while
print(time.time()-start)

这个程序,当保存为文件并在 Python 3.4 中以 IDLE 运行时,大约需要 10 秒,即使从time.time() 打印出 0.0 也是如此。 IDLE 的问题非常明显,因为从命令行运行该程序几乎不需要任何时间。

senshin 发现的另一个具有相同效果的程序是:

def f(): 
    a = 9<<(1<<26)

我已经确认,当在 Python 2.7 IDLE 中或从 python 2.7 或 3.4 的命令行运行时,这个相同的程序几乎是瞬时的。

那么 Python 3.4 IDLE 做了什么让它花这么长时间?我知道计算这个数字并将其保存到内存中是磁盘密集型的,但我想知道的是为什么 Python 3.4 IDLE 执行此计算并编写,而 Python 2.7 IDLE 和命令行 Python 大概不这样做。

【问题讨论】:

  • 对我来说不需要 10 秒,事实上它对我来说就像预期的那样工作
  • @isaacg 为什么您认为该程序需要大约 10 秒?像这样的简单程序几乎不需要运行时间——例如,在我的 2.7GHz mac mini 上,“time python isaacg.py”报告它需要大约 0.028 秒才能运行。
  • 你试过从终端运行这个吗?结果还是一样吗?
  • 您可能希望多次运行并取平均值。
  • 在文件中自行尝试并从 IDLE 运行:def f(): a = 9&lt;&lt;(1&lt;&lt;26)。它使我的 IDLE 滞后(即使没有调用该函数);它为你做吗?如果是这样,它可能与 IDLE 预计算常量或类似的奇怪东西有关。

标签: python python-idle python-3.4


【解决方案1】:

我会查看那条线并将其分开。你有:

9 &lt;&lt; (1 &lt;&lt; 26)

(1 &lt;&lt; 26) 是第一个计算的表达式,它产生一个非常大的数字。这条线的意思是,您要将数字 1 乘以 2 的 26 次方,从而有效地在内存中生成数字 2 ** 26。然而,这不是问题。然后,您将 9 左移 2 ** 26 的计数。这会在内存中产生一个大约 5000 万位长的数字(我什至无法准确计算!),因为左移太大了。未来要小心,因为看似少量的变化实际上增长非常快。如果它更大,您的程序可能根本没有运行。如果您好奇的话,您的表达式在数学上的计算结果为 9 * 2 ** (2 ** 26)

评论部分的含糊之处可能实际上是在处理这个巨大的内存部分是如何由 python 在后台处理的,而不是 IDLE。

编辑 1:

我认为正在发生的事情是,即使将数学表达式放在尚未调用的函数内部,只有当表达式是自给自足的时,它才会计算出它的答案。这意味着如果等式中使用了变量,则等式将在字节码中保持不变,并且在硬执行之前不会被评估。该函数必须被解释,在这个过程中,我认为你的值实际上是计算出来的,导致时间变慢。我不确定这一点,但我强烈怀疑这种行为是根本原因。即使不是这样,你也得承认9&lt;&lt;(1&lt;&lt;26) 把电脑踢到后面了,那里没有太多可以做的优化。

In[73]: def create_number():
            return 9<<(1<<26)
In[74]: #Note that this seems instantaneous, but try calling the function!
In[75]: %timeit create_number()
#Python environment crashes because task is too hard

然而,这种测试存在轻微的欺骗性。使用常规时间尝试此操作时,我得到:

In[3]: from timeit import timeit
In[4]: timeit(setup = 'from __main__ import create_number', stmt = 'create_number()', number = 1)
Out[4]: .004942887388800443

另外请记住,打印值是不可行的,所以类似:

In[102]: 9&lt;&lt;(1&lt;&lt;26)

甚至不应该尝试。

更多支持:

我觉得自己像个叛逆者,所以我决定看看如果我对等式的原始执行进行计时会发生什么:

In[107]: %timeit 9<<(1<<26)
10000000 loops, best of 3: 22.8 ns per loop

In[108]: def empty(): pass
In[109]: %timeit empty()
10000000 loops, best of 3: 96.3 ns per loop

这真的很可疑,因为显然这种计算发生的速度比 Python 调用空函数的时间要快,显然情况并非如此。我再说一遍,这不是瞬时的,但可能与检索内存中某处已计算的对象并重用该值来计算表达式有关。无论如何,好问题。

【讨论】:

  • 对不起,但我认为这不是一个完整的答案。它没有解释为什么当我使用 Python 3.4 IDLE 而不是 Python 2.7 IDLE 或命令行时会出现这种观察到的时间延迟。我将改写我的问题以使其更清楚。
  • 命令行中的表达式是什么样的?您是否考虑过在不同的环境中可能会发生隐蔽的内存引用问题?
【解决方案2】:

我真的很困惑。以下是 3.4.1 的更多结果。在 3.4.1 或 3.3.5 的编辑器中运行前两行中的任何一行都会产生相同的对比度。

>>> a = 1 << 26; b = 9 << a  # fast, , .5 sec
>>> c = 9 << (1 << 26)  # slow, about 3 sec
>>> b == c  # fast
True
>>> exec('d=9<<(1<<26)', globals())  # fast
>>> c == d  # fast
True

正常执行和空闲执行的区别在于,空闲执行程序的代码在类似上面的 exec 调用中,除了传递给 exec 的 'globals' 不是 globals() 而是配置为看起来像 globals() 的 dict。除了 exec 从语句到函数的变化之外,我不知道 Idle 在这方面有任何 2.7 - 3.4 差异。执行一个 exec 怎么能比单个 exec 快?添加中间绑定如何更快?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-18
    • 2017-11-15
    • 1970-01-01
    • 2011-08-27
    相关资源
    最近更新 更多