【问题标题】:Why is file.close() slowing my code为什么 file.close() 会减慢我的代码
【发布时间】:2017-01-16 11:23:57
【问题描述】:

我正在编写一个程序,它生成指定数量的句子,每个句子都写入一个文件。我一直在尝试针对 1000 万个或更多句子的案例优化代码。

我最近在我的开放调用中将缓冲区参数指定为 512MB,以提高写入性能,然而,我的代码实际上变慢了 3 秒。罪魁祸首是一个叫做{method 'close' of '_io.TextIOWrapper' objects}的东西。我认为这与文件关闭方法有关,但这是我第一次比较配置文件输出。

这是我以前的写入速度有多慢:

10000000   49.057    0.000   49.057    0.000 {method 'write' of '_io.TextIOWrapper' objects}

现在是这样的:

10000000    3.184    0.000    3.184    0.000 {method 'write' of '_io.TextIOWrapper' objects}

相当大的改进。

这是我以前的关闭方法:

3    4.003    1.334    4.003    1.334 {method 'close' of '_io.TextIOWrapper' objects}

这是我的新的:

 1   62.668   62.668   62.668   62.668 {method 'close' of '_io.TextIOWrapper' objects}

这是我的代码:

def sentence_maker(nouns, verbs, number_of_sentences, file_name):
    writer = open(file_name, "w", 536870912)
    for num in range(number_of_sentences):
        string = (choice(nouns) + " " + choice(verbs) + " " + choice(nouns))
        writer.write(string + "\n")
    writer.close()

为什么close() 这么慢?

注意:在程序的早期某处,我曾经有一些 close() 语句,因此在我的旧 close() 示例中使用了 ncalls = 3。我已确定这些对性能没有明显影响。

【问题讨论】:

  • 请确保在发布 Python 代码时准确地复制缩进。严重缩进的 Python 代码是无稽之谈。
  • 您是否尝试过使用with open(<file_name>,'w') as fp: 样式进行测试?
  • 通常最好使用with 而不是“正常”关闭文件。
  • 由于close 也会刷新文件缓冲区尝试使用显式writer.flush() 进行测量
  • @Bakuriu 是的......它需要 134 和几百万行才能使缓冲区溢出。我想性能开销来自维护缓冲区所需的内存分配/管理(特别是正如 chepner 所指出的 - 如果其中一些缓冲区最终被换出),以及单个大刷新最终会影响底层操作系统的事实系统缓冲区,仍然需要 N 次较小的刷新/同步。一般来说,任何大于物理驱动器块大小的东西都没有多大好处。

标签: python performance io


【解决方案1】:

您明确选择使用一个巨大的缓冲区(536870912 是在刷新缓冲区之前缓冲的字节数,大约是半 GB 内存)。 close 包含一个隐含的 flush ,表示缓冲区中剩余的任何内容,假设你写了很多,这意味着它涉及到全部写出来。

您必须在某些时候为实际的 I/O 付费;一个大缓冲区使write 便宜(因为它实际上不执行任何 I/O),但一个大缓冲区只是推迟了痛苦,而不是避免它。我怀疑任何超过 1 MB 的缓冲区大小实际上会节省有意义的工作(并且限制可能会更低);如果你经常这样做,执行系统调用的成本会很高,但是当每次调用完成的工作(实际的物理 I/O)超过两者时,每 MB 一次调用和每 512 MB 一次调用之间的差异没有意义一个数量级或更多。

作为比较,缓冲的原因是系统调用与常规函数调用相比具有较高的开销 (a few hundred clock ticks, vs. a dozen or less for most function calls)。 CPython 在 I/O 中涉及一些额外的系统调用(释放和恢复 GIL),因此对 write 与 memcpy 的系统调用的增量成本可能相差 100-1000 倍。但即使是 2000 个滴答声,现代 CPU 的开销仍然在微秒范围内。但是 I/O 本身比这要贵得多;写入 10 MB 的数据可能需要十分之一秒左右。 当 I/O 本身的成本以 秒 为单位时,通过使用更大的缓冲区大小来节省几 毫秒的系统调用并不重要。一个大的缓冲区将开始引入一个较小的缓冲区可以避免的缓存未命中(以及可能的页面错误)。

【讨论】:

    【解决方案2】:

    写入磁盘很慢,因此许多程序将写入存储到大块中,它们一次写入。这称为缓冲,当您打开文件时 Python 会自动执行此操作。当您写入文件时,您实际上是在写入内存中的“缓冲区”。当它填满时,Python 会自动将其写入磁盘或调用 close()。

    【讨论】:

      猜你喜欢
      • 2018-11-30
      • 2012-01-02
      • 2019-01-03
      • 2021-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 2018-01-02
      相关资源
      最近更新 更多