Paralelizando una operación vectorial de Numpy

numpy.sin() , por ejemplo, numpy.sin()

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, así que me gustaría usarlos. (Es posible que la sobrecarga no valga la pena para algo como numpy.sin() pero la función que realmente quiero usar es un poco más complicada, y trabajaré con una gran cantidad de datos).

Es este el mejor método (leído: el más inteligente o el más rápido):

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

¿O hay una mejor manera de hacer esto?

Hay una mejor manera: numexpr

Ligeramente reformulada desde su página principal:

Es una máquina virtual de múltiples subprocesos escrita en C que analiza las expresiones, las reescribe de manera más eficiente y las comstack sobre la marcha en un código que se acerca al rendimiento paralelo óptimo tanto para la memoria como para las operaciones delimitadas por CPU.

Por ejemplo, en mi máquina de 4 núcleos, la evaluación de un seno es un poco menos de 4 veces más rápida que la cantidad.

 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, incluyendo funciones soportadas aquí . Tendrá que verificar o darnos más información para ver si numexpr puede evaluar su función más complicada.

Bueno, esta es una nota interesante si ejecutas 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 eso, entonces, ¿qué está pasando?

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

sip numpy.sin se implementa en c como tal, no puede usarlo directamente con multiprocesamiento.

Así que tenemos que envolverlo con otra función.

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, tampoco esperaba que, bueno, hay un par de problemas para empezar, estamos usando una función python incluso si es solo una envoltura frente a una función c pura, y también existe la sobrecarga de copiar los valores, el multiproceso por defecto no lo hace. t compartir datos, como tal, cada valor debe ser copiado hacia atrás / adelante.

Tenga en cuenta que si segmenta adecuadamente 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 sacar de esto? El multiprocesamiento es excelente, pero siempre debemos probarlo y compararlo, a veces es más rápido y, a veces, más lento, dependiendo de cómo se use …

Por supuesto, no está utilizando numpy.sin pero le recomendaría que primero verifique que, de hecho, el multiprocesamiento acelerará el cálculo, tal vez la sobrecarga de copiar valores hacia atrás / adelante pueda afectarle.

De cualquier manera, también creo que usar pool.map es el mejor y más seguro método de multiprocesamiento de código …

Espero que esto ayude.

SciPy en realidad tiene una buena reseña sobre este tema aquí: http://wiki.scipy.org/ParallelProgramming