为什么打印stdout这么慢?可以加快吗?

问题:

我一直很惊讶/沮丧,只需输入一个打印语句到终端需要多长时间。在最近一段令人痛苦的慢记录之后,我决定研究一下,很惊讶地发现,几乎所有花费的时间在等待终端处理结果。
可以写信给stdout加速了吗?
我在这个问题的底部写了一个脚本(“print_timer.py”)来比较写入100k行到stdout,文件,和stdout重定向到/dev/null时的时间。这是时间结果:
$python print_timer.py
this is a test
this is a test

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

所以它不是一个蟒蛇的技巧,它只是终端。我总是知道倾销输出到/ dev / null sped的东西,但从来没有想到这是重要的!
令我惊讶的是,tty是多么的缓慢。写入物理磁盘的速度比写入“屏幕”(推测是全RAM操作)的速度更快,并且和使用/ dev / null一样简单的将其转储到垃圾中的速度呢?
 This link谈论终端如何阻止I / O,以便“parse [the input], update its frame buffer, communicate with the X server in order to scroll the window and so on” …但我没有完全了解。可以花多长时间?
我期望没有办法(没有更快的tty实现?),但是图我会问。
更新:阅读一些评论后,我想知道我的屏幕尺寸实际上对打印时间有多大的影响,这确实有一定的意义。上面真的很慢的数字是我的Gnome终端炸毁到1920×1200。如果我减少它非常小我得到…
—–
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),但不改变我的问题。我的问题只有adds,因为我不明白为什么终端屏幕渲染应该减缓写入stdout的应用程序。为什么我的程序需要等待屏幕渲染才能继续?
所有终端/ 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

回答:

写入物理磁盘的速度比写入“屏幕”(推测是全RAM操作)的速度更快,并且和使用/ dev / null一样简单的将其转储到垃圾中的速度呢?

恭喜,您刚才发现了I / O缓冲的重要性。 🙂
磁盘appears更快,因为它是高度缓冲的:所有Python的write()调用在任何实际写入物理磁盘之前返回。 (操作系统稍后会将数千个单独的写入结合成一个大而高效的块)。
另一方面,终端几乎没有或没有缓冲:每个print / write(line)等待充分写(即显示输出设备)完成。
为了使比较公平,您必须使文件测试与终端使用相同的输出缓冲,您可以通过将示例修改为:

fp = file("out.txt", "w", 1)   # line-buffered, like stdout
[...]
for x in range(lineCount):
    fp.write(line)
    os.fsync(fp.fileno())      # wait for the write to actually complete

我在我的机器上运行你的文件写作测试,并且缓冲,这里也有0.05s的线为100,000行。
然而,通过上述修改来无缓冲地写入,只需要40秒才能将磁盘写入1000行。我放弃了等待10万行的写作,但是从前一步推断,这将需要一个多小时
那终端的11秒进入角度,不是吗?
所以为了回答你原来的问题,写一个终端实际上​​是快速的,所有的事情都考虑过了,而且没有太多的空间让它快得多(但是个人终端在做多少工作方面有所不同;请参见Russ对此的评论回答)。
(您可以添加更多的写入缓冲,如磁盘I / O,但是在缓冲区被刷新之前,您将看不到写入终端的内容,这是一个权衡:交互性与批量效率。)

 
 
Code问答: http://codewenda.com/topics/python/
Stackoverflow: Why is printing to stdout so slow? Can it be sped up?

*转载请注明本文链接以及stackoverflow的英文链接

发表评论

电子邮件地址不会被公开。 必填项已用*标注

18 − 13 =