加载pickle python对象会占用大量内存

Rom*_*uin 5 python memory pickle

我有一个python的pickle对象,它生成一个180 Mb的文件.当我取消它时,内存使用量会爆炸到2或3Gb.你有类似的经历吗?这是正常的吗?

对象是包含字典的树:每个边是一个字母,每个节点都是一个潜在的单词.因此,要存储一个单词,您需要的边数与该单词的长度一样多.所以,第一级是最多26个节点,第二个是26 ^ 2,第三个是26 ^ 3等...对于每个节点都是一个单词我有一个属性指向关于单词的信息(动词,名词,定义等...).

我有最多约40个字符的单词.我有大约五十万条款.一切顺利,直到我腌制(使用简单的cpickle转储):它提供180 Mb文件.我在Mac OS上,当我取消这些180 Mb时,操作系统会给python进程提供2或3 Gb的"内存/虚拟内存":(

我没有在这棵树上看到任何递归:边缘的节点本身就是一个数组数组.不涉及递归.

我有点卡住:这些180 Mb的加载大约是20秒(没有谈到内存问题).我不得不说我的CPU不是那么快:核心i5,1.3Ghz.但我的硬盘是ssd.我只有4Gb的内存.

要在我的树中添加这500 000个单词,我会阅读大约7 000个文件,每个文件包含大约100个单词.这个读取使得mac os分配的内存高达15 Gb,主要是在虚拟内存上:(我一直在使用"with"语句确保关闭每个文件,但实际上并没有帮助.阅读文件带走对于40 Ko,0.2秒.对我来说似乎很长.将它添加到树上要快得多(0.002秒).

最后我想创建一个对象数据库,但我猜python并不适合.也许我会去MongoDB :(

class Trie():
    """
    Class to store known entities / word / verbs...
    """
    longest_word  = -1
    nb_entree     = 0

    def __init__(self):
        self.children    = {}
        self.isWord      = False
        self.infos       =[]

    def add(self, orthographe, entree):  
        """
        Store a string with the given type and definition in the Trie structure.
        """
        if len(orthographe) >Trie.longest_word:
            Trie.longest_word = len(orthographe)

        if len(orthographe)==0:
            self.isWord = True
            self.infos.append(entree)
            Trie.nb_entree += 1
            return True

        car = orthographe[0]
        if car not in self.children.keys():
            self.children[car] = Trie()

        self.children[car].add(orthographe[1:], entree)
Run Code Online (Sandbox Code Playgroud)

nne*_*neo 4

Python 对象,特别是在 64 位机器上,非常大。当 pickle 时,对象会获得非常紧凑的表示形式,适合磁盘文件。这是一个拆解的 pickle 的示例:

>>> pickle.dumps({'x':'y','z':{'x':'y'}},-1)
'\x80\x02}q\x00(U\x01xq\x01U\x01yq\x02U\x01zq\x03}q\x04h\x01h\x02su.'
>>> pickletools.dis(_)
    0: \x80 PROTO      2
    2: }    EMPTY_DICT
    3: q    BINPUT     0
    5: (    MARK
    6: U        SHORT_BINSTRING 'x'
    9: q        BINPUT     1
   11: U        SHORT_BINSTRING 'y'
   14: q        BINPUT     2
   16: U        SHORT_BINSTRING 'z'
   19: q        BINPUT     3
   21: }        EMPTY_DICT
   22: q        BINPUT     4
   24: h        BINGET     1
   26: h        BINGET     2
   28: s        SETITEM
   29: u        SETITEMS   (MARK at 5)
   30: .    STOP
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,它非常紧凑。如果可能的话,什么都不重复。

然而,在内存中,对象由相当数量的指针组成。我们来问Python一个空字典有多大(64位机器):

>>> {}.__sizeof__()
248
Run Code Online (Sandbox Code Playgroud)

哇!空字典248 字节!请注意,字典预先分配了最多可容纳八个元素的空间。但是,即使字典中有一个元素,您也会付出相同的内存成本。

类实例包含一个字典来保存实例变量。您的尝试为孩子们准备了一本额外的字典。因此,每个实例花费近 500 字节。通过估计 2-4 百万个 Trie 对象,您可以轻松查看内存使用情况的来源。


__slots__您可以通过向 Trie添加 a 来消除实例字典来稍微缓解这一问题。通过这样做,您可能会节省大约 750MB(我的猜测)。它会阻止您向 Trie 添加更多变量,但这可能不是一个大问题。