2011-05-18 57 views
10

我想知道是否有人可能知道下面的答案。Python内存序列化

我正在使用Python构建一个基于字符的后缀树。树中有超过1100万个节点,可以容纳大约3GB的内存。通过使用插槽类方法而不是Dict方法,这从7GB降低。

当我序列化树(使用最高协议)时,生成的文件小了一百多倍。

当我重新加载酸洗文件时,它又消耗了3GB的内存。这些额外开销从哪里来,是否与Pythons处理内存引用类实例有关?

更新

谢谢larsmans和Gurgeh你非常有帮助的解释和建议。我使用树作为文本语料库上信息检索界面的一部分。

我最初将孩子(最多30个)作为Numpy数组存储,然后尝试硬件版本(ctypes.py_object*30),Python数组(ArrayType)以及字典和Set类型。

列表似乎做得更好(使用guppy来描述内存,并__slots__['variable',...]),但我仍然试图压扁它,如果我可以多一点。我对阵列的唯一问题是不得不事先指定它们的大小,这导致了只有一个孩子的节点有点冗余,而且我有相当多的问题。 ;-)

构建树之后,我打算用第二遍将它转换为概率树,但也可能是我可以在树构建时做到这一点。由于构建时间对我而言并不重要,因此array.array()听起来像是一些有用的尝试,感谢提示,非常感谢。

我会让你知道它是怎么回事。

回答

9

如果您尝试腌制空列表,您可以:

>>> s = StringIO() 
>>> pickle.dump([], s) 
>>> s.getvalue() 
'(l.' 

,同样'(d.'一个空dict。这是三个字节。的in-memory representation of a list,然而,包含

  • 参考计数
  • 一类型ID,在包含的指针类型名称和簿记信息对存储器分配
  • 的指针的指针的实际元件的载体转
  • 还有更多的簿记信息。

在我的机器上,它有64位指针,sizeof Python列表头对象是40个字节,所以这是一个数量级。我假设一个空的dict将有相似的大小。

然后,既listdict使用过度分配策略,以获得amortized O(1) performance他们的主营业务,malloc介绍的开销,还有排列,成员属性,您可能会或可能不会甚至是让你的第二个知道的以及其他各种因素数量级。

总结:泡菜是Python对象:)一个不错的压缩算法

+0

我对Pickle留下了深刻的印象,甚至还有可能使用pickletools优化功能将文件大小再缩小25%。 Pickle是如此高效。 :-) – Martyn 2011-06-14 00:20:37

3

你建立你的树一次,然后用它无需进一步修改呢?在这种情况下,您可能需要考虑为动态构建和静态用法使用单独的结构。

指令和对象非常适合动态修改,但它们在只读场景中不是非常节省空间。我不知道你使用后缀树是什么,但你可以让每个节点由一个有序数组array.array('c')的2元组和一个等长的子节点元组(代​​替一个元组的矢量以避免重新定位)。使用数组中的二等分模块遍历树进行查找。数组中字符的索引将对应于子节点元组中的子节点。这样你可以避免字典,对象和矢量。

您可以在构建过程中做类似的事情,可能使用子节点向量而不是子节点元组。但是,这当然会使构造变慢,因为在排序后的向量中插入新节点是O(N)。

+1

动态和静态结构之间的这种差异也是数据在磁盘上如此小的原因。它被存储为一个紧凑的静态结构。想象一下,如果每次在该块中间的某个位置添加节点,速度会有多慢。 – Gurgeh 2011-05-18 14:42:18