2011-02-18 9 views
8

Estoy escribiendo un índice invertido para un motor de búsqueda en una colección de documentos. En este momento, estoy almacenando el índice como un diccionario de diccionarios. Es decir, cada palabra clave se correlaciona con un diccionario de docIDs-> posiciones de ocurrencia.Usar cPickle para serializar un diccionario grande causa MemoryError

modelar los datos se ve algo como: { palabra: {DOC_NAME: [location_list]}}

Construcción del índice en la memoria funciona bien, pero cuando intento para serializar en el disco, me pegó un MemoryError. Aquí está mi código:

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

Justo antes de la serialización, mi programa utiliza aproximadamente el 50% de memoria (1,6 Gb). Tan pronto como realizo la llamada a cPickle, mi uso de memoria se dispara al 80% antes de fallar.

¿Por qué cPickle usa tanta memoria para la serialización? ¿Hay una mejor manera de abordar este problema?

Respuesta

10

cPickle necesita utilizar una gran cantidad de memoria adicional porque detecta ciclos. Podría intentar usar el módulo Marshal si está seguro de que sus datos no tienen ciclos

+1

Funcionó como un amuleto. Solución increíblemente simple: básicamente cambió "pickle" a "mariscal" y ya estaba hecho. No me di cuenta de que cPickle realizaba la detección de ciclo. Al usar Mariscal en su lugar, escribir en el disco tomó una cuestión de segundos en lugar de 20 minutos, y redujo el consumo de memoria del 30% y se estrelló a casi 0%. ¡Gracias! –

+0

Solución simple más una explicación concisa, 100% impresionante. – mitchus

+0

¡Es bueno saberlo, gracias @gnibbler! –

0

Hay otra biblioteca de pepinillos que podrías probar. También podría haber algunas configuraciones de cPickle que podría cambiar.

Otras opciones: Divida su diccionario en piezas más pequeñas y cPickle cada pieza. Luego vuélvalos a juntar cuando cargue todo.

Lo siento, esto es vago, solo estoy escribiendo en mi cabeza. Pensé que todavía podría ser útil ya que nadie más ha respondido.

0

Puede que esté utilizando una herramienta incorrecta para este trabajo. Si desea conservar una gran cantidad de datos indexados, le sugiero que use una base de datos SQLite en disco (o, por supuesto, solo una base de datos normal) con un ORM como SQLObject o SQL Alchemy.

Estos se hará cargo de las cosas mundanas como la compatibilidad, la optimización de formato para el propósito, y no posee la totalidad de los datos en la memoria al mismo tiempo para que se agote la memoria ...

Agregado: Porque estaba trabajando en algo casi idéntico de todos modos, pero principalmente porque soy una persona agradable, aquí hay una demostración que parece hacer lo que necesita (creará un archivo SQLite en su directorio actual, y lo eliminará si un archivo con ese el nombre ya existe, así que colóquelo en algún lugar vacío primero):

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 

Esto es cert Ainly no es la única forma (o necesariamente la mejor) para hacer esto. Si las tablas de documento o de Word deben ser tablas separadas de la tabla de ubicación depende de sus datos y uso típico. En su caso, la tabla "Word" probablemente sea una tabla separada con algunas configuraciones adicionales para indexación y unicidad.

+0

Gracias por su sugerencia. Por ahora, voy a utilizar Marshal en lugar de Pickle, pero puedo volver a visitar esto y migrar a una solución basada en db en el futuro. ¡Aclamaciones! –

+0

@Stephen Poletto - eso es genial, si marhsal funciona, funciona, y esto puede permanecer aquí para la posteridad :) – detly

Cuestiones relacionadas