gpt4 book ai didi

python - 以 block 的形式处理比物理内存大得多的数据

转载 作者:太空狗 更新时间:2023-10-29 19:33:45 24 4
gpt4 key购买 nike

我需要处理一些比 RAM 大几百倍的数据。我想读一大块,处理它,保存结果,释放内存并重复。有没有办法在 Python 中提高效率?

最佳答案

总的来说关键是你要迭代处理文件。

如果您只是处理一个文本文件,这很简单:for line in f: 一次只读取一行。 (实际上它缓冲了东西,但缓冲区足够小,你不必担心。)

如果你正在处理一些其他特定的文件类型,比如 numpy 二进制文件、CSV 文件、XML 文档等,通常有类似的专用解决方案,但没有人可以向你描述它们,除非你告诉我们你有什么样的数据。

但是如果你有一个通用的二进制文件呢?


首先,read方法需要一个可选的最大字节数来读取。所以,而不是这个:

data = f.read()
process(data)

你可以这样做:

while True:
data = f.read(8192)
if not data:
break
process(data)

您可能想编写这样的函数:

def chunks(f):
while True:
data = f.read(8192)
if not data:
break
yield data

那么你可以这样做:

for chunk in chunks(f):
process(chunk)

您也可以使用双参数 iter 来完成此操作,但很多人觉得这有点晦涩:

for chunk in iter(partial(f.read, 8192), b''):
process(chunk)

无论哪种方式,此选项都适用于下面的所有其他变体(单个 mmap 除外,它非常琐碎,没有意义)。


那里的数字 8192 没有什么神奇之处。您通常需要 2 的幂,最好是系统页面大小的倍数。除此之外,无论您使用的是 4KB 还是 4MB,您的性能都不会发生太大变化 — 如果发生变化,您将必须测试哪种方式最适合您的用例。


无论如何,这假设您可以一次处理每个 8K,而无需保留任何上下文。例如,如果您要将数据输入渐进式解码器或哈希器或其他东西,那就太完美了。

但是如果您需要一次处理一个“ block ”,您的 block 最终可能会跨越 8K 边界。你是如何处理的?

这取决于文件中 block 的分隔方式,但基本思想非常简单。例如,假设您使用 NUL 字节作为分隔符(不太可能,但作为玩具示例很容易展示)。

data = b''
while True:
buf = f.read(8192)
if not buf:
process(data)
break
data += buf
chunks = data.split(b'\0')
for chunk in chunks[:-1]:
process(chunk)
data = chunks[-1]

这种代码在网络中很常见(因为套接字 不能只是“读取所有”,所以你总是必须读入缓冲区并分 block 放入消息中),因此您可能会在使用与您的文件格式类似的协议(protocol)的网络代码中找到一些有用的示例。


或者,您可以使用 mmap .

如果您的虚拟内存大小大于文件,这是微不足道的:

with mmap.mmap(f.fileno(), access=mmap.ACCESS_READ) as m:
process(m)

现在 m 就像一个巨大的 bytes 对象,就像您调用 read() 将整个对象读入内存一样— 但操作系统会根据需要自动将位分页进出内存。


如果您尝试读取一个太大而无法容纳您的虚拟内存大小的文件(例如,一个 4GB 的 32 位 Python 文件,或一个 20EB 的 64 位 Python 文件——这只可能发生在2013 如果你正在读取一个稀疏或虚拟文件,比如 Linux 上另一个进程的 VM 文件),你必须实现窗口化——一次在一个文件中进行 mmap。例如:

windowsize = 8*1024*1024
size = os.fstat(f.fileno()).st_size
for start in range(0, size, window size):
with mmap.mmap(f.fileno(), access=mmap.ACCESS_READ,
length=windowsize, offset=start) as m:
process(m)

当然映射窗口和读取 block 一样的问题,如果你需要分隔东西,你可以用同样的方法解决。

但是,作为一种优化,您可以将窗口向前滑动到包含最后一条完整消息末尾的页面而不是缓冲,而不是一次 8MB,这样您就可以避免任何复制。这有点复杂,所以如果你想这样做,搜索“滑动 mmap 窗口”之类的东西,如果你卡住了,写一个新问题。

关于python - 以 block 的形式处理比物理内存大得多的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17710748/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com