2016-05-31 54 views
12

Vorrei un aiuto per capire esattamente cosa ho fatto/perché il mio codice non funziona come mi aspetterei.Python - Ciclo di parallelizzazione con joblib

Ho iniziato a usare joblib per cercare di velocizzare il mio codice eseguendo un (grande) ciclo in parallelo.

Io lo utilizzo in questo modo:

from joblib import Parallel, delayed 
def frame(indeces, image_pad, m): 

    XY_Patches = np.float32(image_pad[indeces[0]:indeces[0]+m, indeces[1]:indeces[1]+m, indeces[2]]) 
    XZ_Patches = np.float32(image_pad[indeces[0]:indeces[0]+m, indeces[1],     indeces[2]:indeces[2]+m]) 
    YZ_Patches = np.float32(image_pad[indeces[0],     indeces[1]:indeces[1]+m, indeces[2]:indeces[2]+m]) 

    return XY_Patches, XZ_Patches, YZ_Patches 


def Patch_triplanar_para(image_path, patch_size): 

    Image, Label, indeces = Sampling(image_path) 

    n = (patch_size -1)/2 
    m = patch_size 

    image_pad = np.pad(Image, pad_width=n, mode='constant', constant_values = 0) 

    A = Parallel(n_jobs= 1)(delayed(frame)(i, image_pad, m) for i in indeces) 
    A = np.array(A) 
    Label = np.float32(Label.reshape(len(Label), 1)) 
    R, T, Y = np.hsplit(A, 3) 

    return R, T, Y, Label 

ho avuto modo di sperimentare con "n_jobs", in attesa che l'aumento di questo accelererà la mia funzione. Tuttavia, con l'aumento di n_jobs, le cose rallentano in modo significativo. Quando si esegue questo codice senza "Parallelo", le cose sono più lente, finché non aumento il numero di lavori da 1.

Perché è questo il caso? Ho capito che più lavori ho eseguito, più veloce è la sceneggiatura? sto usando questo sbagliato?

Grazie!

+0

Innanzitutto, quante CPU o core hai nel computer che usi? In secondo luogo, 'n_jobs' imposta il numero massimo di lavori in esecuzione contemporaneamente. Hai provato 'n_jobs = -1'? Questo dovrebbe usare tutte le CPU nel tuo computer. In terzo luogo, quanto è grande questo 'indeces' del tuo ciclo for? – fedepad

+0

Ho 24 core e un'enorme quantità di memoria. indeces ha circa 10.000 voci, quindi ho pensato che sarebbe stato un buon parallelismo. Posso provare n_jobs = -1 e riferire. – JB1

+0

Sì. Posso immaginare che se si aumentano n_jobs da 1 a max (n_jobs = 23, njobs = -1), si raggiungerà un punto in cui l'incremento di questo numero comporterà un ulteriore sovraccarico, quindi è necessario trovare un punto debole. Certo se puoi usare il backend = "threading" potrebbe essere forse meglio ma devi sperimentare. – fedepad

risposta

3

Forse il tuo problema è causato dal fatto che image_pad è un array di grandi dimensioni. Nel codice, si utilizza il backend predefinito multiprocessing di joblib. Questo backend crea un pool di worker, ognuno dei quali è un processo Python. I dati di input per la funzione vengono quindi copiati n_jobs volte e trasmessi a ciascun operatore nel pool, il che può comportare un sovraccarico serio. Citando documenti s' joblib:

Per impostazione predefinita i lavoratori della piscina sono processi Python reali biforcuta utilizzando il modulo multiprocessing della libreria standard di Python quando n_jobs = 1. Gli argomenti passati come input per la chiamata in parallelo sono! serializzato e riallocato nella memoria di ciascun processo di lavoro.

Questo può essere problematico per argomenti di grandi dimensioni in quanto saranno ridistribuiti n_jobs volte dai lavoratori.

Poiché questo problema può spesso verificarsi nel calcolo scientifico con datastrutture basate su numpy, joblib.Parallel offre una gestione speciale per array di grandi dimensioni per scaricarli automaticamente sul filesystem e passare un riferimento al worker per aprirli come mappa di memoria su tale file usando la sottoclasse numpy.memmap di numpy.ndarray. Ciò rende possibile condividere un segmento di dati tra tutti i processi di lavoro.

Nota: quanto segue si applica solo con il backend predefinito di "multiprocessing". Se il tuo codice può rilasciare GIL, allora usare backend = "threading" è ancora più efficiente.

Quindi, se questo è il vostro caso, si dovrebbe passare al backend threading, se si è in grado di rilasciare il blocco interprete globale quando si chiama frame, o passare l'approccio memoria condivisa di joblib.

Il docs afferma che joblib fornisce una conversione automatica memmap che potrebbe essere utile.

2

È possibile che il problema che si incontra sia fondamentale per la natura del compilatore Python.

Se si legge "https://www.ibm.com/developerworks/community/blogs/jfp/entry/Python_Is_Not_C?lang=en", è possibile vedere da un professionista specializzato nell'ottimizzazione e parallelizzazione del codice Python che l'iterazione tramite loop di grandi dimensioni è un'operazione intrinsecamente lenta per l'esecuzione di un thread python. Pertanto, la generazione di più processi che passano attraverso gli array rallenta solo le cose.

Tuttavia, ci sono cose che si possono fare.

Il Cython e Numba compilatori sono entrambi progettati per ottimizzare il codice che è simile a/stile C C++ (cioè il vostro caso) - in particolare Numba di nuove @vectorise decoratori permettono funzioni scalari di prendere in e applicare operazioni su grandi array con grandi array in modo parallelo (target=Parallel).

Non capisco abbastanza il tuo codice per dare un esempio di implementazione, ma prova questo! Questi compilatori, usati nel modo corretto, hanno portato aumenti di velocità del 3000.000% per i processi paralleli in passato!