Как десериализовать 1 ГБ объектов в Python быстрее, чем cPickle? - PullRequest
12 голосов
/ 16 ноября 2010

У нас есть веб-сервер на основе 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?

Ответы [ 6 ]

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

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

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

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

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

7 голосов
/ 16 ноября 2010

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

4 голосов
/ 16 ноября 2010

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

3 голосов
/ 16 ноября 2010

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

2 голосов
/ 16 ноября 2010

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

2 голосов
/ 16 ноября 2010

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

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

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

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

...