Pregunta

Tengo un módulo de Python que utiliza una gran variable global de diccionario, actualmente pongo el código de cálculo en la sección superior, cada primera vez que la importación o recarga del módulo lleva más de un minuto, lo cual es totalmente inaceptable. ¿Cómo puedo guardar el resultado del cálculo en algún lugar para que la próxima importación / recarga no tenga que calcularlo? Intenté cPickle, pero cargar la variable del diccionario desde un archivo (1.3M) toma aproximadamente el mismo tiempo que el cálculo.

Para dar más información sobre mi problema,

FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
¿Fue útil?

Solución

Solo para aclarar: el código en el cuerpo de un módulo no se ejecuta cada vez que se importa el módulo; se ejecuta solo una vez, después de lo cual las importaciones futuras encontrarán el módulo ya creado, en lugar de recreándolo Eche un vistazo a sys.modules para ver la lista de módulos en caché.

Sin embargo, si su problema es el tiempo que lleva la primera importación después de ejecutar el programa, es probable que necesite usar algún otro método que no sea un dict de Python. Probablemente lo mejor sería usar un formulario en disco, por ejemplo, una base de datos sqlite, uno de los módulos dbm.

Para un cambio mínimo en su interfaz, el módulo de estantería puede ser su mejor opción: esto pone una interfaz bastante transparente entre los módulos dbm que los hace actuar como un dict arbitrario de python, lo que permite almacenar cualquier valor seleccionable. Aquí hay un ejemplo:

# Create dict with a million items:
import shelve
d = shelve.open('path/to/my_persistant_dict')
d.update(('key%d' % x, x) for x in xrange(1000000))
d.close()

Luego, en el siguiente proceso, úsalo. No debería haber un gran retraso, ya que las búsquedas solo se realizan para la clave solicitada en el formulario en el disco, por lo que no todo tiene que cargarse en la memoria:

>>> d = shelve.open('path/to/my_persistant_dict')
>>> print d['key99999']
99999

Es un poco más lento que un dict real, y todavía tardará mucho tiempo en cargarse si hace algo que requiere todas las teclas (por ejemplo, intente imprimirlo), pero puede resolver tu problema.

Otros consejos

Calcule su var global en el primer uso.

class Proxy:
    @property
    def global_name(self):
        # calculate your global var here, enable cache if needed
        ...

_proxy_object = Proxy()
GLOBAL_NAME = _proxy_object.global_name

O mejor aún, acceda a los datos necesarios a través de un objeto de datos especial.

class Data:
    GLOBAL_NAME = property(...)

data = Data()

Ejemplo:

from some_module import data

print(data.GLOBAL_NAME)

Consulte configuración de Django .

Supongo que ha pegado el dict literal en la fuente, ¿y eso es lo que está tomando un minuto? No sé cómo solucionarlo, pero probablemente podría evitar crear una instancia de este dict al importar ... Podría crear una instancia perezosa la primera vez que realmente se usa.

Puede intentar utilizar el marshal en lugar del módulo c ? Pickle uno; Podría ser más rápido. Python utiliza este módulo para almacenar valores en formato binario. Observe especialmente el siguiente párrafo, para ver si Marshal se ajusta a sus necesidades:

  

No todos los tipos de objetos Python son compatibles; en general, solo los objetos cuyo valor es independiente de una invocación particular de Python pueden ser escritos y leídos por este módulo. Se admiten los siguientes tipos: Ninguno, enteros, enteros largos, números de coma flotante, cadenas, objetos Unicode, tuplas, listas, conjuntos, diccionarios y objetos de código, donde debe entenderse que las tuplas, listas y diccionarios solo son compatibles siempre ya que los valores contenidos en ellos mismos son compatibles; y las listas y diccionarios recursivos no deben escribirse (causarán bucles infinitos).

Solo para estar seguro, antes de desarmar el dict, asegúrese de que la versión de Python que desarma el dict es la misma que hizo el mariscal, ya que no hay garantías de compatibilidad con versiones anteriores.

Si la solución 'estantería' resulta ser demasiado lenta o complicada, hay otras posibilidades:

