【问题标题】:Why does periodically pressing the enter key substantially speed up my code?为什么定期按回车键会大大加快我的代码速度?
【发布时间】:2014-02-14 00:07:11
【问题描述】:

我无意中遇到了一个让我有点困惑的现象。我正在使用 IDLE 进行一些快速测试,并且我有一些非常简单的代码(为了便于说明,我已对其进行了简化):

from time import clock  # I am presently using windows

def test_speedup():
    c = clock()
    for i in range(1000):
        print i,
    print '=>', clock() - c

现在我像这样运行这段代码(几次,基本结果相同):

# without pressing enter
>>> test_speedup()
0 1 2 3 4 . . . 997 998 999 => 12.8300956124  # the time to run code in seconds

# pressing enter ONLY 3 TIMES while the code ran
>>> test_speedup()
0 1 2 3 4 . . . 997 998 999 => 4.8656890089

# Pressing enter several times while the code ran
>>> test_speedup()
0 1 2 3 4 . . . 997 998 999 => 1.91522580283

我的第一个预感是,也许代码运行得更快,因为当我按下 Enter 时,输出系统可能不需要整理那么多的字符串(每次按下 Enter 时重新开始)。事实上,输出系统似乎总是在我按下回车后立即获得速度提升。

我也查看了documentation here,但我还是有点疑惑,为什么三个换行符会大大加快速度。

诚然,这个问题有些微不足道,除非想知道如何在不中止脚本的情况下加速 IDLE 中的输出系统。不过,我想了解这里的输出系统发生了什么。

(我使用的是 Python 2.7.x。)

【问题讨论】:

  • 附带说明,不要尝试使用time.clock() 来计时;使用timeit。对于像这样的粗粒度测试,存在明显的数量级差异,这没什么大不了的,但值得养成正确做事的习惯。
  • @abarnert 点。感谢您提供有用的建议。
  • 当我在 Mac 上运行它时,我实际上可以看到它随着线路变长而变慢。它逐渐变慢,还有两个点(大约 500 和 920)突然变慢了很多。这意味着这是长线问题,而不是阻塞输入问题……
  • 潜在问题是 tcl/tk 文本小部件针对屏幕大小的行进行了优化,而对于“长”行则减慢了速度。但它将处理至少 500,000 条短线(我测试过的最多)。 Windows 控制台不会因长行而窒息,但只会在环形缓冲区中保留相当少的有限行数。最大9999,默认300。打印第301行时,删除第1行,以此类推。

标签: python python-idle


【解决方案1】:

在幕后,IDLE 在 Tk 小部件之上模拟终端,我很确定它最终源自 Text

长线的存在会稍微减慢该小部件的速度。并且追加到长行比追加到短行花费更长的时间。如果您真的想了解为什么会发生这种情况,您需要查看 Tk Text 小部件底层的 Tcl 代码,Tkinter.Text 只是一个薄包装。

同时,IDLE 运行的 Tkinter 循环做了一些有趣的事情来允许它在不阻塞循环的情况下接受输入。当它认为没有其他事情发生时,它有时可能会阻塞一小段时间,直到它看到输入或 Tk 事件,所有这些短阻塞可能会加起来;按 Enter 可能只会取消其中的一些。

我实际上不确定这两者中哪一个更相关。您必须使用仅发送长行的程序与每隔(例如 10 个数字)插入换行符的程序对其进行测试,然后看看您通过这种方式获得了多少性能改进。

从我的 Mac 上的快速测试来看,原始程序明显逐渐变慢,并且在 500 和 920 附近也有两次缓慢的量子跃迁。因此,每 333 左右按一次进入会大大加快速度是有道理的——你可能会避免这两种量子减速。如果我将其更改为仅删除逗号,问题就会消失。

为每个数字打印一个换行符当然会导致不同减速,因为这会使终端足够长以需要滚动,增加回滚缓冲区等。我没有看到这个成本在 IDLE 中,但在 Windows 命令行上运行相同的操作,您会发现换行过多的问题与 IDLE 中换行过少的问题一样糟糕。最好的权衡可能应该来自“方形”数据,或者尽可能接近 80 列而不超过的数据。

【讨论】:

  • 啊,我明白了。我会查看 Tkinter.Text 以获得进一步的解释。我会接受这个答案(如果可以的话,我会赞成它,尽管我似乎还没有代表)(只是为了确保没有其他见解出现)。谢谢。
【解决方案2】:

我相信这与您用来执行代码的 IDE 有关。

我已经运行了您的代码(对 3.3 进行了语法更改)并且两次执行的时间相同:

999 => 8.09542283367021

edit:这是在 Python 3.3 的库存 IDLE 中执行的。我通过多次实验向回车键发送了垃圾邮件,并且见证了控件上的字符串输出与实验之间没有时间差。

凭直觉,我决定去掉一个打印功能,把它压缩成一行。:

print(i, "=>", clock()-c)

这产生了 999 => 7.141783124325343

在此基础上,我相信您所看到的时间差异是由于 IDE 占用了更多线程来处理您的代码,从而缩短了时间。显然,最后的时间是计算和打印 for 循环的总时间。

为了证实我的怀疑,我决定将所有内容放入一个元组列表中,然后打印该列表。

def new_speed_test():
    speed_list = []
    c = clock()
    for i in range(1000):
        speed_list.append((i, clock()-c))
    print(speed_list[-1])

哪个输出:(999, 0.0005668934240929957)

总结:我的实验证实了您的 IDE 是如何处理输出和 CPU 消耗的。

【讨论】:

  • 我很确定 OP 已经知道这一点。这个问题是关于 IDLE 的输出系统的,甚至承认“这个问题确实有些微不足道,除非人们想知道如何在不中止脚本的情况下加快 IDLE 中的输出系统。”
  • 我以为我在回答之前阅读了问题并正确理解了它;但是,在重新阅读后,我明白了您的意思。可以说,我唯一浪费时间的人是我自己,所以没有造成真正的损害(因为我的回答没有误导......在最坏的情况下,它只是浪费了 30 秒的阅读时间)。
  • 是的,它似乎是一个正确且至少有点有用的评论。毕竟,OP 只是 猜测 应该归咎于 IDLE 的输出系统,而您的回答有助于确认这一点。这就是为什么我只是添加了一条评论,而不是投反对票或举报之类的。
  • 我忘了在我原来的帖子中提到,即使发送了回车键我也没有改变输出时间,这就是我把帖子放在这里开始的全部原因(哎呀) .它将事件隔离到提问者使用的任何 IDE 作为 IDLE 标准与 Python 3.3 没有观察到的行为。
  • 我在两个使用 IDLE 的不同 Mac 上看到了相同的行为,python.org 3.3 和 Apple Tcl/Tk,python.org 3.3 和 ActiveState Tcl/Tk,以及定制的 3.4 和包含的 Tcl/ TK。 (clock 输出在非 Windows 上当然没用,但速度差异很明显。)
猜你喜欢
  • 2019-05-13
  • 1970-01-01
  • 1970-01-01
  • 2013-08-22
  • 2010-11-17
  • 1970-01-01
  • 2013-01-18
  • 2010-11-17
  • 2020-05-14
相关资源
最近更新 更多