2011-12-15 61 views
18

我正在實現一個需要序列化和反序列化大對象的程序,所以我正在用pickle,cPicklemarshal模塊進行一些測試以選擇最佳模塊。一路上我發現了一些非常有趣的東西:元帥轉儲更快,cPickle加載速度更快

我在使用dumps,然後loads(對於每個模塊)列表中的字典,元組,int,float和字符串。

這是我的標杆輸出:

DUMPING a list of length 7340032 
---------------------------------------------------------------------- 
pickle => 14.675 seconds 
length of pickle serialized string: 31457430 

cPickle => 2.619 seconds 
length of cPickle serialized string: 31457457 

marshal => 0.991 seconds 
length of marshal serialized string: 117440540 

LOADING a list of length: 7340032 
---------------------------------------------------------------------- 
pickle => 13.768 seconds 
(same length?) 7340032 == 7340032 

cPickle => 2.038 seconds 
(same length?) 7340032 == 7340032 

marshal => 6.378 seconds 
(same length?) 7340032 == 7340032 

因此,從這些結果中我們可以看出,marshal是在傾銷的基準部分極快:

14.8x比pickle快了2.6倍,比cPickle快了2.6倍。

但是,對於我很大的驚喜,marshal是遠遠超過cPickle慢於裝載部分:快

2.2倍倍pickle,但比cPickle慢3.1X倍。

至於RAM,marshal性能的同時,加載也非常低效:

Ubuntu System Monitor

我猜爲什麼加載與marshal是如此之慢是某種與長度相關的原因的序列化字符串(遠遠長於picklecPickle)。

  • 爲什麼marshal轉儲速度更快,加載速度更慢?
  • 爲什麼marshal序列化的字符串很長?
  • 爲什麼marshal的加載在RAM中如此低效?
  • 有沒有辦法改善marshal的加載性能?
  • 有沒有辦法合併marshal快速傾銷與cPickle快速加載?
+0

downvoter,小心分享? – juliomalegria

+3

你的問題是死路一條。 'marshal'模塊並不是用來替代'pickle'的。沒有關於編組文件格式的官方文檔,它可能因版本而異,因此您的基準測試結果可能在未來是錯誤的。 –

+0

關於速度差異:我懷疑它是關於文件IO的:文件產生的文件是近四倍(112MB vs 30MB)。 –

回答

18

cPickle有一個比marshal更聰明的算法,並能夠做一些技巧來減少大對象使用的空間。這意味着解碼速度會更慢,但編碼速度更快,因爲輸出結果更小。 marshal是過分簡單的,並且直接將對象序列化,而不進行任何進一步的分析。這也解釋了爲什麼marshal加載效率非常低,只需要做更多的工作 - 就像從磁盤讀取更多數據一樣 - 能夠做到與cPickle一樣的功能。因爲快速保存意味着分析數據結構的次數越來越少,這意味着將大量數據保存到磁盤上,所以您不能真正得到快速保存和快速加載。

關於事實marshal可能不兼容到Python的其他版本,你通常應該使用cPickle

