【问题标题】:Buffered versus unbuffered output for Python in WindowsWindows 中 Python 的缓冲输出与非缓冲输出
【发布时间】:2012-04-20 00:58:24
【问题描述】:

我使用 NppExec/Notepad++ 运行 Python 脚本,并在运行时不断刷新我的输出缓冲区以使用 print 语句更新我的控制台窗口(默认缓冲输出仅在脚本完成执行)。

This 链接显示您需要做的就是使用命令python -u 来获得无缓冲的输出。对我的所有我的 Python 脚本使用这种执行模式是否有缺点,无论使用的编辑器是什么?我不清楚缓冲输出和非缓冲输出之间的区别。

编辑:我包含了这个小的 Python 计时器脚本作为示例:

#!usr/bin/env python
import time
import threading
import sys

class Timer(threading.Thread):
    def __init__(self, seconds):
        self.runTime = seconds
        threading.Thread.__init__(self)
    def run(self):
        counter = self.runTime
        for sec in range(self.runTime):
            print counter
            time.sleep(1.0)
            counter -= 1
        print "Done."

if __name__ == '__main__':
    t = Timer(10)
    t.start()

在这种情况下,缓冲输出和非缓冲输出在效率方面有何不同?

【问题讨论】:

    标签: python notepad++ nppexec unbuffered-output


    【解决方案1】:

    缓冲输出意味着计算机将输出假脱机到内存中的某个位置,直到累积到一定数量。然后它一次写入整个块。这比使用无缓冲输出更有效,后者在您请求写入输出时立即写入输出。

    缺点是您的程序运行速度会稍慢(或很多),具体取决于您编写的输出量。如果它们是不做太多输出的短程序,你不太可能注意到差异......

    编辑

    缓冲与非缓冲输出不仅仅是一个 python 问题。相同的概念(和术语)也适用于其他语言。在较低级别的语言中,它在某些方面变得更加重要——如果我使用缓冲输出在 C 程序中编写消息,然后我的程序由于编程错误而死,任何在错误之前被假脱机的数据,但不是写入磁盘丢失。这不是一个问题,因为要让 python 解释器在错误时中止是相当困难的——即使你的代码很糟糕,解释器最终仍然会清理干净......(除非你向它发送终止信号什么的)...

    【讨论】:

    • 您能否详细说明一下“短”和“长”程序?它是否与浮点运算、迭代、算法复杂性或我放置的打印命令的数量有关?我还编辑了我的问题
    • @prrao 这将与打印命令的数量有关——您一次写出的字节数。在您的示例中, time.sleep 函数将比您的 IO 花费 WAY 更长的时间来完成,因为它只有 1 个打印语句。所以,对于那个例子,无缓冲的 IO 很好。不过一般来说,最简单的判断方法是计时(使用timeit 模块),看看是否有任何明显的差异。
    【解决方案2】:

    我可以想到两个缺点,但它们的重要性取决于您的需求:

    1. 无缓冲的读写可能会明显变慢;如果您一次编写一行文本文件,您的代码可能会进行数百次系统调用以要求操作系统写入文件。根据您写入磁盘的速度,这甚至可能意味着需要从磁盘重新读取文件的最后一个块,以便使用新的最后一行重新保存文件。 (这可能很少见;但更多的系统调用几乎总是导致速度变慢的秘诀。)

      下面是对写入速度有很大影响的系统调用数量的简单演示:

      $ cat initrd.img-2.6.38-8-generic > /dev/null
      

      第一行确保文件在缓存中,因此只测量输出速度。

      $ dd if=initrd.img-2.6.38-8-generic of=/tmp/out bs=16 oflag=dsync
      ^C262766+0 records in
      262766+0 records out
      4204256 bytes (4.2 MB) copied, 50.7754 s, 82.8 kB/s
      

      我放弃了等待——它的速度太慢了。这是一次“无缓冲”将 16 个字节写入磁盘,并确保每次写入都成功,然后再进行下一次写入。 (这是dsync——稍后会详细介绍。)

      $ dd if=initrd.img-2.6.38-8-generic of=/tmp/out bs=$((4096)) oflag=dsync
      3218+1 records in
      3218+1 records out
      13181130 bytes (13 MB) copied, 3.69961 s, 3.6 MB/s
      $ dd if=initrd.img-2.6.38-8-generic of=/tmp/out bs=$((4096 * 10)) oflag=dsync
      321+1 records in
      321+1 records out
      13181130 bytes (13 MB) copied, 0.471143 s, 28.0 MB/s
      

      这两个命令显示了一些缓冲的效果——第一个命令以 4096 字节的块写入数据,这可能是默认缓冲提供给您的。这大约比一次 16 个字节快 50 倍。第二个命令是以 40960 字节的块写入数据,它的速度大约快了 9 倍仍然。 (总而言之,一次写入 40960 个字节比一次写入 16 个字节快大约 345 倍。)

      如果您的数据很小,这并不重要。毕竟,无论哪种方式都不会花费太多时间。如果您的数据很大,它可能更重要,这取决于您一次写入的数据量以及它在底层设备的快乐“字节边界”上排列的频率。

    2. 套接字上的某些协议可能会根据您发送的数据的时间改变它们的行为。如果您以增量方式构建数据,您可能会在单个数据包中发送部分数据,而基于数据包的接收器可能无法妥善处理此问题。 (我很难想象一个基于 TCP 的系统会出现这个问题,而不是驱动某种游戏;一个基于 UDP 的系统更容易想象会出现这个问题。)

    【讨论】:

    • 你的例子有点超出我的想象!你能看看我的问题编辑并解释一下吗?感谢您的详细回复!
    • 我不太清楚“数据是小是大”。这是否意味着我要打印出 行数?我通过控制台在本地运行所有 Python 文件
    • 好的,我想我的问题已经回答了。谢谢!
    • 哈,再次表明了询问更多上下文的重要性。您的代码如此之小,而且功能如此之少,以至于性能并不重要——但正确性确实。您可以通过在print 语句之后立即添加刷新命令来使您的程序正常工作,但老实说,它的工作方式相同。只需使用-u 并快乐。 :)
    • 如果我的代码运行时间远远大于 打印几行更新行以通知用户运行进度所花费的时间,我将失去这样的寓意,我可以放心使用python -u,不用担心。希望我在这里是正确的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多