Сериализация памяти Python - PullRequest
10 голосов
/ 18 мая 2011

Мне было интересно, может ли кто-нибудь знать ответ на следующий вопрос.

Я использую Python для построения символьного дерева суффиксов. В дереве более 11 миллионов узлов, которые вмещают примерно 3 ГБ памяти. Это было меньше, чем 7 ГБ, используя метод класса slot вместо метода Dict .

Когда я сериализую дерево (используя самый высокий протокол), результирующий файл будет более чем в сто раз меньше.

Когда я загружаю маринованный файл обратно, он снова потребляет 3 ГБ памяти. Откуда эти дополнительные издержки, это как-то связано с обработкой Питоном ссылок на память на экземпляры классов?

Обновление

Спасибо, Ларсман и Гурге, за ваши очень полезные объяснения и советы. Я использую дерево как часть интерфейса поиска информации по совокупности текстов.

Первоначально я сохранил дочерние элементы (максимум 30) в виде массива Numpy, затем попробовал аппаратную версию (ctypes.py_object*30), массив Python (ArrayType), а также словарь и типы Set.

Списки, кажется, работали лучше (с помощью guppy для профилирования памяти и __slots__['variable',...]), но я все еще пытаюсь сжать его немного больше, если смогу. Единственная проблема, с которой я столкнулся при работе с массивами, заключается в необходимости заранее указывать их размер, что вызывает некоторую избыточность с точки зрения узлов только с одним дочерним элементом, и их у меня довольно много. ; -)

После того, как дерево построено, я собираюсь преобразовать его в вероятностное дерево со вторым проходом, но, возможно, я смогу сделать это, когда дерево построено. Поскольку время создания в моем случае не слишком важно, array.array () звучит как то, что было бы полезно попробовать, спасибо за подсказку, очень понравилось.

Я дам вам знать, как оно идет.

Ответы [ 2 ]

9 голосов
/ 18 мая 2011

Если вы попытаетесь выбрать пустой список, вы получите:

>>> s = StringIO()
>>> pickle.dump([], s)
>>> s.getvalue()
'(l.'

и аналогично '(d.' для пустого dict. Это три байта. Однако представление в памяти списка содержит

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

На моей машине, которая имеет 64-битные указатели, объект заголовка списка * Python sizeof составляет 40 байт, так что это один порядок величины. Я предполагаю, что пустой dict будет иметь аналогичный размер.

Затем и list, и dict используют стратегию перераспределения для получения амортизированной производительности O (1) для своих основных операций, malloc вводит накладные расходы, есть выравнивание, атрибуты членов, которые вы можете или может даже не знать и о других факторах, которые дают вам второй порядок величины.

Подводя итог: pickle - довольно хороший алгоритм сжатия объектов Python:)

3 голосов
/ 18 мая 2011

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

Dicts и объекты очень хороши для динамического изменения, но они не очень экономят место в сценарии только для чтения.Я не знаю точно, для чего вы используете свое дерево суффиксов, но вы можете позволить каждому узлу быть представленным кортежем из отсортированного array.array ('c') и одинаково длинным кортежем подузлов (вместо этого кортежем)вектора, чтобы избежать перераспределения).Вы просматриваете дерево, используя bisect-модуль для поиска в массиве.Индекс символа в массиве будет соответствовать подузлу в кортеже подузлов.Таким образом, вы избегаете диктов, объектов и векторов.

Вы можете сделать что-то подобное в процессе построения, возможно, используя subnode-vector вместо subnode-tuple.Но это, конечно, замедлит построение, поскольку вставка новых узлов в отсортированный вектор - это O (N).

...