Domanda

Ho un modulo Python che fa uso di un'enorme variabile globale del dizionario, attualmente inserisco il codice di calcolo nella sezione superiore, ogni prima importazione o ricarica del modulo richiede più di un minuto, il che è totalmente inaccettabile. Come posso salvare il risultato del calcolo da qualche parte in modo che l'importazione / ricarica successiva non debba calcolarlo? Ho provato cPickle, ma caricare la variabile del dizionario da un file (1.3M) richiede approssimativamente lo stesso tempo del calcolo.

Per fornire ulteriori informazioni sul mio problema,

FD = FreqDist(word for word in brown.words()) # this line of code takes 1 min
È stato utile?

Soluzione

Solo per chiarire: il codice nel corpo di un modulo viene non eseguito ogni volta che il modulo viene importato - viene eseguito solo una volta, dopodiché le importazioni future trovano il modulo già creato, anziché ricreando. Dai un'occhiata a sys.modules per vedere l'elenco dei moduli memorizzati nella cache.

Tuttavia, se il tuo problema è il tempo impiegato per la prima importazione dopo l'esecuzione del programma, probabilmente dovrai usare un metodo diverso da un python dict. Probabilmente la cosa migliore sarebbe usare un modulo su disco, ad esempio un database sqlite, uno dei moduli dbm.

Per un cambiamento minimo nella tua interfaccia, il modulo shelve potrebbe essere la tua migliore opzione - questo mette un'interfaccia piuttosto trasparente tra i moduli dbm che li fa agire come un dict di pitone arbitrario, permettendo di archiviare qualsiasi valore selezionabile. Ecco un esempio:

# 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()

Quindi, nel processo successivo, utilizzalo. Non dovrebbero esserci grandi ritardi, poiché le ricerche vengono eseguite solo per la chiave richiesta nel modulo su disco, quindi non è necessario caricare tutto nella memoria:

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

È un po 'più lento di un vero dict e impiegherà ancora molto tempo a caricarsi se fai qualcosa che richiede tutti i tasti (es. prova a stamparlo), ma potrebbe risolvere il tuo problema.

Altri suggerimenti

Calcola la tua var globale al primo utilizzo.

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 meglio ancora, accedi ai dati necessari tramite un oggetto dati speciale.

class Data:
    GLOBAL_NAME = property(...)

data = Data()

Esempio:

from some_module import data

print(data.GLOBAL_NAME)

Vedi Impostazioni Django .

Suppongo che tu abbia incollato il dict letterale nella fonte, ed è quello che ci vuole un minuto? Non so come evitarlo, ma probabilmente potresti evitare di creare un'istanza di questo dict su import ... Potresti pigramente istanziarlo la prima volta che viene effettivamente utilizzato.

Puoi provare a utilizzare il modulo marshal anziché il c ? Pickle uno; potrebbe essere più veloce. Questo modulo viene utilizzato da Python per memorizzare i valori in un formato binario. Nota in particolare il paragrafo seguente, per vedere se il maresciallo soddisfa le tue esigenze:

  

Non sono supportati tutti i tipi di oggetti Python; in generale, solo gli oggetti il ??cui valore è indipendente da una particolare invocazione di Python possono essere scritti e letti da questo modulo. Sono supportati i seguenti tipi: Nessuno, numeri interi, numeri interi lunghi, numeri in virgola mobile, stringhe, oggetti Unicode, tuple, elenchi, insiemi, dizionari e oggetti di codice, dove si dovrebbe comprendere che tuple, elenchi e dizionari sono supportati solo per il tempo poiché i valori in esso contenuti sono essi stessi supportati; e non dovrebbero essere scritti elenchi e dizionari ricorsivi (causeranno loop infiniti).

Solo per essere al sicuro, prima di smascherare il dict, assicurati che la versione di Python che annulla il dict sia la stessa di quella che ha fatto il maresciallo, dal momento che non ci sono garanzie di compatibilità con le versioni precedenti.

Se la soluzione 'shelve' risulta essere troppo lenta o complicata, ci sono altre possibilità:

