当前位置: 首页 > 面试题库 >

以块为单位处理比物理内存大得多的数据

宗政欣可
2023-03-14
问题内容

我需要处理一些比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,而无需保留任何上下文。例如,如果您要将数据馈送到渐进式解码器或哈希器等中,那是完美的。

但是,如果您需要一次处理一个“块”,那么您的块最终可能会跨越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]

这种代码在网络中非常常见(因为sockets 不能 只是“全部读取”,因此您 总是
必须读入缓冲区并分块放入消息中),因此在网络代码中可能会找到一些有用的示例,这些示例使用类似的协议为您的文件格式。

或者,您可以使用mmap

如果您的虚拟内存大小大于文件大小,那么这很简单:

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

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

如果您尝试读取的文件太大而无法容纳您的虚拟内存大小(例如,具有32位Python的4GB文件或具有64位Python的20EB文件),那么只有在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)

当然,如果需要定界,则映射窗口与读取块具有相同的问题,并且可以用相同的方法解决。

但是,作为一种优化,而不是缓冲,您可以将窗口向前滑动到包含最后一条完整消息末尾的页面,而不是一次滑动8MB,然后可以避免任何复制。这有点复杂,因此,如果要执行此操作,请搜索“滑动mmap窗口”之类的内容,并在遇到问题时写一个新问题。



 类似资料:
  • 以页为单位管理物理内存 在获得可用物理内存范围后,系统需要建立相应的数据结构来管理以物理页(按4KB对齐,且大小为4KB的物理内存单元)为最小单位的整个物理内存,以配合后续涉及的分页管理机制。每个物理页可以用一个 Page数据结构来表示。由于一个物理页需要占用一个Page结构的空间,Page结构在设计时须尽可能小,以减少对内存的占用。Page的定义在kern/mm/memlayout.h中。以页为

  • 物理内存管理 接下来将首先对实验的执行流程做个介绍,并进一步介绍如何探测物理内存的大小与布局,如何以页为单位来管理计算机系统中的物理内存,如何设计物理内存页的分配算法,最后比较详细地分析了在80386的段页式硬件机制下,ucore操作系统把段式内存管理的功能弱化,并实现以分页为主的页式内存管理的过程。

  • 物理内存管理 物理页 通常,我们在分配物理内存时并不是以字节为单位,而是以一物理页(Frame),即连续的 4 KB 字节为单位分配。我们希望用物理页号(Physical Page Number,PPN)来代表一物理页,实际上代表物理地址范围在 [PPN×4KB,(PPN+1)×4KB)[\text{PPN}\times 4\text{KB},(\text{PPN}+1)\times 4\text

  • 问题内容: 我用来并行化一些繁重的计算。 目标函数返回大量数据(庞大的列表)。我的RAM用完了。 如果不使用,我只需将生成的元素依次计算出来,就将目标函数更改为生成器。 我了解多处理不支持生成器- 它等待整个输出并立即返回,对吗?没有屈服。有没有一种方法可以使工作人员在数据可用时立即产生数据,而无需在RAM中构造整个结果数组? 简单的例子: 这是Python 2.7。 问题答案: 这听起来像是队列

  • 归功于 GRUB ,在启动后,内核可以知道可用物理内存的大小。 在实现操作系统时,前 8Mb 字节的物理内存将被内核保留使用,这些内存被用来存放: The kernel 内核 GDT, IDT et TSS Kernel Stack 内核栈 Some space reserved to hardware (video memory, ...) 保留硬件所需空间 Page directory and

  • 在与 GDT 相关的章节中,我们知道分段物理内存地址使用的是段选择和计算偏移(Linux在X86上的虚拟内存管理) 在本章中,我们将实现内存的分页功能,其原理是将分段的线性地址转换成物理地址(分页表存储了虚拟(线性)地址到物理地址间的映射)。 为什么我们需要分页管理内存? 内存分页将允许我们的内核: 为避免歧义,保留部分原文 use the hard-drive as a memory and n