【问题标题】:Python: how to memory-map(mmap) a gzipped filePython:如何内存映射(mmap)压缩文件
【发布时间】:2021-11-02 12:20:29
【问题描述】:

我的要求是读取一个 gzip 压缩文件,由于这些文件很大,我想对其进行内存映射以获得 I/O 性能。

我尝试了以下代码:

import gzip
import mmap
with gzip.open("/home/test.json.gz", mode="r") as f:
    with mmap.mmap(f.fileno(), length=0, access=mmap.ACCESS_READ) as f_mmap:
        print(f_mmap.read())

上述代码中的print 语句将打印以下一系列十六进制作为输出:

b'\x1f\x8b

当我尝试从上述代码中删除 mmap 时,我看到了正确的预期结果。

您能否提供有关如何对 gzip 文件进行内存映射的建议?

【问题讨论】:

    标签: python python-3.x gzip mmap


    【解决方案1】:

    mmap原始文件访问;它从f (从gzip.open 创建的对象)唯一使用的是.fileno() 方法,它获取原始文件描述符,它根本不知道文件被压缩(gzip.open 包装了原始文件层中的文件描述符按需执行解压缩,但低级文件描述符不知道所有这些)。

    为了解决一些困惑:mmap 不会为您带来神奇的 I/O 性能提升。如果您满足以下条件,这将非常有用:

    1. 正在对文件执行随机访问,理想情况下
    2. 重复读取文件(经常重新访问您之前访问过的相同页面)

    对于像 JSON 这样的东西,随机访问本质上是无用的;该文件可能是 UTF-8 文本,因此随机访问甚至不能保证落在有效字符的开头,即使是,JSON 中字符 N 的解释取决于字符 0 到 N- 的解释1(我们是在一个对象、一个数组、一个字符串等吗?不知道其余的就不能知道)。所以#1 不适用。

    同样,一遍又一遍地重复读取同一个 JSON 文件并没有什么好处;反序列化一次并使用它。

    重点是,跳过mmap,然后做:

    import json
    import gzip
    
    with gzip.open("/home/test.json.gz", mode="r") as f:
        data = json.load(f)
    

    如果您的文件格式适合随机访问,您仍然不能直接使用 gzip 压缩文件(压缩数据,如 JSON,取决于上下文;您需要前一个字节的上下文来解释下一个字节)。如果您出于某种原因必须使用mmap,则需要先对其进行解压缩,例如:

    import gzip
    import shutil
    import tempfile
    
    with tempfile.TemporaryFile() as f_temp:  # Make an unnamed temporary file to use for mmap
        with gzip.open("/home/test.data.gz") as f:
            shutil.copyfileobj(f, f_temp)  # Efficiently decompress from gzip to temp file
        f_temp.flush()  # Ensure no data stuck in user-mode buffers
    
        # Memory map temporary file and use it
        with mmap.mmap(f_temp.fileno(), length=0, access=mmap.ACCESS_READ) as f_mmap:
            print(f_mmap.read())
    
        # mapping closed and deleted outside its with
    # temporary file closed and deleted outside its with
    

    在实践中,如果您要经常重复使用此文件,我建议您将其未压缩存储,以避免每次在使用前解压缩,但我想我会演示如何制作这样的文件以防万一您的实际用例需要它。

    【讨论】:

    • 请注意,可以mmap 原始(压缩)数据,然后使用zlib.decompressobj 进行延迟解压缩,将来自mmap 对象的数据提供给它。它根本不会给你带来任何好处(你仍然需要从头到尾解压缩,gzip.open 的类文件对象会在幕后为你做这些),所以我不打算详细说明这个过程;它不会加速 I/O,也不会节省内存,除非您从头开始重写 JSON 解析等。只是不要这样做。
    • 附加说明:如果您可以在使用前打开文件并做一些工作,您可能using os.posix_fadvise 中受益打开后原始 fd 上的 SEQUENTIALWILLNEED 标志。这不太重要(通过顺序读取文件得到了很好的优化),但如果在打开和使用之间存在延迟,后台的非阻塞批量读取WILLNEED 触发器可能会导致最终的read 调用加载JSON 运行得更快一些(解压缩工作限制了收益)。
    • @ShadowRanger 非常感谢您的详细解释!!。这帮助很大
    猜你喜欢
    • 1970-01-01
    • 2023-03-06
    • 2019-07-08
    • 2014-05-24
    • 2015-05-20
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    • 2018-04-03
    相关资源
    最近更新 更多