2011-05-18 59 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