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

我一直对使用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"

幕布斯7119047
浏览 1094回答 3
3回答

喵喔喔

写入物理磁盘比写入“屏幕”(大概是全RAM操作)要快得多,并且实际上与使用/ dev / null转储到垃圾中一样快?恭喜,您刚刚发现了I / O缓冲的重要性。:-)磁盘似乎速度更快,因为它具有很高的缓冲能力:write()在将任何内容实际写入物理磁盘之前,所有Python的调用都将返回。(操作系统稍后执行此操作,将成千上万的单个写入合并为一个大而有效的块。)另一方面,终端几乎不执行缓冲或不执行缓冲:每个人print/ write(line)等待完整的写入(即显示到输出设备)完成。为了使比较合理,您必须使文件测试使用与终端相同的输出缓冲,可以通过将示例修改为以下操作来做到这一点:fp = file("out.txt", "w", 1)&nbsp; &nbsp;# line-buffered, like stdout[...]for x in range(lineCount):&nbsp; &nbsp; fp.write(line)&nbsp; &nbsp; os.fsync(fp.fileno())&nbsp; &nbsp; &nbsp; # wait for the write to actually complete我在我的机器上运行了文件写入测试,并通过缓冲在100,000行中也进行了0.05s的测试。但是,通过上述修改以无缓冲方式写入数据,只需要40秒就可以将1,000行写入磁盘。我放弃了等待100,000行的写操作,但是从以前的内容推论得出,这将花费一个多小时。这使航站楼的11秒成为现实,不是吗?因此,要回答您最初的问题,考虑到所有因素,写一个终端实际上非常快,并且没有太多的空间可以使它更快(但是各个终端的工作量有所不同;请参阅Russ对此的评论)回答)。(您可以像使用磁盘I / O一样添加更多的写缓冲,但是直到缓冲区被刷新后,您才能看到向终端写入的内容。这是一个折衷方案:交互性与大容量效率。)
打开App,查看更多内容
随时随地看视频慕课网APP