保持文件句柄打开的缺点?

Downsides of keeping file handles open?

我正在解析一些 XML 并根据当前正在处理的 XML 元素将数据写入不同的文件。处理一个元素非常快,写入数据也是如此。因此,文件需要经常打开和关闭。例如,给定一个巨大的 file:

for _, node in lxml.etree.iterparse(file):
    with open(f"{node.tag}.txt", 'a') as fout:
        fout.write(node.attrib['someattr']+'\n'])

这样可以,但是相对来说打开和关闭文件会花费很多时间。 (注意:这是一个玩具程序,实际上我写入文件的内容和文件名都是不同的。数据详情见最后一段。)

备选方案可以是:

fhs = {}
for _, node in lxml.etree.iterparse(file):
    if node.tag not in fhs:
        fhs[node.tag] = open(f"{node.tag}.txt", 'w')

    fhs[node.tag].write(node.attrib['someattr']+'\n'])

for _, fh in fhs.items(): fh.close()

这将使文件保持打开状态,直到 XML 的解析完成。有一点查找开销,但与迭代打开和关闭文件相比,这应该是最小的。

我的问题是,这种方法在性能方面的缺点是什么?我知道这会使其他进程无法访问打开的文件,并且您可能 运行 变成 a limit of open files。但是,我对性能问题更感兴趣。保持所有文件句柄打开会产生某种内存问题或处理问题吗?在这种情况下,可能发生了过多的文件缓冲?我不确定,所以这个问题。

输入 XML 文件最大可达 70GB 左右。生成的文件数量限制在 35 个左右,这与我在上述 post.

中读到的限制相去甚远

您已经提到的明显缺点是,打开所有文件句柄需要大量内存,当然这取决于文件的数量。这是您必须自己进行的计算。并且不要忘记写锁。

除此之外并没有太大的问题,但如果有一些预防措施会很好:

fhs = {}
try:
    for _, node in lxml.etree.iterparse(file):
        if node.tag not in fhs:
            fhs[node.tag] = open(f"{node.tag}.txt", 'w')

        fhs[node.tag].write(node.attrib['someattr']+'\n'])
finally:
    for fh in fhs.values(): fh.close()

注意: 当在 python 中循环一个 dict 时,你得到的项目实际上只是键。我建议做 for key, item in d.items():for item in d.values():

没有说进程最终会打开多少文件。如果它不是太多而造成问题,那么这可能是一个好方法。我怀疑如果不在您的数据和执行环境中尝试,您是否真的知道。

根据我的经验,open() 相对较慢,因此避免不必要的调用绝对值得考虑——您还可以避免设置所有关联的缓冲区,填充它们,每次关闭文件时刷新它们和垃圾收集。既然你问了,文件指针确实带有大缓冲区。在 OS X 上,默认缓冲区大小为 8192 字节 (8KB),对象有额外开销,与所有 Python 对象一样。因此,如果您有成百上千个文件和很少的 RAM,它会累加起来。您可以指定更少的缓冲或根本没有缓冲,但这可能会破坏通过避免重复打开而获得的任何效率。

编辑: 对于仅 35 个不同的文件(或任何两位数),您无需担心:35 个输出缓冲区将需要 space (实际缓冲每个缓冲区 8 KB)甚至不会是内存占用的最大部分。因此,请继续按照您建议的方式进行操作。与每个 xml 节点打开和关闭文件相比,您将看到显着的速度提升。

PS。默认缓冲区大小由 io.DEFAULT_BUFFER_SIZE.

给出

如果您正在按照您可能考虑编写的方式生成数千个文件 他们到一个目录结构让他们分别存储在不同的 目录以便以后更容易访问。例如:a/a/aanode.txt、a/c/acnode.txt 等

如果 XML 包含连续的节点,您可以同时写入 条件为真。您只会在另一个文件的另一个节点出现时关闭。 您从中获得的收益在很大程度上取决于 XML 文件的结构。

作为一个好的规则,请尽快关闭文件。

请注意,您的操作系统也有限制 - 您只能打开一定数量的文件。因此,您可能很快就会达到此限制,并且您将开始收到 "Failed to open file" 个异常。

内存和文件句柄泄漏是明显的问题(如果由于某种原因无法关闭文件)。