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

2025-03-05 09:14:00
admin
原创
108
摘要:问题描述:我一直对使用 print 语句简单地输出到终端所花费的时间感到惊讶/沮丧。在最近经历了一些非常缓慢的日志记录后,我决定研究一下,并惊讶地发现几乎所有的时间都花在等待终端处理结果上。可以以某种方式加快写入标准输出的速度吗?我编写了一个脚本(print_timer.py本问题底部的“”)来比较将 100...

问题描述:

我一直对使用 print 语句简单地输出到终端所花费的时间感到惊讶/沮丧。在最近经历了一些非常缓慢的日志记录后,我决定研究一下,并惊讶地发现几乎所有的时间都花在等待终端处理结果上。

可以以某种方式加快写入标准输出的速度吗?

我编写了一个脚本(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
" % (cmd, t)

#Add a newline to match line outputs above...
line += "
"

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
" % (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
" % (cmd, t)

print >> sys.stderr, "-----"
print >> sys.stderr, "timing summary (100k lines each)"
print >> sys.stderr, "-----"
print >> sys.stderr, summary

解决方案 1:

为什么写入物理磁盘比写入“屏幕”(大概是全 RAM 操作)要快得多,并且实际上与使用 /dev/null 简单地转储到垃圾一样快?

恭喜,您刚刚发现了 I/O 缓冲的重要性。:-)

磁盘似乎更快,因为它具有高度缓冲:所有 Pythonwrite()调用都会在实际将任何内容写入物理磁盘之前返回。(操作系统稍后会执行此操作,将数千个单独的写入组合成一个大而高效的块。)

另一方面,终端很少或根本不进行缓冲:每个单独的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

我在我的机器上运行了您的文件写入测试,通过缓冲,100,000 行也只需 0.05 秒。

但是,通过上述修改,以非缓冲方式写入,需要 40 秒才能将 1,000 行写入磁盘。我放弃了等待 100,000 行写入的想法,但根据之前的情况推断,这将需要一个多小时

这样就让我们对航站楼的 11 秒有了更直观的认识,不是吗?

因此,要回答你最初的问题,从各方面来看,写入终端的速度实际上非常快,并且没有太多的空间可以使其更快(但各个终端在执行的工作量上确实有所不同;请参阅 Russ 对这个答案的评论)。

(您可以添加更多的写入缓冲,例如磁盘 I/O,但之后您才会看到写入终端的内容,直到缓冲区被刷新之后。这是一个权衡:交互性与批量效率。)

解决方案 2:

感谢大家的评论!在你们的帮助下,我最终自己回答了这个问题。不过,自己回答问题感觉很不礼貌。

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

答案:打印到 stdout本身并不慢。慢的是你使用的终端。而且它与应用程序端的 I/O 缓冲(例如:python 文件缓冲)几乎没有关系。见下文。

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

答案:是的,可以,但从程序端(向 stdout 进行“打印”的端)看来不行。要加快速度,请使用更快的其他终端仿真器。

解释...

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

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

0.26 秒比 12 秒好多了!我不知道 是否wterm更智能地按照我建议的方式渲染到屏幕上(以合理的帧速率渲染“可见”的尾部),或者它只是“做得更少” gnome-terminal。不过,就我的问题而言,我已经找到了答案。 gnome-terminal很慢。

所以 - 如果您有一个长时间运行的脚本,您觉得它很慢并且它会向标准输出大量文本......请尝试使用不同的终端,看看是否有所改善!

请注意,我几乎是随机wterm从 ubuntu/debian 存储库中提取的。 此链接可能是同一个终端,但我不确定。我没有测试任何其他终端仿真器。


更新:因为我不得不解决这个问题,所以我用相同的脚本和全屏(1920x1200)测试了一大堆其他终端仿真器。我手动收集的统计数据如下:

冬季 0.3秒
0.3秒
接收 0.3秒
mrxvt 0.4秒
控制台 0.6秒
yakuake 0.7s
外部终端 7s
xterm 9s
gnome 终端 12s
xfce4-终端 12s
vala-18s 号航站楼
48秒

记录的时间是手动收集的,但它们相当一致。我记录了最佳值。显然,YMMV 不同。

作为奖励,这是一次有趣的旅程,带我了解了市面上各种终端模拟器!我很惊讶我的第一个“替代”测试竟然是最好的。

解决方案 3:

您的重定向可能没有任何作用,因为程序可以确定它们的输出 FD 是否指向 tty。

当指向终端时,stdout 很可能是行缓冲的(与 C 的stdout流行为相同)。

作为一个有趣的实验,尝试将输出通过管道传输到cat


我进行了自己的有趣实验,结果如下。

$ python test.py 2>foo
...
$ cat foo
-----
timing summary (100k lines each)
-----
print                         : 6.040 s
write to file                 : 0.122 s
print with stdout = /dev/null : 0.121 s

$ python test.py 2>foo |cat
...
$ cat foo
-----
timing summary (100k lines each)
-----
print                         : 1.024 s
write to file                 : 0.131 s
print with stdout = /dev/null : 0.122 s

解决方案 4:

我不能谈论技术细节,因为我不知道,但这并不让我感到惊讶:终端不是为打印大量数据而设计的。事实上,你甚至提供了一个指向大量 GUI 内容的链接,每次你想打印某些东西时它都必须执行这些操作!请注意,如果你用 调用脚本pythonw,它不会花费 15 秒;这完全是一个 GUI 问题。重定向stdout到文件以避免这种情况:

import contextlib, io
@contextlib.contextmanager
def redirect_stdout(stream):
    import sys
    sys.stdout = stream
    yield
    sys.stdout = sys.__stdout__

output = io.StringIO
with redirect_stdout(output):
    ...

解决方案 5:

打印到终端会很慢。遗憾的是,除非编写新的终端实现,否则我真的看不出如何显著加快速度。

解决方案 6:

除了输出可能默认为行缓冲模式之外,输出到终端还会导致数据流入具有最大吞吐量的终端和串行线路,或流入伪终端和单独的进程,该进程处理显示事件循环、渲染某种字体的字符、移动显示位以实现滚动显示。后一种情况可能分布在多个进程中(例如 telnet 服务器/客户端、终端应用程序、X11 显示服务器),因此也存在上下文切换和延迟问题。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3970  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2740  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   79  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   87  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   74  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用