我一直对使用print语句简单地输出到终端需要多长时间感到惊讶/沮丧。在经历了最近令人痛苦的缓慢日志记录之后,我决定进行调查,并惊讶地发现几乎 所有 的时间都在等待终端处理结果。
可以以某种方式加快对stdout的写入速度吗?
我编写了一个脚本(print_timer.py此问题底部的’ ‘)来比较将100k行写入stdout,文件以及将stdout重定向到时的时序/dev/null。计时结果如下:
print_timer.py
/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转储到垃圾中一样快?
该链接讨论了终端如何阻止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倍),但不会改变我的问题。这只会 增加 我的问题,因为我不明白为什么终端屏幕渲染会减慢应用程序向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
感谢所有的评论!我最终在您的帮助下自行回答。不过,回答您自己的问题感觉很脏。
问题1:为什么打印到标准输出速度慢?
答: 打印到标准输出 并不是 天生就慢。您正在使用的终端很慢。它与应用程序端的I / O缓冲(例如python文件缓冲)几乎为零。见下文。
问题2:可以加快速度吗?
答: 是的,可以,但似乎不是从程序方面(将“打印”到stdout的那一侧)进行。为了加快速度,请使用更快的其他终端仿真器。
说明…
我尝试了一个自描述为“轻量级”的终端程序,wterm并获得了 明显 更好的结果。下面是在wterm同一系统上以1920x1200 运行时,我的测试脚本的输出(在问题的底部),该系统使用gnome-terminal的基本打印选项花费了12s:
wterm
----- 时序摘要(每条10万行) ----- 打印:0.261 s 写入文件(+ fsync):0.110 s 用stdout = / dev / null打印:0.050 s
0.26s比12s好得多!我不知道是否wterm更聪明地按照我的建议(以合理的帧速率渲染“可见”尾巴)来渲染屏幕,或者是否比“少” gnome- terminal。为了我的问题,我得到了答案。 gnome-terminal是慢的。
gnome- terminal
gnome-terminal
所以-如果您运行的脚本长时间运行,感觉很慢,并且会向stdout喷出大量文本,请尝试其他终端,看看它是否更好!
请注意,我几乎是wterm从ubuntu / debian存储库中随机提取的。 该链接可能是同一终端,但我不确定。我没有测试任何其他终端模拟器。
更新:因为必须要抓痒,所以我用相同的脚本和全屏(1920x1200)测试了一大堆其他终端模拟器。我的手动收集的统计信息在这里:
wterm 0.3秒 间隔0.3秒 rxvt 0.3秒 mrxvt 0.4s konsole 0.6秒 药师0.7s 接线柱7s xterm 9s gnome终端12s xfce4端12s 巴拉终端18s xvt 48s
记录的时间是手动收集的,但它们是相当一致的。我记录了最好的(ish)值。YMMV,显然。
另外,它是对其中可用的各种终端仿真器的一次有趣的浏览!我很惊讶我的第一个“替代”测试是同类中最好的。