【问题标题】:Speed up reading in a compressed bz2 file ('rb' mode)加快读取压缩 bz2 文件('rb' 模式)
【发布时间】:2021-04-22 03:03:19
【问题描述】:

我有一个超过 10GB 的 BZ2 文件。我想在不将其解压缩为临时文件的情况下阅读它(它会超过 50GB)。

用这个方法:

import bz2, time
t0 = time.time()
time.sleep(0.001) # to avoid / by 0
with bz2.open("F:\test.bz2", 'rb') as f:
    for i, l in enumerate(f):
        if i % 100000 == 0:
            print('%i lines/sec' % (i/(time.time() - t0)))

我每秒只能读取 ~ 250k 行。在一个类似的文件上,首先解压缩,我每秒得到大约 3M 行,即 x10 因子:

with open("F:\test.txt", 'rb') as f:

我认为这不仅是由于固有的解压 CPU 时间(因为解压到临时文件的总时间 + 读取为未压缩文件的总时间远小于此处描述的方法),还可能是缺少缓冲,或者其他原因。 bz2.open 还有其他更快的 Python 实现吗?

如何加快 BZ2 文件的读取速度,以二进制模式,并在“行”上循环?(以\n 分隔)

注意:目前time to decompress test.bz2 into test.tmp + time to iterate over lines of test.tmp 远小于time to iterate over lines of bz2.open('test.bz2'),这可能不应该是这样。

链接主题:https://discuss.python.org/t/non-optimal-bz2-reading-speed/6869

【问题讨论】:

    标签: python compression buffering bz2


    【解决方案1】:

    您可以使用BZ2Decompressor 来处理大文件。它以增量方式解压缩数据块,开箱即用:

    t0 = time.time()
    time.sleep(0.000001)
    with open('temp.bz2', 'rb') as fi:
        decomp = bz2.BZ2Decompressor()
        residue = b''
        total_lines = 0
        for data in iter(lambda: fi.read(100 * 1024), b''):
            raw = residue + decomp.decompress(data) # process the raw data and  concatenate residual of the previous block to the beginning of the current raw data block
            residue = b''
            # process_data(current_block) => do the processing of the current data block
            current_block = raw.split(b'\n')
            if raw[-1] != b'\n':
                residue = current_block.pop() # last line could be incomplete
            total_lines += len(current_block)
            print('%i lines/sec' % (total_lines / (time.time() - t0)))
        # process_data(residue) => now finish processing the last line
        total_lines += 1
        print('Final: %i lines/sec' % (total_lines / (time.time() - t0)))
    

    在这里,我读取了一大块二进制文件,将其输入解压缩器并接收一大块解压缩数据。请注意,必须将解压缩的数据块连接起来才能恢复原始数据。这就是为什么最后一个条目需要特殊处理的原因。

    在我的实验中,它的运行速度比您使用 io.BytesIO() 的解决方案要快一些。众所周知,bz2 速度很慢,因此如果您觉得麻烦,请考虑迁移到 snappyzstandard

    关于在 Python 中处理 bz2 所需的时间。使用 Linux 实用程序将文件解压缩为临时文件,然后处理普通文本文件可能是最快的。否则,您将依赖 Python 的 bz2 实现。

    【讨论】:

    • 感谢您的回答。你确定bz2.open() 解压成一个临时文件吗?我不这么认为:我没有在文档中找到这个,而且我在任何地方都没有看到临时文件(但我可能错了!)你的方法效果很好(我的测试中为 850k 行/秒 vs. 700k行/秒(使用我的最新方法),但要付出的代价是能够将整个解压缩文件加载到内存中,在您的 datatemp 对象中。对于 50 GB 的文件,这对我来说是不可能的;)
    • 1) re temp file 这是我从互联网上的文档中了解到的。今天试了一下,现在不是这样了。 2)重新块处理。很抱歉没有说得足够清楚,该方法允许处理大文件。请查看更新版本。
    • 太好了,我会测试你的新版本! PS:您也许可以更新有关“临时文件”的第一段。
    • 你有一个很好的解决方案@igrinis,你可能想把它贴在这里(核心python开发者讨论):discuss.python.org/t/non-optimal-bz2-reading-speed/6869
    【解决方案2】:

    这种方法已经比原生 bz2.open 提高了 2 倍。

    import bz2, time, io
    
    def chunked_readlines(f):
        s = io.BytesIO()
        while True:
            buf = f.read(1024*1024)
            if not buf:
                return s.getvalue()
            s.write(buf)
            s.seek(0)
            L = s.readlines()
            yield from L[:-1]
            s = io.BytesIO()
            s.write(L[-1])  # very important: the last line read in the 1 MB chunk might be
                            # incomplete, so we keep it to be processed in the next iteration
                            # TODO: check if this is ok if f.read() stopped in the middle of a \r\n?
    
    t0 = time.time()
    i = 0
    with bz2.open("D:\test.bz2", 'rb') as f:
        for l in chunked_readlines(f):       # 500k lines per second
        # for l in f:                        # 250k lines per second
            i += 1
            if i % 100000 == 0:
                print('%i lines/sec' % (i/(time.time() - t0)))
    

    或许可以做得更好。

    如果我们可以使用s 作为一个简单的bytes 对象而不是io.BytesIO,我们可以得到x4 的改进。但不幸的是,在这种情况下,splitlines() 的行为与预期不同:splitlines() and iterating over an opened file give different results

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-03
      • 1970-01-01
      • 2011-06-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多