Маршал сбрасывается быстрее, cPickle быстрее загружается

Я реализую программу, которая требует сериализации и десериализации больших объектов, поэтому я делал несколько тестов с модулями pickle, cPickle и marshal, чтобы выбрать лучший модуль. По пути я нашел что-то очень интересное:

Я использую dumps, а затем loads (для каждого модуля) в списке dicts, tuples, ints, float и string.

Это результат моего теста:

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.

Но, к моему большому удивлению, marshal был намного медленнее, чем cPickle в загрузке:

В 2,2 раза быстрее, чем pickle, но в 3,1 раза меньше, чем cPickle.

А для оперативной памяти производительность marshal при загрузке также была очень неэффективной:

Ubuntu System Monitor

Я предполагаю, почему загрузка с marshal настолько медленная, так или иначе связана с длиной ее сериализованной строки (намного дольше pickle и cPickle).

  • Почему marshal быстрее сбрасывается и загружается медленнее?
  • Почему marshal сериализованная строка длинна?
  • Почему загрузка marshal настолько неэффективна в ОЗУ?
  • Есть ли способ повысить производительность загрузки marshal?
  • Есть ли способ слить marshal быстрый сброс с cPickle быстрой загрузкой?

Ответ 1

cPickle имеет более умный алгоритм, чем marshal, и может делать трюки, чтобы уменьшить пространство, используемое крупными объектами. Это означает, что будет медленнее декодировать, но быстрее кодировать, поскольку результирующий результат меньше. marshal является упрощенным и сериализует объект прямо как есть, не делая дальнейшего анализа. Это также объясняет, почему загрузка marshal настолько неэффективна, что ей просто нужно сделать больше работы - как при чтении большего количества данных с диска - чтобы сделать то же самое, что и cPickle.

marshal и cPickle в конце концов, действительно разные вещи, вы не можете получить как быструю экономию, так и быструю загрузку, поскольку быстрое сохранение подразумевает анализ структур данных меньше, что предполагает сохранение большого количества данных на диск.

Относительно того, что marshal может быть несовместимым с другими версиями Python, вы обычно должны использовать cPickle:

"Это не общий" персистентный "модуль. Для общей сохранности и передачи объектов Python через вызовы RPC см. модули разборки и полки. Модуль маршала существует в основном для поддержки чтения и записи" псевдокомпилированного "кода для Python-модулей .pyc файлов. Поэтому разработчики Python оставляют за собой право изменять формат маршала в обратных несовместимых путях, если это необходимо. Если вы сериализуете и десериализуете объекты Python, вместо этого используйте модуль рассола - производительность сопоставима, независимость от версии гарантируется, а маринование поддерживает значительно более широкий круг объектов, чем маршал". (документы python о маршале)

Ответ 2

Некоторые люди могут подумать, что это слишком много взлома, но я имел большой успех, просто обернув вызовы дампа рассола с помощью gc.disable() и gc.enable(). Например, сниппинг ниже записи списка словарей ~ 50 Мбайт идет от 78 секунд до 4.

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

Ответ 3

Разница между этими критериями дает одну идею для ускорения 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

В первом случае список повторяет ту же строку. Второй список эквивалентен, но каждая строка является отдельным объектом, потому что это результат выражения. Теперь, если вы изначально читаете свои данные из внешнего источника, вы можете рассмотреть некоторую дедупликацию строк.

Ответ 4

Вы можете сделать cPickle cca. 50x (!) Быстрее, создав экземпляр cPickle.Pickler, а затем установив недокументированную опцию "fast" в 1:

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

Но если ваш myHugeObject имеет циклические ссылки, метод дампа никогда не закончится.

Ответ 5

Как вы можете видеть, выход, созданный cPickle.dump, имеет около 1/4 длины вывода, создаваемого marshal.dump. Это означает, что cPickle должен использовать более сложный алгоритм для удаления данных, поскольку ненужные вещи удаляются. При загрузке сбрасываемого списка marshal должен работать с гораздо большим количеством данных, а cPickle может быстро обрабатывать свои данные, поскольку есть меньше данных, которые необходимо проанализировать.

Что касается того, что marshal может быть несовместимым с другими версиями Python, вы обычно должны использовать cPickle:

"Это не общий" персистентный "модуль. Для общей сохранности и передачи объектов Python через вызовы RPC см. модули разборки и полки. Модуль маршала существует в основном для поддержки чтения и записи" псевдокомпилированного "кода для Python-модулей .pyc файлов. Поэтому разработчики Python оставляют за собой право изменять формат маршала в обратных несовместимых путях, если это необходимо. Если вы сериализуете и десериализуете объекты Python, вместо этого используйте модуль рассола - производительность сопоставима, независимость от версии гарантируется, а маринование поддерживает значительно более широкий круг объектов, чем маршал". (документы python о маршале)

Ответ 6

Вы можете повысить эффективность хранения, сжимая результат сериализации.

Моя догадка заключается в том, что сжатие данных и их подача в unserialize будут быстрее, чем чтение сырья с диска через жесткий диск.

Ниже приведен тест, чтобы доказать, что сжатие ускорит процесс несериализации. Результат не ожидался, так как машина оснащалась SSD. На аппаратной машине HHD сжатие данных с использованием lz4 будет быстрее, поскольку чтение с диска среднее между 60-70 мб/с.

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