一尘不染

为什么打印到标准输出这么慢?可以加快速度吗?

linux

我一直对使用print语句简单地输出到终端需要多长时间感到惊讶/沮丧。在经历了最近令人痛苦的缓慢日志记录之后,我决定进行调查,并惊讶地发现几乎 所有
的时间都在等待终端处理结果。

可以以某种方式加快对stdout的写入速度吗?

我编写了一个脚本(print_timer.py此问题底部的’
‘)来比较将100k行写入stdout,文件以及将stdout重定向到时的时序/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

阅读 696

收藏
2020-06-02

共1个答案

一尘不染

感谢所有的评论!我最终在您的帮助下自行回答。不过,回答您自己的问题感觉很脏。

问题1:为什么打印到标准输出速度慢?

答: 打印到标准输出 并不是 天生就慢。您正在使用的终端很慢。它与应用程序端的I / O缓冲(例如python文件缓冲)几乎为零。见下文。

问题2:可以加快速度吗?

答: 是的,可以,但似乎不是从程序方面(将“打印”到stdout的那一侧)进行。为了加快速度,请使用更快的其他终端仿真器。

说明…

我尝试了一个自描述为“轻量级”的终端程序,wterm并获得了 明显 更好的结果。下面是在wterm同一系统上以1920x1200
运行时,我的测试脚本的输出(在问题的底部),该系统使用gnome-terminal的基本打印选项花费了12s:

-----
时序摘要(每条10万行)
-----
打印:0.261 s
写入文件(+ fsync):0.110 s
用stdout = / dev / null打印:0.050 s

0.26s比12s好得多!我不知道是否wterm更聪明地按照我的建议(以合理的帧速率渲染“可见”尾巴)来渲染屏幕,或者是否比“少” 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,显然。

另外,它是对其中可用的各种终端仿真器的一次有趣的浏览!我很惊讶我的第一个“替代”测试是同类中最好的。

2020-06-02