shelve diventa molto lento con set di dati di grandi dimensioni. Ho usato redis abbastanza con successo e ha scritto un wrapper FreqDist attorno ad esso. È molto veloce e è possibile accedervi contemporaneamente.

Puoi utilizzare un shelve per archiviare i tuoi dati su disco invece di caricare in memoria tutti i dati Quindi il tempo di avvio sarà molto veloce, ma il compromesso sarà il tempo di accesso più lento.

Shelve decifrerà anche i valori di dict, ma eseguirà il (un) pickle non all'avvio per tutti gli oggetti, ma solo al momento dell'accesso per ogni oggetto stesso.

Un paio di cose che aiuteranno ad accelerare le importazioni:

  1. Potresti provare a eseguire python usando il flag -OO quando esegui python. Questo farà alcune ottimizzazioni che ridurranno i tempi di importazione dei moduli.
  2. C'è qualche motivo per cui non è possibile suddividere il dizionario in dizionari più piccoli in moduli separati che possono essere caricati più rapidamente?
  3. Come ultima risorsa, potresti fare i calcoli in modo asincrono in modo che non ritardino il tuo programma fino a quando non avrà bisogno dei risultati. O magari metti il ??dizionario in un processo separato e passa i dati avanti e indietro usando IPC se vuoi sfruttare le architetture multi-core.

Detto questo, sono d'accordo che non dovresti riscontrare alcun ritardo nell'importazione dei moduli dopo la prima importazione. Ecco un paio di altri pensieri generali:

  1. Stai importando il modulo all'interno di una funzione? In tal caso, questo può portare a problemi di prestazioni poiché deve controllare e vedere se il modulo viene caricato ogni volta che colpisce l'istruzione import.
  2. Il tuo programma è multi-thread? Ho visto occasioni in cui l'esecuzione del codice durante l'importazione del modulo in un'app multi-thread può causare un po 'di instabilità e instabilità dell'applicazione (in particolare con il modulo cgitb).
  3. Se si tratta di una variabile globale, tenere presente che i tempi di ricerca delle variabili globali possono essere significativamente più lunghi dei tempi di ricerca delle variabili locali. In questo caso, puoi ottenere un significativo miglioramento delle prestazioni vincolando il dizionario a una variabile locale se lo usi più volte nello stesso contesto.

Detto questo, è un po 'difficile darti consigli specifici senza un po' più di contesto. Più specificamente, dove lo stai importando? E quali sono i calcoli?

  1. Fattorizza la parte intensivamente computazionale in un modulo separato. Quindi almeno al ricaricamento, non dovrai aspettare.

  2. Prova a scaricare la struttura dei dati usando il protocollo 2. Il comando da provare sarebbe cPickle.dump (FD, protocol = 2) . Dalla documentazione per 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. 
    

Sto attraversando questo stesso problema ... shelve, database, ecc ... sono tutti troppo lenti per questo tipo di problema. Dovrai subire il colpo una volta, inserirlo in una chiave di memoria / archivio val come Redis. Vivrà solo lì in memoria (avvertendo che potrebbe consumare una buona quantità di memoria, quindi potresti volere una scatola dedicata). Non dovrai mai ricaricarlo e dovrai solo cercare in memoria le chiavi

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

word = r.get(key)

Espandendo l'idea del calcolo ritardato, perché non trasformare il dict in una classe che fornisce (e memorizza nella cache) gli elementi necessari?

Puoi anche usare psyco per velocizzare l'esecuzione complessiva ...

O potresti semplicemente usare un database per archiviare i valori? Dai un'occhiata a SQLObject, che semplifica l'archiviazione di elementi in un database.

C'è un'altra soluzione abbastanza ovvia per questo problema. Quando il codice viene ricaricato, l'ambito originale è ancora disponibile.

Quindi ... facendo qualcosa di simile, questo codice verrà eseguito una sola volta.

try:
    FD
except NameError:
    FD = FreqDist(word for word in brown.words())
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top