我惊讶/沮丧地发现,简单地用打印语句输出到终端都需要很长时间。在最近打印一些慢日志记录后,我决定调查看看,并相当惊讶地发现几乎所有的时间花费在等待终端处理结果。
是否可以以某种方式加快写入标准输出的速度?
我写了一个脚本(在这个问题的底部'print_timer.py
')来比较将100k行写入标准输出、文件和标准输出重定向到/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
所以这不是一个python的技巧,它只是一个终端。我一直知道将输出转储到/dev/null可以加快速度,但从未想过它有这么重要!
tty的速度真让我吃惊。为什么写到物理磁盘比写到“屏幕”(大概是一个全ram操作)要快,并且有效地和简单地用/dev/null转储到垃圾一样快?
这个链接讨论了终端如何阻塞I/O,以便它可以“解析[输入],更新它的帧缓冲区,与X服务器通信以便滚动窗口等等”…但我不太明白怎么花了这么长时间?
我想这是没有出路的(缺少一个更快的tty实现?),但我还是会问。
更新:在阅读了一些评论后,我想知道我的屏幕尺寸对打印时间有多大影响,它确实有一些意义。上面真正慢的数字是我的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
这当然更好(~4倍),但并没有改变我的问题。它只增加了我的问题,因为我不明白为什么终端屏幕呈现应该减慢应用程序写入标准输出。为什么我的程序需要等待屏幕呈现继续?
是不是所有的终端/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