【发布时间】:2011-04-20 21:36:13
【问题描述】:
我一直对使用 print 语句简单地输出到终端需要多长时间感到惊讶/沮丧。在最近一些令人痛苦的缓慢记录之后,我决定研究它,并惊讶地发现几乎所有所花费的时间都在等待终端处理结果。
可以以某种方式加快写入标准输出的速度吗?
我写了一个脚本(这个问题底部的'print_timer.py')来比较将 100k 行写入标准输出、文件以及将标准输出重定向到/dev/null 时的时间。以下是计时结果:
$ python print_timer.py
this is a test
this is a test
<snipped 99997 lines>
this is a test
-----
timing summary (100k lines each)
-----
print :11.950 s
write to file (+ fsync) : 0.122 s
print with stdout = /dev/null : 0.050 s
哇。为了确保 python 没有在幕后做一些事情,比如认识到我将 stdout 重新分配给 /dev/null 之类的,我在脚本之外进行了重定向......
$ python print_timer.py > /dev/null
-----
timing summary (100k lines each)
-----
print : 0.053 s
write to file (+fsync) : 0.108 s
print with stdout = /dev/null : 0.045 s
所以这不是 python 技巧,它只是终端。我一直都知道将输出转储到 /dev/null 会加快速度,但从未想过它有那么重要!
tty 的速度让我吃惊。为什么写入物理磁盘比写入“屏幕”(可能是全 RAM 操作)快得多,并且实际上与使用 /dev/null 转储到垃圾中一样快?
This link 谈论终端将如何阻止 I/O,以便它可以“解析 [输入]、更新其帧缓冲区、与 X 服务器通信以滚动窗口等等” ...但我不完全明白。什么可以花这么长时间?
我预计没有出路(缺少更快的 tty 实现?)但我还是会问。
更新:在阅读了一些 cmets 之后,我想知道我的屏幕尺寸实际上对打印时间有多大影响,而且它确实具有一定的意义。上面真正慢的数字是我的 Gnome 终端被炸到 1920x1200。如果我把它减小得非常小,我会得到......
-----
timing summary (100k lines each)
-----
print : 2.920 s
write to file (+fsync) : 0.121 s
print with stdout = /dev/null : 0.048 s
这当然更好(~4x),但不会改变我的问题。它只是添加到我的问题,因为我不明白为什么终端屏幕渲染应该减慢应用程序写入标准输出的速度。为什么我的程序需要等待屏幕渲染才能继续?
不是所有终端/tty 应用程序都是平等的吗?我还没有做实验。在我看来,终端应该能够缓冲所有传入的数据,不可见地解析/渲染它,并且仅以合理的帧速率渲染当前屏幕配置中可见的最新块。因此,如果我可以在大约 0.1 秒内将 +fsync 写入磁盘,那么终端应该能够以该顺序完成相同的操作(在执行此操作时可能会进行一些屏幕更新)。
我仍然有点希望有一个可以从应用程序端更改的 tty 设置,以使这种行为对程序员更好。如果这严格来说是一个终端应用程序问题,那么这可能甚至不属于 StackOverflow?
我错过了什么?
这是用于生成计时的python程序:
import time, sys, tty
import os
lineCount = 100000
line = "this is a test"
summary = ""
cmd = "print"
startTime_s = time.time()
for x in range(lineCount):
print line
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
#Add a newline to match line outputs above...
line += "\n"
cmd = "write to file (+fsync)"
fp = file("out.txt", "w")
startTime_s = time.time()
for x in range(lineCount):
fp.write(line)
os.fsync(fp.fileno())
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
cmd = "print with stdout = /dev/null"
sys.stdout = file(os.devnull, "w")
startTime_s = time.time()
for x in range(lineCount):
fp.write(line)
t = time.time() - startTime_s
summary += "%-30s:%6.3f s\n" % (cmd, t)
print >> sys.stderr, "-----"
print >> sys.stderr, "timing summary (100k lines each)"
print >> sys.stderr, "-----"
print >> sys.stderr, summary
【问题讨论】:
-
写入标准输出的全部目的是让人们可以读取输出。世界上没有人能在 12 秒内读完 10000 行文本,那么让 stdout 更快有什么意义???
-
@Seun Osewa:一个例子(引发了我的问题)是在做print statement debugging 之类的事情时。您想运行您的程序并在结果发生时查看结果。您显然是对的,大多数行都会飞过而您看不到,但是当发生异常时(或者您点击了您仔细放置的条件 getch/raw_input/sleep 语句),您希望直接查看打印输出而不是经常需要打开或刷新文件视图。
-
打印语句调试是 tty 设备(即终端)默认使用行缓冲而不是块缓冲的原因之一:如果程序挂起并且最后几行,调试输出没有多大用处的调试输出仍在缓冲区中,而不是刷新到终端。
-
@Stephen:这就是为什么我没有费心去追求一位评论者声称通过增加缓冲区大小来实现巨大改进的原因。它完全违背了调试打印的目的!我在调查时做了一些实验,但没有看到任何净改善。我仍然对这种差异感到好奇,但不是真的。
-
@SeunOsewa 不,不,再次不!写入 stdout 的全部目的是它是写入输出的标准位置。不要将标准输出与终端混为一谈。这整个问题都是错误的。写入标准输出不本质上比写入任何其他文件慢。写入终端很慢。 STDOUT 不是终端。原谅我的咆哮,但请让我再说一遍。不要将标准输出与 tty 混为一谈。它们是两个非常不同的东西,只是碰巧经常联系在一起。
标签: python linux printing stdout tty