Использование cPickle для сериализации большого словаря вызывает MemoryError - PullRequest
8 голосов
/ 18 февраля 2011

Я пишу перевернутый индекс для поисковой системы для коллекции документов. Прямо сейчас я храню указатель как словарь словарей. То есть каждое ключевое слово отображается в словарь docID-> положений вхождения.

Модель данных выглядит примерно так: {word: {doc_name: [location_list]}}

Создание индекса в памяти работает нормально, но когда я пытаюсь сериализовать на диск, я нажимаю MemoryError. Вот мой код:

# Write the index out to disk
serializedIndex = open(sys.argv[3], 'wb')
cPickle.dump(index, serializedIndex, cPickle.HIGHEST_PROTOCOL)

Прямо перед сериализацией моя программа использует около 50% памяти (1,6 Гб). Как только я звоню в cPickle, мое использование памяти возрастает до 80%, прежде чем происходит сбой.

Почему cPickle использует так много памяти для сериализации? Есть ли лучший способ подойти к этой проблеме?

Ответы [ 3 ]

10 голосов
/ 18 февраля 2011

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

0 голосов
/ 18 февраля 2011

Возможно, вы используете неправильный инструмент для этой работы. Если вы хотите сохранить огромное количество проиндексированных данных, я настоятельно рекомендую использовать базу данных на диске SQLite (или, конечно, просто обычную базу данных) с ORM, например SQLObject или SQL Алхимия .

Они позаботятся о таких рутинных вещах, как совместимость, оптимизация формата по назначению и одновременное хранение не всех данных в памяти, так что вам не хватит памяти ...

Добавлено: Поскольку я все равно работал над почти одинаковой вещью, но главным образом потому, что я такой хороший человек, вот демоверсия, которая, кажется, делает то, что вам нужно (она создаст SQLite файл в текущем каталоге и удалите его, если файл с таким именем уже существует, поэтому сначала поместите его в пустое место):

import sqlobject
from sqlobject import SQLObject, UnicodeCol, ForeignKey, IntCol, SQLMultipleJoin
import os

DB_NAME = "mydb"
ENCODING = "utf8"

class Document(SQLObject):
    dbName = UnicodeCol(dbEncoding=ENCODING)

class Location(SQLObject):
    """ Location of each individual occurrence of a word within a document.
    """
    dbWord = UnicodeCol(dbEncoding=ENCODING)
    dbDocument = ForeignKey('Document')
    dbLocation = IntCol()

TEST_DATA = {
    'one' : {
        'doc1' : [1,2,10],
        'doc3' : [6],
    },

    'two' : {
        'doc1' : [2, 13],
        'doc2' : [5,6,7],
    },

    'three' : {
        'doc3' : [1],
    },
}        

if __name__ == "__main__":
    db_filename = os.path.abspath(DB_NAME)
    if os.path.exists(db_filename):
        os.unlink(db_filename)
    connection = sqlobject.connectionForURI("sqlite:%s" % (db_filename))
    sqlobject.sqlhub.processConnection = connection

    # Create the tables
    Document.createTable()
    Location.createTable()

    # Import the dict data:
    for word, locs in TEST_DATA.items():
        for doc, indices in locs.items():
            sql_doc = Document(dbName=doc)
            for index in indices:
                Location(dbWord=word, dbDocument=sql_doc, dbLocation=index)

    # Let's check out the data... where can we find 'two'?
    locs_for_two = Location.selectBy(dbWord = 'two')

    # Or...
    # locs_for_two = Location.select(Location.q.dbWord == 'two')

    print "Word 'two' found at..."
    for loc in locs_for_two:
        print "Found: %s, p%s" % (loc.dbDocument.dbName, loc.dbLocation)

    # What documents have 'one' in them?
    docs_with_one = Location.selectBy(dbWord = 'one').throughTo.dbDocument

    print
    print "Word 'one' found in documents..."
    for doc in docs_with_one:
        print "Found: %s" % doc.dbName

Это, конечно, не единственный способ (или обязательно лучший способ) сделать это. Должны ли таблицы Document или Word быть отдельными таблицами из таблицы Location, зависит от ваших данных и типичного использования. В вашем случае таблица «Word» может быть отдельной таблицей с некоторыми дополнительными настройками индексации и уникальности.

0 голосов
/ 18 февраля 2011

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

Другие варианты: разбить словарь на более мелкие кусочки и выбрать каждый кусочек. Затем соедините их вместе, когда загрузите все.

Извините, это расплывчато, я просто списываю свою голову. Я подумал, что это все еще может быть полезно, так как никто не ответил.

...