2012-06-01 22 views
18

Ho scritto un algoritmo che acquisisce dati geospaziali ed esegue una serie di passaggi. I dati di input sono uno shapefile di poligoni e raster covariati per un'area di studio raster di grandi dimensioni (~ 150 milioni di pixel). I passi sono i seguenti:Progettazione multiprocessing Python

  1. punti del campione dall'interno poligoni dello shapefile
  2. Per ogni punto di campionamento, i valori estratto del raster covariate
  3. costruire un modello predittivo sui punti di campionamento
  4. covariate Estratto per griglia di destinazione punti
  5. Applicare modello predittivo per indirizzare griglia
  6. previsioni scrittura a una serie di griglie di uscita

L'intero processo deve essere ripetuto un numero di volte (ad esempio 100) ma ogni iterazione richiede attualmente più di un'ora quando viene elaborata in serie. Per ogni iterazione, le parti che richiedono più tempo sono i passaggi 4 e 5. Poiché la griglia di destinazione è così grande, l'ho elaborata in blocco (ad esempio 1000 righe) alla volta.

ho una CPU 6-core con 32 Gb RAM, quindi all'interno di ogni iterazione, ho avuto un andare a utilizzando moduli di Python multiprocessing con un oggetto Pool per elaborare un numero di blocchi contemporaneamente (passaggi 4 e 5) e poi scrivere l'output (le previsioni) al set comune di griglie di output utilizzando una funzione di callback che chiama una funzione di scrittura di output globale. Questo sembra funzionare, ma non è più veloce (in realtà, è probabilmente più lento) rispetto all'elaborazione di ogni blocco in serie.

Quindi la mia domanda è, c'è un modo più efficiente per farlo? Sono interessato alla classe Queue del modulo multiprocessing, ma non sono sicuro di come funzioni. Ad esempio, mi chiedo se è più efficiente avere una coda che esegue i passaggi 4 e 5, quindi passa i risultati a un'altra coda che esegue il passaggio 6. O è anche ciò che è per Queue?

Qualsiasi suggerimento sarebbe apprezzato.

+0

Qual è il massimo RSS durante l'intero processo? Forse sarebbe più semplice eseguire questa cosa sei volte contemporaneamente se tutto andasse bene nella memoria ... – sarnold

+0

Sei davvero legato alla CPU? Questo sembra un problema legato all'I/O. – stark

+0

@Sarnold: non si adatta alla memoria, questo è il problema ... – hendra

risposta

3

Lo stato attuale delle funzionalità multi-elaborazione di Python non sono l'ideale per CPU bound in lavorazione. Temo di dirti che non c'è modo di farlo funzionare più velocemente usando il modulo multiprocessing né è il tuo uso di multiprocessing questo è il problema.

Il vero problema è che Python è ancora vincolato dalle regole dello GlobalInterpreterLock(GIL) (consiglio vivamente lo slides). Ci sono stati alcuni eccitanti teorici e experimental advances su come aggirare il GIL. L'evento Python 3.2 contiene un nuovo GIL che risolve alcuni dei problemi, ma ne introduce altri.

Per ora, è più rapido eseguire molti processi Python con un singolo thread seriale che tentare di eseguire molti thread all'interno di un processo. Questo ti permetterà di evitare problemi nell'acquisire GIL tra i thread (avendo effettivamente più GIL). Ciò tuttavia è utile solo se l'overhead IPC tra i processi Python non eclissa i vantaggi dell'elaborazione.

Eli Bendersky ha scritto un discreto overview article sulle sue esperienze con il tentativo di rendere un processo legato alla CPU più veloce con il multiprocessing.

Vale la pena notare che PEP 371 aveva il desiderio di 'side-step' GIL con l'introduzione del modulo multiprocessing (già confezionati non standard denominato pyProcessing). Comunque GIL sembra giocare un ruolo troppo grande nell'interprete Python per farlo funzionare bene con algoritmi legati alla CPU. Molte persone diverse hanno lavorato sulla rimozione/riscrittura di GIL, ma nulla ha reso abbastanza la trazione per trasformarlo in una versione di Python.