「這不是一般的‘持久性’模塊對於一般的持久性和Python的傳輸對象通過。 RPC調用,請參閱pickle和shelve模塊,marshal模塊主要用於支持讀取和編寫.pyc文件的Python模塊的「僞編譯」代碼,因此,Python維護者有權修改marshal格式的後向如果需要序列化和反序列化Python對象,請改用pickle模塊 - 性能爲c無與倫比的版本獨立性得到保證,而醃菜支持的範圍更廣泛的對象不是元帥。「 (the python docs about marshal

3

正如你可以看到,通過cPickle.dump產生的輸出具有由marshal.dump產生的輸出的長度的約四分之一。這意味着cPickle必須使用更復雜的算法來轉儲數據,因爲不必要的事情被刪除。當加載轉儲列表時,marshal必須處理更多的數據,而cPickle可以快速處理其數據,因爲需要分析的數據更少。

關於事實marshal可能不兼容到Python的其他版本,你通常應該使用cPickle

「這不是一般的‘持久性’模塊對於一般的持久性和Python的傳輸對象通過。 RPC調用,請參閱pickle和shelve模塊,marshal模塊主要用於支持讀取和編寫.pyc文件的Python模塊的「僞編譯」代碼,因此,Python維護者有權修改marshal格式的後向如果需要序列化和反序列化Python對象,請改用pickle模塊 - 性能爲c無與倫比的版本獨立性得到保證,而醃菜支持的範圍更廣泛的對象不是元帥。「 (the python docs about marshal

9

這些基準之間的差異給出了加快cPickle的一個想法:

Input: ["This is a string of 33 characters" for _ in xrange(1000000)] 
cPickle dumps 0.199 s loads 0.099 s 2002041 bytes 
marshal dumps 0.368 s loads 0.138 s 38000005 bytes 

Input: ["This is a string of 33 "+"characters" for _ in xrange(1000000)] 
cPickle dumps 1.374 s loads 0.550 s 40001244 bytes 
marshal dumps 0.361 s loads 0.141 s 38000005 bytes 

在第一種情況下,列表重複相同的字符串。第二個列表是等價的,但每個字符串都是一個單獨的對象,因爲它是表達式的結果。現在,如果您最初從外部來源讀取數據,則可以考慮某種字符串重複數據消除。

11

有些人可能認爲這太過分了,但通過簡單地使用gc.disable()和gc.enable()來包裝pickle dump調用,我獲得了巨大的成功。例如,下面編寫一個〜50MB字典列表的字典從78秒變爲4.

# not a complete example.... 
gc.disable() 
cPickle.dump(params,fout,cPickle.HIGHEST_PROTOCOL)   
fout.close()    
gc.enable() 
+2

哇,這真的有效......但是什麼影響? – tdc

+0

這工作完美!所需時間總計也減少了20倍。雖然@克里斯,你能否指出我們有什麼影響(如果有的話)? –

+0

@tdc,Tejas,您將無法再轉儲非循環對象,例如'x = in'x = [];如果啓用Pickler.fast,則x.append(x)'將導致ValueError。 – kay

5

您可以製作cPickle cca。通過創建cPickle的實例可以更快速度提高50倍(!)。皮克勒,然後設置選項無證「快」,以1:

outfile = open('outfile.pickle') 
fastPickler = cPickle.Pickler(outfile, cPickle.HIGHEST_PROTOCOL) 
fastPickler.fast = 1 
fastPickler.dump(myHugeObject) 
outfile.close() 

但是如果你的myHugeObject具有循環引用,轉儲方法永遠不會結束。

+0

有用瞭解!它是否也使「加載」速度更快? – juliomalegria

+0

我不這麼認爲,快速選項僅在酸洗數據時禁用重複性子對象檢測。您可以在Python 3系列文檔(http://docs.python.org/3/library/pickle.html?highlight=pickle#pickle.Pickler.fast)中找到更多信息,當然也可以在代碼 –

3

您可以通過壓縮序列化結果來提高存儲效率。

我的預感是,壓縮數據並將其送入反序列化比通過硬盤讀原始數據更快。

以下的測試證明壓縮會加速非串行化過程。 由於該機器配備SSD,結果並不如預期。 在HHD設備上,使用lz4壓縮數據的速度會更快,因爲從60-70mb/s的平均讀取磁盤。

LZ4:在減速18%時,壓縮產量爲額外存儲的77.6%。

marshal - compression speed time 
Bz2 7.492605924606323 10363490 
Lz4 1.3733329772949219 46018121 
--- 1.126852035522461 205618472 
cPickle - compression speed time 
Bz2 15.488649845123291 10650522 
Lz4 9.192650079727173 55388264 
--- 8.839831113815308 204340701 
+0

中找到有趣的結果!你是否暗示你以某種方式避免了在反序列化之前解壓數據?如果是這樣,怎麼樣? – seaotternerd