我需要处理一些比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