Как десериализовать 1GB объектов в Python быстрее, чем cPickle?

У нас есть веб-сервер на основе Python, который загружает большое количество больших файлов данных при запуске с помощью cPickle . Файлы данных (маринованные с использованием HIGHEST_PROTOCOL ) составляют около 0,4 ГБ на диске и загружаются в память как около 1,2 ГБ объектов Python – это занимает около 20 секунд . Мы используем Python 2.6 на 64-битных машинах Windows.

Узкое место, конечно, не является диском (требуется меньше, чем 0,5 с, чтобы действительно прочитать много данных), но выделение памяти и создание объекта (создаются миллионы создаваемых объектов). Мы хотим уменьшить 20 секунд, чтобы уменьшить время запуска.

Есть ли способ десериализовать более 1 ГБ объектов в Python намного быстрее, чем cPickle (например, 5-10x)? Поскольку время выполнения связано с распределением памяти и созданием объекта, я полагаю, что использование другого метода разбрасывания, такого как JSON, здесь не помогло.

Я знаю, что некоторые интерпретируемые языки имеют способ сохранить весь образ памяти в виде файла на диске, поэтому они могут загружать его обратно в память за один раз, без выделения / создания для каждого объекта. Есть ли способ сделать это или добиться чего-то подобного в Python?

  1. Попробуйте модуль маршала – он внутренний (используется байтом-компилятором) и намеренно не рекламируется много, но он намного быстрее. Обратите внимание, что он не сериализует произвольные экземпляры, такие как pickle, только встроенные типы (не помните точные ограничения, см. Документы). Также обратите внимание, что формат нестабилен.

  2. Если вам нужно инициализировать несколько процессов и вы можете переносить один процесс, который всегда загружается, есть изящное решение: загружать объекты в один процесс, а затем ничего не делать в нем, кроме процессов forking по требованию. Форкирование выполняется быстро (копирование при записи) и разделяет память между всеми процессами. [Отказ от ответственности: непроверенный; в отличие от Ruby , подсчет ссылок Python вызывает копии страниц, поэтому это, вероятно, бесполезно, если у вас есть огромные объекты и / или доступ к небольшой их части.]

  3. Если ваши объекты содержат много необработанных данных, таких как массивы numpy, вы можете их скопировать по карте для более быстрого запуска. pytables также хорош для этих сценариев.

  4. Если вы будете использовать только небольшую часть объектов, возможно, вам может помочь база данных OO (например, Zope's). Хотя, если вам нужны все они в памяти, вы просто потратите много накладных расходов, чтобы получить небольшой выигрыш. (никогда не использовал один, так что это может быть вздор).

  5. Может быть, другие реализации python могут это сделать? Не знаю, просто мысль …

Вы загружаете () из маринованных данных непосредственно из файла? Как попытаться загрузить файл в память, а затем выполнить загрузку? Я бы начал с попытки cStringIO (); в качестве альтернативы вы можете попытаться написать свою собственную версию StringIO, которая будет использовать buffer (), чтобы нарезать память, которая уменьшила бы необходимые операции копирования () (cStringIO все еще может быть быстрее, но вам придется попробовать).

Иногда при выполнении таких операций иногда возникают огромные узкие места, особенно на платформе Windows; система Windows как-то очень неоптимизирована для выполнения большого количества небольших чтений, в то время как UNIX справляется достаточно хорошо; если load () делает много небольших чтений или вы вызываете load () несколько раз, чтобы прочитать данные, это поможет.

Вы пытались пожертвовать эффективностью травления, не используя HIGHEST_PROTOCOL? Неясно, какие затраты на производительность связаны с использованием этого протокола, но, возможно, стоит попробовать.

Я не использовал cPickle (или Python), но в таких случаях я считаю, что лучшая стратегия заключается в том, чтобы избежать ненужной загрузки объектов, пока они не понадобятся – скажем, загрузка после запуска в другом потоке, на самом деле ее обычно лучше избегать ненужная загрузка / инициализация в любое время по очевидным причинам. Google «ленивая загрузка» или «ленивая инициализация». Если вам действительно нужны все объекты для выполнения какой-либо задачи до запуска сервера, возможно, вы можете попробовать внедрить ручной метод десериализации, другими словами реализовать что-то самостоятельно, если у вас есть глубокое знание данных, с которыми вы будете иметь дело, что может помочь вам «сжать» более высокую производительность, чем общий инструмент для борьбы с ним.

Невозможно ответить на это, не зная больше о том, какие данные вы загружаете и как вы его используете.

Если это какая-то бизнес-логика, возможно, вам стоит попробовать превратить ее в предварительно скомпилированный модуль;

Если это структурированные данные, можете ли вы делегировать их в базу данных и извлекать только то, что нужно?

Имеют ли данные регулярную структуру? Есть ли способ разделить его и решить, что требуется, и только потом загрузить его?

Я добавлю еще один ответ, который может быть полезен – если можно, можете ли вы попытаться определить _ слоты _ в классе, который наиболее часто создается? Это может быть немного ограниченным и невозможным, однако, похоже, это сократило время, необходимое для инициализации моего теста примерно до половины.