2012-07-11 15 views
36
uso

Vamos, por ejemplo, numpy.sin()Paralelización una operación vectorial Numpy

El siguiente código devolverá el valor del seno para cada valor de la matriz a:

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

Pero mi máquina tiene 32 núcleos , entonces me gustaría hacer uso de ellos. (La sobrecarga puede no ser válida para algo como numpy.sin() pero la función que realmente quiero usar es bastante más complicada, y trabajaré con una gran cantidad de datos.)

¿Es esto lo mejor? (Léase: más inteligente o más rápido) método:

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

o hay una mejor manera de hacerlo?

+0

Si va a usar 'pool.map()', debe usar 'math.sin' porque es más rápido que' numpy.sin'. Referencia: http://stackoverflow.com/questions/3650194/are-numpys-math-functions-faster-than-pythons. – EOL

+2

Para 'numpy.sin', el [wiki oficial numpy/scipy] (http://wiki.scipy.org/ParallelProgramming) dice que debería funcionar en paralelo si [compila numpy con openmp activado] (https: // software.intel.com/en-us/articles/numpyscipy-with-intel-mkl). – ziyuang

+0

También podría usar [Bohrium] (http://bohrium.readthedocs.io/): debería ser tan simple como reemplazar su primera línea con 'import bohrium numpy' ... – j08lue

Respuesta

48

Hay es una mejor manera: numexpr

modificado ligeramente desde su página principal:

Es un multi-hilo VM escrito en C que analiza las expresiones, los reescriba manera más eficiente, y los compila en vuela al código que se acerca al rendimiento paralelo óptimo para las operaciones limitadas de la memoria y la CPU.

Por ejemplo, en mi máquina de 4 núcleos, la evaluación de un seno es apenas un poco menos de 4 veces más rápido que el 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 

Documentación, incluidas las funciones compatibles here. Deberá verificar o darnos más información para ver si su función más complicada puede ser evaluada por numexpr.

+4

Escribí mi código haciendo uso de numexpr y funciona unas 6 veces más rápido que el mismo código usando numpy ¡Muchas gracias por la sugerencia! Ahora me pregunto por qué numexpr no está más extendido. En toda mi búsqueda de paquetes numéricos en Python, no me he encontrado hasta ahora. También hubo una pequeña molestia en numexpr que no soporta indexación de matriz, pero eso no fue un revés. – user1475412

+1

Quizás también deba consultar Theano y Cython. Theano puede usar GPU, pero realmente no lo he usado, así que no puedo darte un ejemplo. – jorgeca

+2

Una razón por la que numexpr no está más extendido es que creo que es más engorroso de usar que NumPy puro (como en el ejemplo anterior). Sin embargo, es genial para acelerar fácilmente los cálculos de NumPy que para correr más rápido. – EOL

17

Bueno, esto es una especie de interesante nota si ejecuta los siguientes comandos:

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 

no esperaba que, por lo cuál pasando, así:

>>> 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. 

numpy.sin sí está implementado en C, como tal, realmente no se puede utilizar directamente con multiprocesamiento.

así que tenemos que envolverlo con otra función

Potencia:

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, no esperaba que, o bien, así theres un par de temas para empezar estamos utilizando una función de Python incluso si solo se trata de un wrapper frente a una función c pura, y también existe la sobrecarga de copiar los valores, el multiprocesamiento de forma predeterminada no comparte datos, por lo tanto, cada valor debe copiarse hacia adelante o hacia atrás.

tenga en cuenta que si bien el segmento nuestros datos:

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 

Entonces, ¿qué podemos obtener de esto, el multiprocesamiento está muy bien, pero que siempre debe comprobar y comparar que a veces es más rápido ya veces su lento, dependiendo de cómo su usado ...

Concedido que usted no está usando numpy.sin pero otra función que le recomendaría primero verifique que de hecho multiprocesamiento acelerará el cálculo, tal vez la sobrecarga de copiar valores hacia atrás/adelante puede afectarlo.

De cualquier manera Yo también creo que el uso de pool.map es el mejor método, más segura de código multihilo ...

espero que esto ayude.

+0

¡Muchas gracias! Esto es muy informativo Había supuesto, en base a lo que leí, que la función 'map' de' Pool' funcionaría de manera un tanto inteligente en los datos, pero supongo que la segmentación primero hace una gran diferencia. ¿Hay alguna otra forma de evitar la sobrecarga de los procesos que copian los datos? ¿Esperas alguna diferencia de rendimiento si utilizo 'math.sin()' en su lugar? – user1475412

+0

De hecho, probé 'math.sin' y bueno es mucho más lento, incluso multiproceso y luego un solo subproceso numpy.sin, aunque fue más rápido (tomó' 6,435199' que el multiproceso 'numpy.sin' que tomó' 10,5', probablemente debido al hecho de que 'numpy.sin' puede manejar arreglos, los chicos' numpy' son realmente buenos en matemáticas;), sí, hay una manera de usar 'shared memory' http://docs.python.org/library/multiprocessing .html, pero no lo use, es bastante peligroso y tiene un soporte limitado, o al menos, pisa con cuidado. –

+0

Si solo lee, puede ser seguro, el subproceso solo necesita hacer un seguimiento de su índice o subconjunto de índices correspondiente ... –