shelve se vuelve realmente lento con grandes conjuntos de datos. He estado usando redis bastante con éxito, y escribió un envoltorio FreqDist a su alrededor. Es muy rápido y se puede acceder simultáneamente.

Puede usar un estante para almacenar sus datos en el disco en lugar de cargar todos los datos en la memoria. Por lo tanto, el tiempo de inicio será muy rápido, pero la compensación será un tiempo de acceso más lento.

Shelve también seleccionará los valores de dict, pero hará el (des) pickle no al inicio de todos los elementos, sino solo en el momento de acceso para cada elemento en sí.

Un par de cosas que ayudarán a acelerar las importaciones:

  1. Puede intentar ejecutar python usando el indicador -OO cuando ejecute python. Esto hará algunas optimizaciones que reducirán el tiempo de importación de módulos.
  2. ¿Hay alguna razón por la que no pueda dividir el diccionario en diccionarios más pequeños en módulos separados que puedan cargarse más rápidamente?
  3. Como último recurso, puede hacer los cálculos de forma asincrónica para que no retrasen su programa hasta que necesite los resultados. O tal vez incluso ponga el diccionario en un proceso separado y pase datos de un lado a otro utilizando IPC si desea aprovechar las arquitecturas de múltiples núcleos.

Dicho esto, estoy de acuerdo en que no debería experimentar ningún retraso en la importación de módulos después de la primera vez que lo importe. Aquí hay otros pensamientos generales:

  1. ¿Está importando el módulo dentro de una función? Si es así, esto puede conducir a problemas de rendimiento ya que tiene que verificar y ver si el módulo se carga cada vez que llega a la declaración de importación.
  2. ¿Su programa es multiproceso? He visto ocasiones en las que la ejecución de código al importar el módulo en una aplicación multiproceso puede causar cierta inestabilidad e inestabilidad de la aplicación (especialmente con el módulo cgitb).
  3. Si esta es una variable global, tenga en cuenta que los tiempos de búsqueda de la variable global pueden ser significativamente más largos que los tiempos de búsqueda de la variable local. En este caso, puede lograr una mejora significativa en el rendimiento vinculando el diccionario a una variable local si lo está utilizando varias veces en el mismo contexto.

Dicho esto, es un poco difícil darle algún consejo específico sin un poco más de contexto. Más específicamente, ¿dónde lo está importando? ¿Y cuáles son los cálculos?

  1. Factoriza la parte computacionalmente intensiva en un módulo separado. Luego, al menos en la recarga, no tendrá que esperar.

  2. Intente volcar la estructura de datos utilizando el protocolo 2. El comando para intentar sería cPickle.dump (FD, protocol = 2) . Desde la cadena de documentación para cPickle.Pickler :

    Protocol 0 is the
    only protocol that can be written to a file opened in text
    mode and read back successfully.  When using a protocol higher
    than 0, make sure the file is opened in binary mode, both when
    pickling and unpickling. 
    

Estoy pasando por este mismo problema ... las estanterías, las bases de datos, etc. son demasiado lentas para este tipo de problema. Tendrá que recibir el golpe una vez, insertarlo en una tienda clave / val inmemory como Redis. Simplemente vivirá allí en la memoria (advirtiendo que podría usar una buena cantidad de memoria, por lo que es posible que desee una caja dedicada). Nunca tendrá que volver a cargarlo y solo buscará claves en la memoria

r = Redis()
r.set(key, word)

word = r.get(key)

Ampliando la idea del cálculo retrasado, ¿por qué no convertir el dict en una clase que suministre (y almacene) elementos según sea necesario?

También puede usar psyco para acelerar la ejecución general ...

O ¿podría usar una base de datos para almacenar los valores? Consulte SQLObject, que hace que sea muy fácil almacenar cosas en una base de datos.

Hay otra solución bastante obvia para este problema. Cuando se vuelve a cargar el código, el alcance original todavía está disponible.

Entonces ... hacer algo como esto asegurará que este código se ejecute solo una vez.

try:
    FD
except NameError:
    FD = FreqDist(word for word in brown.words())
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top