View Full Version : [Python] Multiprocessing e condivisione di oggetti read-only
Salve, ho un problema con Python.
Ho implementato un algoritmo genetico usando Deap, ma questo non è importante. L'algoritmo funziona, purtroppo però la versione multi-processo ha un problema, consuma molta più memoria della versione single-process.
Immagino che l'utilizzo esagerato della memoria sia dovuto al fatto che viene allocata memoria per ogni processo così da poter caricare gli oggetti che poi verranno usati.
Questo è sbagliato perché gli oggetti che vengono usati vengono soltanto letti dai diversi processi, quindi potrebbero benissimo essere condivisi così da risparmiare memoria.
Questa è la struttura del codice che ho scritto. Dovrei passare l'oggetto Dataset ai singoli processi.
def evaluate(individual, dataset=None):
penalty = dataset.compute(individual)
return penalty
def initialize():
dataset = dataset(file1, file2)
pool = multiprocessing.Pool()
toolbox.register("map", pool.map)
toolbox.register("evaluate", evaluate, dataset=dataset)
return toolbox, dataset
def main():
toolbox, dataset = initialize()
dataset.data = some_training_set
fitnesses = toolbox.map(toolbox.evaluate, population)
dataset.data = some_validation_set
fitnesses = toolbox.map(toolbox.evaluate, population)
Questa è la classe Dataset che contiene il dataset appunto, il quale viene letto da csv con pandas e un dizionario.
class Dataset:
def __init__(self, file1, file2):
self.data = read(file1)
self.dict = loadpickle(file2)
def compute(self, individual):
for row in self.data
# some stuff reading row and self.dict
Qual è il modo più semplice di passare l'oggetto Dataset ai vari processi, senza che ne vengano fatte diverse copie?
cdimauro
16-01-2015, 20:08
Purtroppo è un casino. Forse questa (http://stackoverflow.com/questions/14124588/python-multiprocessing-shared-memory) soluzione su StackOverflow può aiutarti.
EDIT. Ecco un altro articolo (http://pymotw.com/2/multiprocessing/communication.html) interessante sull'argomento.
EDIT2: altri due risposte (qui (http://stackoverflow.com/questions/10721915/shared-memory-objects-in-python-multiprocessing) e qui (http://stackoverflow.com/questions/5549190/is-shared-readonly-data-copied-to-different-processes-for-python-multiprocessing/5550156#5550156)) su SO, correlate, ma la musica mi sembra la stessa.
Ti ringrazio per i link, però avevo già dato un'occhiata su Stack Overflow, ma da solo non sono riuscito a trovare una soluzione. Ho da poco iniziato a studiare Python e non sono molto esperto di multi-processing.
Possibile che è così complicato? In fondo, devo semplicemente leggere quegli oggetti, non ho problemi di sincronizzazione o altro... Implementare una versione multithread sarebbe possibile? Avrebbe le stesse performance? Da quello che leggo in giro non sembra essere molto conveniente...
cdimauro
16-01-2015, 20:32
Col multithreading risolvi sicuramente, perché non hai bisogno di farti copia alcuna, come avviene invece normalmente col multiprocessing (a meno di usare il suo tipo Array, come hai potuto leggere, ma con tutte le problematiche del caso).
Soprattutto se si tratta di oggetti "a sola lettura", col multithreading non hai alcun problema di condivisione.
Il problema del modulo threading è, però, che fa uso di un solo core, come penso tu sappia, causa presenza della famigerata GIL.
Se il tuo algoritmo è I/O bound, non penso che ciò sia un problema. Anzi, così viene sfruttato per bene il singolo core. Ma se devi fare "number crunching", allora ti scontrerai con questa enorme limitazione.
In alternativa, se potessi suddividere i dati in parti diverse (file o mmap), e fargliele leggere poi ai singoli processi, probabilmente potresti anche risolvere. E' una tecnica che ho usato tempo addietro, quando dovevo sfruttare tutti i core, ma distribuire un certo insieme di numeri: li ho partizionati in un certo modo, e poi dato in pasto ogni blocco a un ben preciso processo.
Trattandosi di un algoritmo genetico devo valutare la bontà di una soluzione e per fare ciò devo leggere dei dati. Per ottimizzare ho pensato di fare queste valutazioni in parallelo perché indipendenti. Credo che per avere un notevole incremento delle performance ho bisogno per forza del multiprocessing.
Ma è davvero così complicato riuscire a leggere un csv e condividerlo tra i processi? Non è che riusciresti ad aiutarmi? Io ho letto di tutto in giro, multiprocessing.Value, multiprocessing.Array, Manager.... ma davvero non riesco a venirne a capo.
EDIT: Qui (http://stackoverflow.com/questions/22487296/multiprocessing-in-python-sharing-large-object-e-g-pandas-dataframe-between) ad esempio suggeriscono di usare il Manager.
cdimauro
16-01-2015, 21:00
Se i dati del CSV li puoi partizionare, potresti risolvere in questo modo, fornendo a ogni processo del pool soltanto ciò che deve processare.
Ho lavorato poco col multiprocessing, per cui oltre a ciò che ho scritto non mi viene in mente altro usando questo strumento.
Un'altra soluzione potrebbe essere quella di usare mmap e la classica fork per creare i vari processi. Nella documentazione del modulo mmap c'è qualche esempio in merito.
Oltre a questo... I give-up. :(
Non posso partizionare il file.
Ti ringrazio lo stesso per l'aiuto.
cdimauro
16-01-2015, 21:20
Ho visto adesso il tuo edit. Il Manager sembra poter risolvere il tuo problema.
Teoricamente se sei sotto un sistema operativo tipo Linux, e il multi-processing sfrutta il forking, finché non tocchi le pagine di memoria che contengono i dati questi non vengono copiati dal padre al figlio (Copy On Write).
Se riesci ad organizzare i dati in maniera che siano esattamente in pagine di memoria separate (sotto Linux la dimensione di una pagina standard sono 4K), forse te la puoi cavare sfruttando questo meccanismo (a patto ripeto che le pagine non vegano "sporcate" con nuove scritture).
Dopodiché bisognerebbe effettivamente vedere come si comporta Python con quella memoria, non so che controllo puoi avere da quel punto di vista.
Chiaramente rimane un trick ad-hoc, in generale non puoi assumere questo comportamento su altri sistemi (anche se penso che ormai tutti i sistemi operativi moderni adottino tecniche di questo tipo).
cdimauro
18-01-2015, 16:26
Il problema è che, col meccanismo del reference counting usato in CPython, le pagine verranno copiate inevitabilmente al primo accesso, che sia in lettura o scrittura è indifferente, degli oggetti condivisi.
cdimauro
18-01-2015, 18:19
@oNaSsIs: m'è venuto in mente che potresti provare con PyPy (http://pypy.org/), che non ha né GIL né reference counting, e in più ha un ottimo JIT per accelerare, e pure di molto, le prestazioni.
L'ho già usato diverse volte, alcune anche in produzione, e s'è comportato benissimo finora. ;)
ingframin
19-01-2015, 08:10
Non puoi caricare i dati in una Queue o in una Pipe condivisa?
https://docs.python.org/3.4/library/multiprocessing.html
Oppure leggere un pezzetto di file con ogni processo...
http://www.tutorialspoint.com/python/file_seek.htm
Il problema è che, col meccanismo del reference counting usato in CPython, le pagine verranno copiate inevitabilmente al primo accesso, che sia in lettura o scrittura è indifferente, degli oggetti condivisi.
Avevo letto questa cosa, spesso come risposta a chi suggeriva di usare variabili globali. :/
@oNaSsIs: m'è venuto in mente che potresti provare con PyPy, che non ha né GIL né reference counting, e in più ha un ottimo JIT per accelerare, e pure di molto, le prestazioni.
Ottimo suggerimento. Purtroppo non posso applicarlo nel mio caso perché ho verificato che PyPy non supporta Numpy. :cry:
Oppure leggere un pezzetto di file con ogni processo...
Teoricamente si, praticamente no. Non devo caricare solo il dataset ma anche dei dizionari piuttosto grandi. Il dataset viene letto e riletto migliaia di volte, quindi avevo pensato di caricarlo tutto in memoria per ottenere performance migliori. Infine ci sono altri piccoli problemi legati ai meccanismi di sampling del dataset.
Non puoi caricare i dati in una Queue o in una Pipe condivisa?
Questa potrebbe essere un'idea molto interessante. Ma ho due domande, la prima è come passare la Queue/Pipe. Direttamente al Pool, che poi la passerà ai vari processi? Ma soprattutto i processi quando hanno la Queue/Pipe, come dovrebbero gestirla? Da quello che ho visto essendo progettata per altri scopi, ogni volta che viene preso un oggetto questo viene rimosso. Cosa dovrei fare rimettere ogni volta dentro una copia dell'oggetto?
cdimauro
20-01-2015, 06:07
Credo proprio di sì. Tanto una copia dell'oggetto in Python è soltanto una reference.
Riguardo a PyPy, so che al momento supporta un sottoinsieme di NumPy, per cui potrebbe anche essere sufficiente per quel che ti serve.
Altre possibili soluzioni: IronPython e Jython, che implementano Python per .NET e JVM rispettivamente, e che fanno uso di tutti i core perché internamente si occupano loro della protezione delle loro strutture dati e della sincronizzazione / condivisione.
Grazie mille cdimauro per l'aiuto! Vi farò sapere se risolvo! ;)
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.