为什么Python的mmap不适用于大文件?

Sco*_*ths 44 python memory performance mmap

[编辑:此问题仅适用于32位系统.如果你的计算机,你的操作系统和你的python实现是64位的,那么mmap-ing巨大的文件可以正常工作并且非常高效.

我正在编写一个模块,其中包括允许按位读取文件访问.这些文件可能很大(数百GB),所以我编写了一个简单的类,让我像处理字符串一样处理文件并隐藏所有的搜索和阅读.

当我写我的包装类时,我不知道mmap模块.在阅读mmap的文档时,我认为"很棒 - 这正是我所需要的,我将取出我的代码并用mmap替换它.它可能效率更高,删除代码总是好的."

问题是mmap不适用于大文件!这对我来说非常令人惊讶,因为我认为它可能是最明显的应用程序.如果文件超过几千兆字节,那么我得到一个EnvironmentError: [Errno 12] Cannot allocate memory.这只发生在32位Python构建中,所以它似乎耗尽了地址空间,但我找不到任何关于此的文档.

我的代码就是

f = open('somelargefile', 'rb')
map = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Run Code Online (Sandbox Code Playgroud)

所以我的问题是我错过了一些明显的东西吗?有没有办法让mmap可以在大文件上移植,或者我应该回到我天真的文件包装器?


更新:似乎有一种感觉,Python mmap应该具有与POSIX mmap相同的限制.为了更好地表达我的挫败感,这是一个简单的类,它具有mmap的一小部分功能.

import os

class Mmap(object):
    def __init__(self, f):
        """Initialise with a file object."""
        self.source = f

    def __getitem__(self, key):
        try:
            # A slice
            self.source.seek(key.start, os.SEEK_SET)
            return self.source.read(key.stop - key.start)
        except AttributeError:
            # single element
            self.source.seek(key, os.SEEK_SET)
            return self.source.read(1)
Run Code Online (Sandbox Code Playgroud)

它是只读的,并没有做任何花哨的事情,但我可以像使用mmap一样:

map2 = Mmap(f)
print map2[0:10]
print map2[10000000000:10000000010]
Run Code Online (Sandbox Code Playgroud)

除了文件大小没有限制.真的不太难......

Nic*_*tin 37

来自IEEE 1003.1:

mmap()函数应在进程的地址空间与文件,共享内存对象或[TYM]类型的内存对象之间建立映射.

它需要所有虚拟地址空间,因为这正是mmap() 它所做的.

事实上它并没有真正耗尽内存并不重要 - 你不能映射比你可用的地址空间更多的地址空间.既然你再取结果和访问,就好像它记忆,究竟你打算访问超过2 ^ 32字节到文件?即使mmap()没有失败,你仍然只能在32位地址空间中用完空间之前读取前4GB.当然,您可以mmap()在文件上滑动32位窗口,但这并不一定能为您带来任何好处,除非您可以优化访问模式,以便限制访问以前窗口的次数.

  • POSIX mmap规范*绝对*相关.Python mmap模块的重点是让您直接访问操作系统的mmap,允许hw指针访问文件数据,就好像它是内存一样.如果您想要更多便利,请使用Python库中的许多其他IO相关模块或任何其他语言.否则,您需要忍受底层操作系统和CPU虚拟内存架构的约束. (20认同)
  • Windows实现POSIX api调用.POSIX mmap在Windows上和在Linux上做同样的事情:它将文件映射到虚拟地址空间. (2认同)
  • 如果您还没有,请阅读http://en.wikipedia.org/wiki/Mmap并注意有关Windows MapViewOfFile的说明; 查看python Modules/mmapmodule.c的代码,这就是它在Windows上的用法.BTW,欢迎来自bugs.python.org的改进Python文档的建议. (2认同)

Sco*_*ths 17

很抱歉回答我自己的问题,但我认为我遇到的真正问题是没有意识到mmap是一个标准的POSIX系统调用,具有特定的特征和限制,并且Python mmap应该只是为了公开它的功能.

Python文档没有提到POSIX mmap,所以如果你作为一个Python程序员而不太了解POSIX(正如我所做的那样),那么地址空间问题就显得非常随意且设计糟糕了!

感谢其他海报教我mmap的真正含义.不幸的是,没有人建议我将手工制作的类更好地替换为将大文件作为字符串处理,所以我现在必须坚持使用它.也许我会清理它,并在我有机会时将其作为我模块的公共界面的一部分.

  • 在我看来,您手工制作的课程非常适合您的需求.没有强制使用不合适的机制只是因为它们是环境的一部分.感谢您分享学习经历.你让我免于重新发明同样的问题. (8认同)

Mar*_*som 16

32位程序和操作系统最多只能处理32位内存,即4GB.还有其他因素使总数更小; 例如,Windows为硬件访问保留0.5到2GB之间,当然你的程序也会占用一些空间.

编辑:您缺少的显而易见的事情是在任何操作系统上理解mmap的机制.它允许您将文件的一部分映射到一系列内存 - 一旦完成,就可以以尽可能少的开销访问文件的该部分.它的开销很低,因为映射只执行一次,并且每次访问不同的范围时都不必更改.缺点是您需要一个开放的地址范围,足以容纳您尝试映射的部分.如果您要一次映射整个文件,则需要在内存映射中有一个大小足以适合整个文件的漏洞.如果这样的洞不存在,或者比整个地址空间大,那么它就会失败.

  • 典型的mmap实现将保留您要映射的对象的地址空间.如果无法进行映射 - 例如,由于没有足够的空间来映射请求的大小,mmap将失败.在您访问之前,mmap实际上不会读取整个内容.但它会尝试创建地址空间映射. (4认同)

Sin*_*ion 9

mmap模块提供了在大文件中浏览所需的所有工具,但由于其他人提到的限制,您无法一次性映射所有这些工具.您可以一次映射一个大小合适的块,进行一些处理,然后取消映射并映射另一个.mmap该类的关键参数是lengthoffset,它们听起来完全正确,允许您映射length字节,从offset映射文件中的字节开始.只要您希望读取映射窗口之外的内存部分,就必须在新窗口中进行映射.


mor*_*lli 6

您缺少的一点是mmap是一种内存映射函数,它将文件映射到内存中,以便通过任何方式跨请求的数据范围进行任意访问.

您正在寻找的内容听起来更像某种数据窗口类,它提供了一个api,允许您随时查看大型数据结构的小窗口.除了通过调用数据窗口自己的api之外,访问超出此窗口范围是不可能的.

这很好,但它不是一个内存映射,它提供了更广泛的数据范围的优势,代价是更具限制性的api.