2010-12-26 11 views
6

Sto elaborando grandi quantità di dati, memorizzati in un dizionario, utilizzando il multiprocessing. Fondamentalmente tutto ciò che sto facendo è caricare alcune firme, memorizzato in un dizionario, costruirci un oggetto dict condiviso (ottenendo l'oggetto 'proxy' restituito da Manager.dict()) e passare questo proxy come argomento alla funzione che ha da eseguire in multiprocessing.python: condivisione di enormi dizionari utilizzando il multiprocessing

Giusto per chiarire:

signatures = dict() 
load_signatures(signatures) 
[...] 
manager = Manager() 
signaturesProxy = manager.dict(signatures) 
[...] 
result = pool.map (myfunction , [ signaturesProxy ]*NUM_CORES) 

Ora, tutto funziona perfettamente se firme è inferiore a 2 milioni di voci o giù di lì. Ad ogni modo, devo elaborare un dizionario con i tasti 5.8M (decapaggio firme in formato binario genera un file da 4,8 GB). In questo caso, il processo muore durante la creazione dell'oggetto proxy:

Traceback (most recent call last): 
    File "matrix.py", line 617, in <module> 
signaturesProxy = manager.dict(signatures) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 634, in temp 
token, exp = self._create(typeid, *args, **kwds) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 534, in _create 
id, exposed = dispatch(conn, None, 'create', (typeid,)+args, kwds) 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 79, in dispatch 
raise convert_to_error(kind, result) 
multiprocessing.managers.RemoteError: 
--------------------------------------------------------------------------- 
Traceback (most recent call last): 
    File "/usr/lib/python2.6/multiprocessing/managers.py", line 173, in handle_request 
    request = c.recv() 
EOFError 
--------------------------------------------------------------------------- 

so la struttura dei dati è enorme, ma sto lavorando su una macchina dotata w/32GB di RAM, e in esecuzione in alto vedo che il processo, dopo aver caricato le firme, occupa 7 GB di RAM. Quindi inizia a costruire l'oggetto proxy e l'utilizzo della RAM sale a circa 17 GB di RAM, ma non si avvicina mai a 32. A questo punto, l'utilizzo della RAM inizia a diminuire rapidamente e il processo termina con l'errore precedente. Quindi immagino che questo non sia dovuto a un errore di memoria esaurita ...

Qualche idea o suggerimento?

Grazie,

Davide

risposta

-3

Se i dizionari sono di sola lettura, non è necessario disporre di oggetti proxy nella maggior parte dei sistemi operativi.

Basta caricare i dizionari prima di avviare gli addetti e metterli da qualche parte in cui saranno raggiungibili; il posto più semplice è globalmente a un modulo. Saranno leggibili dagli operai.

from multiprocessing import Pool 

buf = "" 

def f(x): 
    buf.find("x") 
    return 0 

if __name__ == '__main__': 
    buf = "a" * 1024 * 1024 * 1024 
    pool = Pool(processes=1) 
    result = pool.apply_async(f, [10]) 
    print result.get(timeout=5) 

Questo utilizza solo 1 GB di memoria combinato, non 1GB per ogni processo, perché qualsiasi sistema operativo moderno farà un'ombra copy-on-write dei dati creati prima della forcella. Ricorda che le modifiche ai dati non verranno viste da altri lavoratori e, ovviamente, la memoria verrà allocata per tutti i dati che cambierai.

Verrà utilizzato un po 'di memoria: la pagina di ciascun oggetto contenente il conteggio dei riferimenti verrà modificata, quindi verrà allocata. Se questo dipende dipende dai dati.

Questo funzionerà su qualsiasi sistema operativo che implementa il normale biforcazione. Non funzionerà su Windows; il suo modello di processo (paralizzato) richiede il rilancio dell'intero processo per ciascun lavoratore, quindi non è molto utile per la condivisione dei dati.

+1

Funziona con Windows 7 (che è sicuramente un sistema operativo moderno?) –

+0

@Seun: non lo so; prova a provarlo Dubito che il suo modello di processo sia più moderno delle versioni precedenti; Windows è sempre stato nei periodi bui a tale proposito. –

+1

(Niente come SO per i downvotes casuali, errati.) –

2

Per motivi di risparmio di tempo e non dover eseguire il debug problemi a livello di sistema, forse si potrebbe dividere il dizionario record di 5,8 milioni in tre set di ~ 2 milioni ciascuno, ed eseguire il lavoro 3 volte.

+0

che potevo ma non è una soluzione ottimale, come, in ogni modo, alla fine avrei dovuto ricostruire l'intero dizionario e utilizzarlo per altre operazioni –

+0

Poi suona come il vostro compito sarebbe opportuno per Hadoop/MapReduce ... Forse dovresti verificarlo. – Fragsworth

6

Perché non provare questo con un database? I database non sono limitati a ram dirabili/fisici e sono sicuri per l'utilizzo multithread/processo.

0

Penso che il problema che stavi incontrando fosse il tavolo di dettatura o hash che si ridimensionava man mano che cresceva. Inizialmente, il dict ha un numero fisso di bucket disponibili. Non sono sicuro di Python, ma so che Perl inizia con 8 e poi quando i bucket sono pieni, l'hash viene ricreato da altri 8 (es.8, 16, 32, ...).

Il bucket è un percorso di atterraggio per l'algoritmo hash. Gli 8 slot non significano 8 voci, significa 8 posizioni di memoria. Quando viene aggiunto il nuovo elemento, viene generato un hash per quella chiave, quindi viene memorizzato in quel bucket.

Questo è il punto in cui entrano in gioco le collisioni. Più elementi si trovano in un bucket, più lenta sarà la funzione, poiché gli elementi vengono aggiunti in sequenza a causa del dimensionamento dinamico dello slot.

Un problema che si può verificare è che le chiavi sono molto simili e producono lo stesso risultato di hash, ovvero la maggior parte delle chiavi si trovano nello stesso slot. La pre-allocazione dei bucket hash consentirà di eliminare questo problema e di migliorare effettivamente i tempi di elaborazione e la gestione delle chiavi, inoltre non è più necessario eseguire tutte le operazioni di swap.

Tuttavia, penso che si sia ancora limitati alla quantità di memoria contigua libera e alla fine sarà necessario andare alla soluzione di database.

nota a margine: Sono ancora nuovo in Python, so che in Perl è possibile visualizzare le statistiche di hash eseguendo print% HASHNAME, mostrerà la distribuzione dell'utilizzo del bucket. Aiuta a identificare i conteggi delle collisioni - nel caso in cui sia necessario pre-allocare i bucket. Questo può essere fatto anche in Python?

Rich