Подтвердить что ты не робот

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

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

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

Ответ 2

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

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

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

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

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

Ответ 6

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