2012-07-11 22 views
36
uso

Let, per esempio, numpy.sin()parallelizzare un'operazione vettore Numpy

Il codice seguente restituisce il valore del seno per ogni valore della matrice a:

import numpy 
a = numpy.arange(1000000) 
result = numpy.sin(a) 

Ma la macchina ha 32 core , quindi mi piacerebbe farne uso. (Il sovraccarico potrebbe non valerne la pena per qualcosa come numpy.sin() ma la funzione che effettivamente desidero utilizzare è un po 'più complicata e lavorerò con un'enorme quantità di dati.)

È il migliore (leggi: metodo più intelligente o più veloce):

from multiprocessing import Pool 
if __name__ == '__main__': 
    pool = Pool() 
    result = pool.map(numpy.sin, a) 

oppure c'è un modo migliore per farlo?

+0

Se si utilizza 'pool.map()', si dovrebbe usare 'math.sin' perché è più veloce di' numpy.sin'. Riferimento: http://stackoverflow.com/questions/3650194/are-numpys-math-functions-faster-than-pythons. – EOL

+2

Per 'numpy.sin', il [wiki numpy/scipy ufficiale (http://wiki.scipy.org/ParallelProgramming) dice che dovrebbe funzionare in parallelo se tu [compili numpy con openmp attivato] (https: // software.intel.com/en-us/articles/numpyscipy-with-intel-mkl). – ziyuang

+0

Puoi anche usare [Bohrium] (http://bohrium.readthedocs.io/): Dovrebbe essere semplice come sostituire la prima linea con 'import bohrium as numpy' ... – j08lue

risposta

48

Ci è un modo migliore: numexpr

leggermente diversa dalla loro pagina principale:

Si tratta di un multi-threaded VM scritto in C che analizza le espressioni, li riscrive in modo più efficiente, e li compila il il volo in codice che si avvicina alle prestazioni parallele ottimali sia per le operazioni di memoria che per le operazioni con CPU.

Ad esempio, nella mia macchina a 4 core, la valutazione di un seno è leggermente inferiore a 4 volte più veloce di numpy.

In [1]: import numpy as np 
In [2]: import numexpr as ne 
In [3]: a = np.arange(1000000) 
In [4]: timeit ne.evaluate('sin(a)') 
100 loops, best of 3: 15.6 ms per loop  
In [5]: timeit np.sin(a) 
10 loops, best of 3: 54 ms per loop 

documentazione, comprese le funzioni supportate here. Dovrai controllare o fornirci ulteriori informazioni per vedere se la funzione più complicata può essere valutata da numexpr.

+4

Ho scritto il mio codice facendo uso di numexpr e si comporta circa 6 volte più velocemente dello stesso codice usando NumPy. Grazie mille per il suggerimento! Ora mi chiedo perché numexpr non sia più diffuso. In tutta la mia ricerca di pacchetti numerici in Python, non l'ho mai incontrato fino ad ora. C'era anche qualche piccolo fastidio in numexpr che non supportava l'indicizzazione degli array, ma non era certo una battuta d'arresto. – user1475412

+1

Forse dovresti controllare anche Theano e Cython. Theano può utilizzare GPU, ma non l'ho davvero usato quindi non posso fornirti un esempio. – jorgeca

+2

Una ragione per cui numexpr non è più diffusa è che suppongo che sia più complicato da usare rispetto a NumPy puro (come nell'esempio sopra). È davvero grande per accelerare facilmente i calcoli di NumPy rispetto alla necessità di correre più veloce, però. – EOL

17

Beh, questa è nota piuttosto interessante se si esegue i seguenti comandi:

import numpy 
from multiprocessing import Pool 
a = numpy.arange(1000000)  
pool = Pool(processes = 5) 
result = pool.map(numpy.sin, a) 

UnpicklingError: NEWOBJ class argument has NULL tp_new 

non mi aspettavo che, così che cosa è in corso, ben:

>>> help(numpy.sin) 
    Help on ufunc object: 

sin = class ufunc(__builtin__.object) 
| Functions that operate element by element on whole arrays. 
| 
| To see the documentation for a specific ufunc, use np.info(). For 
| example, np.info(np.sin). Because ufuncs are written in C 
| (for speed) and linked into Python with NumPy's ufunc facility, 
| Python's help() function finds this page whenever help() is called 
| on a ufunc. 

Yep numpy.sin è implementato in C come tale non si può davvero utilizzare direttamente con multiprocessing.

quindi dobbiamo avvolgerlo con un'altra funzione

perf:

import time 
import numpy 
from multiprocessing import Pool 

def numpy_sin(value): 
    return numpy.sin(value) 

a = numpy.arange(1000000) 
pool = Pool(processes = 5) 

start = time.time() 
result = numpy.sin(a) 
end = time.time() 
print 'Singled threaded %f' % (end - start) 
start = time.time() 
result = pool.map(numpy_sin, a) 
pool.close() 
pool.join() 
end = time.time() 
print 'Multithreaded %f' % (end - start) 


$ python perf.py 
Singled threaded 0.032201 
Multithreaded 10.550432 

wow, non mi aspettavo che o, ben theres un paio di problemi per cominciare stiamo utilizzando una funzione di pitone anche se è solo un wrapper contro una pura funzione c, e c'è anche il sovraccarico di copiare i valori, il multiprocessing di default non condivide i dati, in quanto tale ogni valore deve essere copiato avanti/indietro.

fanno notare che, se correttamente segmento nostri dati:

import time 
import numpy 
from multiprocessing import Pool 

def numpy_sin(value): 
    return numpy.sin(value) 

a = [numpy.arange(100000) for _ in xrange(10)] 
pool = Pool(processes = 5) 

start = time.time() 
result = numpy.sin(a) 
end = time.time() 
print 'Singled threaded %f' % (end - start) 
start = time.time() 
result = pool.map(numpy_sin, a) 
pool.close() 
pool.join() 
end = time.time() 
print 'Multithreaded %f' % (end - start) 

$ python perf.py 
Singled threaded 0.150192 
Multithreaded 0.055083 

Allora, cosa possiamo prendere da questa, multiprocessing è grande, ma ci deve sempre verificare e confrontare a volte il suo più veloce e, talvolta, la sua più lenta, a seconda di come la sua usato ...

È garantito che non si sta utilizzando numpy.sin ma un'altra funzione che si consiglia di verificare per prima cosa che la multielaborazione acceleri il calcolo, forse il sovraccarico di copia dei valori indietro/indietro potrebbe influire sull'utente.

Ad ogni modo anche io credo che usare pool.map è il migliore, il metodo più sicuro di codice multithreading ...

Spero che questo aiuta.

+0

Grazie mille! Questo è molto informativo Avevo pensato, basandomi su quello che ho letto, che la funzione 'map' di' Pool' funzionasse in modo un po 'intelligente sui dati, ma immagino che segmentare prima faccia una grande differenza. C'è un altro modo per evitare il sovraccarico dei processi che copiano i dati? Ti aspetti qualche differenza di prestazioni se uso 'math.sin()' invece? – user1475412

+0

Ho effettivamente provato 'math.sin' e anche molto più lento, anche multithreaded quindi numpy.sin single threaded, sebbene fosse più veloce (ci sono voluti' 6.435199's del 'numpy.sin' con multithreading che ha richiesto' 10.5', probabilmente a causa del fatto che 'numpy.sin' è in grado di gestire gli array, i tipi' numpy' sono veramente bravi in ​​matematica;), sì, c'è un modo per usare 'memoria condivisa' http://docs.python.org/library/multiprocessing .html ma per favore non usarlo è abbastanza pericoloso e ha un supporto limitato, o almeno un battistrada con attenzione. –

+0

Se il tuo solo fare legge allora forse è sicuro, il sottoprocesso deve solo tenere traccia del loro indice o sottogruppo corrispondente di indici ... –

Problemi correlati