2011-12-15 17 views
18

Wdrażam program, który musi serializować i deserializować duże obiekty, więc robiłem testy z pickle, cPickle i marshal modułów, aby wybrać najlepszy moduł. Po drodze znalazłem coś bardzo interesującego:marszałek rzuca szybciej, cPickle ładuje się szybciej

Używam dumps, a następnie loads (dla każdego modułu) na liście dykt, krotek, int, float i stringów.

Jest to wyjście z mojego odniesienia:

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 

Tak, z tych wyników możemy zobaczyć, że marshal był bardzo szybki w dumping część benchmarku:

14.8x razy szybszy niż pickle i 2,6x razy szybszy niż cPickle.

Ale dla mojego wielkiego zaskoczenia, marshal był zdecydowanie wolniejszy niż cPickle w załadunku części:

2.2x razy szybciej niż pickle, ale 3.1x razy wolniej niż cPickle.

A co do pamięci RAM, marshal wydajność podczas loading był również bardzo nieefektywne:

Ubuntu System Monitor

Zgaduję dlaczego ładowanie z marshal jest tak powolny, jest w jakiś sposób związany z długością serializowanego ciągu znaków (znacznie dłuższego niż pickle i cPickle).

  • Dlaczego marshal zrzuca szybciej i ładuje wolniej?
  • Dlaczego serializowany ciąg znaków marshal jest tak długi?
  • Dlaczego ładowanie marshal jest tak niewydajne w pamięci RAM?
  • Czy istnieje sposób na poprawę wydajności ładowania marshal?
  • Czy istnieje sposób na szybkie połączenie z marshal szybkim ładowaniem z cPickle?
+0

downvoter, chcesz się podzielić? – juliomalegria

+3

Twoje pytanie jest ślepym zaułkiem. Moduł 'marshal' nie jest używany jako alternatywa dla' pickle'. Nie ma oficjalnej dokumentacji dla formatu pliku marshmala i może ona zmieniać się z wersji na wersję, więc wyniki testu porównawczego mogą być fałszywe w przyszłości. –

+0

Jeśli chodzi o różnice prędkości: podejrzewam, że wszystko zależy od pliku IO: Plik wyprodukowany przez marszałka jest prawie czterokrotnie większy (112 MB vs. 30 MB). –

Odpowiedz

18

cPickle ma inteligentniejszy algorytm niż marshal i jest w stanie wykonać sztuczki, aby zmniejszyć przestrzeń wykorzystywaną przez duże obiekty. Oznacza to, że będzie wolniej dekodować, ale szybciej będzie kodować, ponieważ wynikowy wynik będzie mniejszy. marshal jest uproszczona i serializuje obiekt bez zmian, bez dalszej analizy. To również odpowiada, dlaczego ładowanie marshal jest tak nieefektywne, że po prostu musi wykonać więcej pracy - jak podczas odczytu większej ilości danych z dysku - aby móc zrobić to samo, co cPickle.

marshal i cPickle są naprawdę różne rzeczy, w końcu, tak naprawdę nie można dostać zarówno szybki oszczędności i szybki załadunek ponieważ szybko oszczędność implikuje analizowanie struktur danych mniej co oznacza oszczędność dużo danych na dysku.

chodzi o fakt, że marshal mogą być niezgodne z innymi wersjami Pythona, należy zazwyczaj używają cPickle:

„To nie jest ogólnym«wytrwałość moduł»Dla ogólnej trwałości i transferu Python obiektów wskroś. Wywołania RPC, zobacz moduły marynarki i półki Moduł marszałkowski istnieje głównie w celu obsługi odczytu i zapisu "pseudo-skompilowanego" kodu dla modułów Pythona plików .pyc, dlatego też opiekunowie Pythona zastrzegają sobie prawo do modyfikacji formatu marszałka w tył W razie potrzeby, w przypadku serializacji i deklasyfikacji obiektów w języku Python, należy użyć modułu pikle - wydajność jest równa nieporównywalna, niezależność wersji jest gwarantowana, a pikle obsługuje znacznie szerszy zakres obiektów niż marszałek. " (the python docs about marshal)

3

