我需要处理一些比RAM大几百倍的数据.我想在大块中读取,处理它,保存结果,释放内存并重复.有没有办法在python中提高效率?
aba*_*ert 24
一般关键是您希望迭代地处理文件.
如果您只是处理文本文件,这是微不足道的:一次for line in f:只能读取一行.(实际上它缓冲了一些东西,但缓冲区足够小,你不必担心它.)
如果您正在处理其他特定文件类型,如numpy二进制文件,CSV文件,XML文档等,通常会有类似的专用解决方案,但除非您告诉我们什么,否则没有人可以向您描述它们你有一些数据.
但是如果你有一个通用的二进制文件呢?
首先,该read方法需要一个可选的最大字节来读取.所以,而不是这个:
data = f.read()
process(data)
Run Code Online (Sandbox Code Playgroud)
你可以这样做:
while True:
data = f.read(8192)
if not data:
break
process(data)
Run Code Online (Sandbox Code Playgroud)
你可能想要写一个这样的函数:
def chunks(f):
while True:
data = f.read(8192)
if not data:
break
yield data
Run Code Online (Sandbox Code Playgroud)
然后你可以这样做:
for chunk in chunks(f):
process(chunk)
Run Code Online (Sandbox Code Playgroud)
你也可以用两个参数做到这一点iter,但很多人发现有点模糊:
for chunk in iter(partial(f.read, 8192), b''):
process(chunk)
Run Code Online (Sandbox Code Playgroud)
无论哪种方式,此选项适用于下面的所有其他变体(除了单个mmap,这是微不足道的,没有任何意义).
那里的数字8192没有什么神奇之处.您通常需要2的幂,理想情况下是系统页面大小的倍数.除此之外,无论您使用的是4KB还是4MB,您的性能都不会有太大变化 - 如果是这样,您必须测试最适合您用例的内容.
无论如何,这假设您可以一次处理每个8K而不保持任何上下文.如果你是,例如,将数据馈送到渐进式解码器或者其他东西,这是完美的.
但是如果你需要一次处理一个"块",你的块可能最终跨越8K边界.你怎么处理那件事呢?
这取决于你的文件在文件中的分隔方式,但基本的想法非常简单.例如,假设您使用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]
Run Code Online (Sandbox Code Playgroud)
这种代码在网络中非常常见(因为sockets 不能只是"全部读取",所以你总是必须读入缓冲区并将其读入消息中),因此您可能会在使用类似协议的网络代码中找到一些有用的示例到你的文件格式.
或者,您可以使用mmap.
如果您的虚拟内存大小大于文件,这是微不足道的:
with mmap.mmap(f.fileno(), access=mmap.ACCESS_READ) as m:
process(m)
Run Code Online (Sandbox Code Playgroud)
现在m就像一个巨大的bytes对象,就像你打电话read()将整个内容读入内存一样 - 但是操作系统会根据需要自动将内存和内存分页.
如果你正在尝试读取一个太大而不适合你的虚拟内存大小的文件(例如,一个带有32位Python的4GB文件,或一个带有64位Python的20EB文件 - 这种情况很可能在2013年发生,如果你你正在读取一个稀疏的或虚拟的文件,例如Linux上另一个进程的VM文件,你必须一次在一个文件中实现windowing-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)
Run Code Online (Sandbox Code Playgroud)
当然,如果你需要分割内容,映射窗口与读取块有相同的问题,你可以用同样的方法解决它.
但是,作为优化而不是缓冲,您只需将窗口向前滑动到包含最后一条完整邮件末尾的页面,而不是一次只有8MB,然后您就可以避免任何复制.这有点复杂,所以如果你想这样做,搜索"滑动mmap窗口"之类的东西,如果你遇到困难就写一个新问题.
| 归档时间: |
|
| 查看次数: |
5969 次 |
| 最近记录: |