+0

Ho il sospetto che questo sarebbe il caso. Controllerò sicuramente i tuoi link, grazie. Con una presa leggermente diversa: suppongo che il modulo Parallel Python sia soggetto agli stessi tipi di problemi. Ma cosa succede se, per esempio, PP è stato utilizzato per dividere l'attività tra più computer? – hendra

+0

@npo qualche aggiornamento? –

+0

Siamo spiacenti, non ancora! – hendra

0

Ti consiglio innanzitutto di controllare quali aspetti del tuo codice impiegano più tempo, quindi dovrai profilarlo, ho usato lo http://packages.python.org/line_profiler/#line-profiler con molto successo, anche se richiede cython.

Per quanto riguarda le code, sono utilizzate principalmente per la condivisione di dati/sincronizzazione di thread, sebbene l'abbia usata raramente. Uso sempre il multiprocessing.

Io per lo più seguire la mappa ridurre filosofia, che è semplice e pulito, ma ha alcuni grandi investimenti, dal momento che i valori devono essere confezionato in dizionari e copiato in tutto ogni processo, quando si applica la funzione di carta ...

Puoi provare a segmentare il tuo file e applicare l'algoritmo a diversi set.

1

Alcuni degli esempi di multiprocessing su python.org non sono molto chiari IMO ed è facile iniziare con un design difettoso. Ecco un esempio semplicistico che ho fatto per ottenerlo iniziato su un progetto:

import os, time, random, multiprocessing 
def busyfunc(runseconds): 
    starttime = int(time.clock()) 
    while 1: 
     for randcount in range(0,100): 
      testnum = random.randint(1, 10000000) 
      newnum = testnum/3.256 
     newtime = int(time.clock()) 
     if newtime - starttime > runseconds: 
      return 

def main(arg): 
    print 'arg from init:', arg 
    print "I am " + multiprocessing.current_process().name 

    busyfunc(15) 

if __name__ == '__main__': 

    p = multiprocessing.Process(name = "One", target=main, args=('passed_arg1',)) 
    p.start() 

    p = multiprocessing.Process(name = "Two", target=main, args=('passed_arg2',)) 
    p.start() 

    p = multiprocessing.Process(name = "Three", target=main, args=('passed_arg3',)) 
    p.start() 

    time.sleep(5) 

Questo dovrebbe esercitare 3 processori per 15 secondi. Dovrebbe essere facile modificarlo per altro. Forse questo ti aiuterà a eseguire il debug del tuo codice corrente e assicurarti di generare realmente più processi indipendenti.

Se è necessario condividere i dati a causa di RAM limitazioni, allora vi consiglio questo: http://docs.python.org/library/multiprocessing.html#sharing-state-between-processes

1

Poiché Python non è veramente destinata a fare intensiva numero-cunching, io di solito avviare la conversione parti time-critical di un programma Python in C/C++ e velocizza molto le cose.

Inoltre, il multithreading Python non è molto buono. Python continua a utilizzare un semaforo globale per tutti i tipi di cose. Quindi, anche quando usi le discussioni offerte da python, le cose non diventeranno più veloci. I thread sono utili per le applicazioni, in cui i thread in genere attendono cose come IO.

Quando si crea un modulo C, è possibile rilasciare manualmente il semaforo globale durante l'elaborazione dei dati (quindi, ovviamente, non accedere più ai valori python).

Ci vuole un po 'di pratica usando l'API C, ma è chiaramente strutturato e molto più facile da usare rispetto, ad esempio, all'API nativa Java.

Vedere "estensione e incorporamento" nella documentazione di Python.

In questo modo è possibile rendere il tempo di parti critiche in C/C++, e le parti più lente con il lavoro di programmazione più veloce in python ...