Jak widać, wynik wyprodukowany przez cPickle.dump ma około 1/4 długości wyjścia produkowanego przez marshal.dump. Oznacza to, że w celu zrzucenia danych konieczne jest użycie bardziej skomplikowanego algorytmu, gdy usuwane są niepotrzebne elementy. Podczas ładowania zrzuconej listy, marshal musi pracować przez znacznie więcej danych, podczas gdy cPickle może szybko przetwarzać dane, ponieważ jest mniej danych, które muszą zostać przeanalizowane.

chodzi o fakt, że marshal mogą być niezgodne z innymi wersjami Pythona, należy zazwyczaj używają cPickle:

„To nie jest ogólnym«wytrwałość moduł»Dla ogólnej trwałości i transferu Python obiektów wskroś. Wywołania RPC, zobacz moduły marynarki i półki Moduł marszałkowski istnieje głównie w celu obsługi odczytu i zapisu "pseudo-skompilowanego" kodu dla modułów Pythona plików .pyc, dlatego też opiekunowie Pythona zastrzegają sobie prawo do modyfikacji formatu marszałka w tył W razie potrzeby, w przypadku serializacji i deklasyfikacji obiektów w języku Python, należy użyć modułu pikle - wydajność jest równa nieporównywalna, niezależność wersji jest gwarantowana, a pikle obsługuje znacznie szerszy zakres obiektów niż marszałek. " (the python docs about marshal)

9

Różnica pomiędzy tymi benchmarków daje jeden pomysł na przyspieszenie 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 

w pierwszym przypadku, lista powtarza ten sam ciąg. Druga lista jest równoważna, ale każdy ciąg jest osobnym obiektem, ponieważ jest wynikiem wyrażenia. Teraz, jeśli początkowo czytasz dane z zewnętrznego źródła, możesz rozważyć jakiś rodzaj deduplikacji.

11

Niektórzy ludzie mogą pomyśleć o tym zbyt mocno, ale odniosłem wielki sukces, po prostu zawijając wywołania marynarki z gc.disable() i gc.enable(). Na przykład, gdy nożyce poniżej pisząc listy ~ 50MB słowników idzie od 78 sekund do 4.

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

Wow, to naprawdę działa ... ale jakie są reperkusje? – tdc

+0

To działa idealnie! Całkowity czas wymagany również dla mnie spadł o 20x. Chociaż @Chris, czy możesz wskazać nam jakiekolwiek reperkusje (jeśli takie są) tego samego? –

+0

@tdc, Tejas, nie będzie można już zrzucić obiektu acyklicznego, np. 'x' w' x = []; x.append (x) 'spowoduje błąd ValueError, jeśli Pickler.fast jest włączony. – kay

5

Można dokonać cPickle cca. 50x (!) Szybciej, tworząc instancję cPickle.Pickler a następnie ustawienie opcji nieudokumentowane „szybki” 1:

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

Ale jeśli myHugeObject ma odwołań cyklicznych, metoda zrzutu nigdy się nie skończy.

+0

Przydatne, aby wiedzieć! Czy to też przyspiesza 'load'? – juliomalegria

+0

Nie sądzę, że opcja szybka wyłącza wykrywanie podobiektów tylko podczas zaznaczania danych. Możesz znaleźć więcej w dokumentacji serii 3 Pythona (http://docs.python.org/3/library/pickle.html?highlight=pickle#pickle.Pickler.fast) lub oczywiście w kodzie –

3

Możesz poprawić wydajność pamięci masowej poprzez kompresję serializowanego wyniku.

Moje przeczucie polega na tym, że kompresowanie danych i wprowadzanie ich do nieprywizji będzie szybsze niż odczytanie danych z dysku na dysku twardym.

Poniższy test został przeprowadzony w celu udowodnienia, że ​​kompresja przyspieszy proces niezdywersyfikowania. Wynik nie był taki, jak należy oczekiwać, ponieważ urządzenie zostało wyposażone w dysk SSD. Na urządzeniu HHD maszyna kompresująca dane za pomocą lz4 byłaby szybsza, ponieważ odczyt z dysku średnio wynosi 60-70mb/s.

LZ4: Przy zmniejszeniu prędkości o 18% wydajność kompresji wynosi 77,6% dodatkowego miejsca.

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

Interesujące wyniki! Czy sugerujesz, że w jakiś sposób unikniesz konieczności dekompresji danych przed ich upuszczeniem? Jeśli tak to jak? – seaotternerd

Powiązane problemy