tamaño - python matrices tutorial
Paralelamente el bucle python con matrices numpy y memoria compartida (3)
Con el soporte paralelo de Cython:
# asd.pyx
from cython.parallel cimport prange
import numpy as np
def foo():
cdef int i, j, n
x = np.zeros((200, 2000), float)
n = x.shape[0]
for i in prange(n, nogil=True):
with gil:
for j in range(100):
x[i,:] = np.cos(x[i,:])
return x
En una máquina de 2 núcleos:
$ cython asd.pyx
$ gcc -fPIC -fopenmp -shared -o asd.so asd.c -I/usr/include/python2.7
$ export OMP_NUM_THREADS=1
$ time python -c ''import asd; asd.foo()''
real 0m1.548s
user 0m1.442s
sys 0m0.061s
$ export OMP_NUM_THREADS=2
$ time python -c ''import asd; asd.foo()''
real 0m0.602s
user 0m0.826s
sys 0m0.075s
Esto funciona bien en paralelo, ya que np.cos
(como otros ufuncs) libera el GIL.
Si quieres usar esto de forma interactiva:
# asd.pyxbdl
def make_ext(modname, pyxfilename):
from distutils.extension import Extension
return Extension(name=modname,
sources=[pyxfilename],
extra_link_args=[''-fopenmp''],
extra_compile_args=[''-fopenmp''])
y (eliminar asd.so
y asd.c
primero):
>>> import pyximport
>>> pyximport.install(reload_support=True)
>>> import asd
>>> q1 = asd.foo()
# Go to an editor and change asd.pyx
>>> reload(asd)
>>> q2 = asd.foo()
Entonces sí, en algunos casos puedes paralelizar simplemente usando hilos. OpenMP es simplemente un envoltorio sofisticado para el enhebrado, por lo que Cython solo es necesario aquí para una sintaxis más fácil. Sin Cython, puede utilizar el módulo de threading
funciona de manera similar al multiprocesamiento (y probablemente de forma más sólida), pero no necesita hacer nada especial para declarar las matrices como memoria compartida.
Sin embargo, no todas las operaciones lanzan el GIL, por lo que YMMV para el rendimiento.
***
Y otro enlace posiblemente útil extraído de otras respuestas de Stackoverflow --- otra interfaz para multiprocesamiento: http://packages.python.org/joblib/parallel.html
Conozco varias preguntas y respuestas sobre este tema, pero no he encontrado una respuesta satisfactoria a este problema en particular:
¿Cuál es la forma más fácil de hacer una paralelización simple de memoria compartida de un bucle python donde las matrices numpy se manipulan a través de funciones numpy / scipy?
No busco la manera más eficiente, solo quería algo simple de implementar que no requiera una reescritura significativa cuando el ciclo no se ejecuta en paralelo. Al igual que OpenMP implementa en idiomas de nivel inferior.
La mejor respuesta que he visto a este respecto es esta , pero esta es una forma bastante torpe que requiere que uno exprese el ciclo en una función que toma un solo argumento, varias líneas de crud de conversión de matriz compartida, parece requerir que la función paralela se llama desde __main__
, y no parece funcionar bien desde el indicador interactivo (donde paso mucho tiempo).
Con toda la simplicidad de Python, ¿esta es realmente la mejor manera de desarrollar un ciclo? De Verdad? Esto es algo trivial para paralelizar en la moda OpenMP.
He leído minuciosamente la documentación opaca del módulo de multiprocesamiento, solo para descubrir que es tan general que parece adecuado para todo menos una simple paralelización de bucle. No estoy interesado en configurar Managers, Proxies, Pipes, etc. Solo tengo un loop simple, completamente paralelo que no tiene comunicación entre tareas. Usar MPI para paralelizar una situación tan simple parece exagerado, sin mencionar que en este caso sería ineficaz para la memoria.
No he tenido tiempo de aprender sobre la multitud de diferentes paquetes paralelos de memoria compartida para Python, pero me preguntaba si alguien tiene más experiencia en esto y me puede mostrar de una manera más simple. No sugiera técnicas de optimización en serie, como Cython (ya lo uso), o el uso de funciones numpy / scipy paralelas, como BLAS (mi caso es más general y más paralelo).
El método .map () de la clase mathDict () en ParallelRegression hace exactamente lo que está buscando en dos líneas de código que deberían ser muy fáciles en un aviso interactivo. Utiliza el verdadero multiprocesamiento, por lo que el requisito de que la función que se ejecutará en paralelo sea inigualable es inevitable, pero proporciona una forma sencilla de recorrer una matriz en memoria compartida desde múltiples procesos.
Supongamos que tiene una función que se puede pickle-able:
def sum_row( matrix, row ):
return( sum( matrix[row,:] ) )
Entonces solo necesitas crear un objeto de mathDict () que lo represente y usar mathDict () .map ():
matrix = np.array( [i for i in range( 24 )] ).reshape( (6, 4) )
RA, MD = mathDictMaker.fromMatrix( matrix, integer=True )
res = MD.map( [(i,) for i in range( 6 )], sum_row, ordered=True )
print( res )
# [6, 22, 38, 54, 70, 86]
La documentación (enlace arriba) explica cómo pasar una combinación de argumentos posicionales y de palabras clave a su función, incluida la matriz en cualquier posición o como argumento de palabra clave. Esto debería permitirle usar casi cualquier función que ya haya escrito sin modificarla.
Usar una operación de mapeo (en este caso multiprocessing.Pool.map()
) es más o menos la forma canónica de paralelizar un ciclo en una sola máquina. A menos y hasta que el map()
incorporado map()
esté siempre paralelo.
Una visión general de las diferentes posibilidades se puede encontrar aquí .
Puede usar openmp con python (o más bien cython), pero no parece exactamente fácil.
IIRC, el punto si solo ejecutar cosas de multiprocesamiento desde __main__
es una necesidad debido a la compatibilidad con Windows. Como Windows carece de fork()
, inicia un nuevo intérprete de python y tiene que importar el código en él.
Editar
Numpy puede paralelizar algunas operaciones como dot()
, vdot()
y innerproduct()
, cuando se configura con una buena biblioteca BLAS de subprocesos múltiples como, por ejemplo, OpenBLAS . (Ver también esta pregunta )
Como las operaciones numpy array son principalmente por elemento, parece posible paralelizarlas. Pero esto implicaría configurar un segmento de memoria compartida para objetos Python, o dividir las matrices en partes y alimentarlas a los diferentes procesos, a diferencia de lo que ocurre con el multiprocessing.Pool
. No importa qué enfoque se tome, incurriría en gastos generales de memoria y procesamiento para administrar todo eso. Uno tendría que realizar pruebas exhaustivas para ver para qué tamaños de matrices realmente valdría la pena el esfuerzo. El resultado de esas pruebas probablemente variará considerablemente según la arquitectura del hardware, el sistema operativo y la cantidad de RAM.