Python:如何内存映射(mmap)gzip 压缩文件

skp*_*skp 0 python gzip mmap python-3.x

我的要求是读取 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())
Run Code Online (Sandbox Code Playgroud)

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

b'\x1f\x8b
Run Code Online (Sandbox Code Playgroud)

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

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

Sha*_*ger 5

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)
Run Code Online (Sandbox Code Playgroud)

如果您的文件采用随机访问有意义的格式,您仍然无法直接使用 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
Run Code Online (Sandbox Code Playgroud)

在实践中,如果您会经常重复使用这个文件,我建议只将其存储为未压缩的,以避免每次使用前都解压缩,但我想我会演示如何使类似的东西工作,以防您真正使用情况需要它。