Tengo una matriz SciPy de 60 GB (matriz) que debo compartir entre más de 5 multiprocessing
Process
objetos. He visto numpy-sharedmem y leí esta discusión en la lista SciPy. Parece haber dos enfoques: numpy-sharedmem
usar multiprocessing.RawArray()
ay mapear NumPy dtype
sa ctype
s. Ahora, numpy-sharedmem
parece ser el camino a seguir, pero todavía tengo que ver un buen ejemplo de referencia. No necesito ningún tipo de bloqueos, ya que la matriz (en realidad una matriz) será de solo lectura. Ahora, debido a su tamaño, me gustaría evitar una copia. Se suena como el método correcto es crear la única copia de la matriz como una sharedmem
matriz y, a continuación, pasar a laProcess
objetos? Un par de preguntas específicas:
¿Cuál es la mejor manera de pasar los identificadores de sharedmem a subes
Process()
? ¿Necesito una cola solo para pasar una matriz? ¿Sería mejor una pipa? ¿Puedo pasarlo como un argumento alProcess()
init de la subclase (donde supongo que está en escabeche)?En la discusión que vinculé anteriormente, ¿se menciona que
numpy-sharedmem
no es seguro para 64 bits? Definitivamente estoy usando algunas estructuras que no son direccionables de 32 bits.¿Hay compensaciones en el
RawArray()
enfoque? ¿Más lento, más buggier?¿Necesito alguna asignación ctype-to-dtype para el método numpy-sharedmem?
¿Alguien tiene un ejemplo de código OpenSource haciendo esto? Soy un aprendiz muy práctico y es difícil hacer que esto funcione sin ningún buen ejemplo que mirar.
Si hay alguna información adicional que pueda proporcionar para ayudar a aclarar esto para otros, comente y la agregaré. ¡Gracias!
Esto debe ejecutarse en Ubuntu Linux y tal vez Mac OS, pero la portabilidad no es una gran preocupación.
multiprocessing
hacer una copia de todo para cada proceso.target_function
). El sistema operativo copiará partes de la memoria de los padres en el espacio de la memoria del niño solo con modificaciones.Respuestas:
@Velimir Mlaker dio una gran respuesta. Pensé que podría agregar algunos comentarios y un pequeño ejemplo.
(No pude encontrar mucha documentación sobre sharedmem; estos son los resultados de mis propios experimentos).
target
yargs
paraProcess
. Esto es potencialmente mejor que usar una variable global.Ejemplo
#!/usr/bin/env python from multiprocessing import Process import sharedmem import numpy def do_work(data, start): data[start] = 0; def split_work(num): n = 20 width = n/num shared = sharedmem.empty(n) shared[:] = numpy.random.rand(1, n)[0] print "values are %s" % shared processes = [Process(target=do_work, args=(shared, i*width)) for i in xrange(num)] for p in processes: p.start() for p in processes: p.join() print "values are %s" % shared print "type is %s" % type(shared[0]) if __name__ == '__main__': split_work(4)
Salida
values are [ 0.81397784 0.59667692 0.10761908 0.6736734 0.46349645 0.98340718 0.44056863 0.10701816 0.67167752 0.29158274 0.22242552 0.14273156 0.34912309 0.43812636 0.58484507 0.81697513 0.57758441 0.4284959 0.7292129 0.06063283] values are [ 0. 0.59667692 0.10761908 0.6736734 0.46349645 0. 0.44056863 0.10701816 0.67167752 0.29158274 0. 0.14273156 0.34912309 0.43812636 0.58484507 0. 0.57758441 0.4284959 0.7292129 0.06063283] type is <type 'numpy.float64'>
Esta pregunta relacionada puede resultar útil.
fuente
Si está en Linux (o en cualquier sistema compatible con POSIX), puede definir esta matriz como una variable global.
multiprocessing
usafork()
en Linux cuando inicia un nuevo proceso hijo. Un proceso hijo recién generado comparte automáticamente la memoria con su padre siempre que no lo cambie ( copiar al escribir mecanismo de ).Dado que está diciendo "No necesito ningún tipo de bloqueos, ya que la matriz (en realidad una matriz) será de solo lectura", aprovechar este comportamiento sería un enfoque muy simple pero extremadamente eficiente: todos los procesos secundarios accederán los mismos datos en la memoria física al leer esta gran matriz numérica.
No entregue su matriz al
Process()
constructor, esto dará instruccionesmultiprocessing
apickle
los datos al niño, lo cual sería extremadamente ineficiente o imposible en su caso. En Linux, justo despuésfork()
del hijo hay una copia exacta del padre que usa la misma memoria física, por lo que todo lo que necesita hacer es asegurarse de que la variable de Python 'que contiene' la matriz sea accesible desde latarget
función que le entregóProcess()
. Normalmente, esto se puede lograr con una variable 'global'.Código de ejemplo:
from multiprocessing import Process from numpy import random global_array = random.random(10**4) def child(): print sum(global_array) def main(): processes = [Process(target=child) for _ in xrange(10)] for p in processes: p.start() for p in processes: p.join() if __name__ == "__main__": main()
En Windows, que no es compatible
fork()
,multiprocessing
se usa la llamada API win32CreateProcess
. Crea un proceso completamente nuevo a partir de cualquier ejecutable. Es por eso que en Windows se requiere recoger datos para el niño si se necesitan datos que se han creado durante el tiempo de ejecución del padre.fuente
multiprocessing
procesos secundarios podría terminar colgando ( normalmente utilizando 1 / n de un procesador en lugar de n procesadores) al realizar operaciones de álgebra lineal en una matriz / matriz global compartida. El conocido conflicto multiproceso con OpenBLAS parece extenderse a Pythonmultiprocessing
fork
para pasar los parámetros dadosProcess
, en lugar de serializarlos? Es decir, ¿no se podríafork
aplicar al proceso principal justo antes de quechild
se llame, de modo que el valor del parámetro todavía esté disponible en el sistema operativo? ¿Parecería ser más eficiente que serializarlo?fork()
no está disponible en Windows, se ha dicho en mi respuesta y varias veces en los comentarios. Sé que esta era su pregunta inicial, y yo contesté cuatro comentarios por encima de esto : "el compromiso es utilizar el mismo método de transferencia de parámetros en ambas plataformas de forma predeterminada, para una mejor capacidad de mantenimiento y para garantizar la igualdad de comportamiento.". Ambas formas tienen sus ventajas y desventajas, por lo que en Python 3 existe una mayor flexibilidad para que el usuario elija el método. Esta discusión no es productiva sin hablar de detalles, lo que no deberíamos hacer aquí.Puede estar interesado en un pequeño fragmento de código que escribí: github.com/vmlaker/benchmark-sharedmem
El único archivo de interés es
main.py
. Es un punto de referencia de numpy-sharedmem : el código simplemente pasa matrices (ya seanumpy
osharedmem
) a procesos generados, a través de Pipe. Los trabajadores simplemente recurrensum()
a los datos. Solo estaba interesado en comparar los tiempos de comunicación de datos entre las dos implementaciones.También escribí otro código más complejo: github.com/vmlaker/sherlock .
Aquí utilizo el módulo numpy-sharedmem para el procesamiento de imágenes en tiempo real con OpenCV; las imágenes son matrices NumPy, según la
cv2
API más nueva de OpenCV . Las imágenes, en realidad referencias a las mismas, se comparten entre procesos a través del objeto de diccionario creado desdemultiprocessing.Manager
(en lugar de usar Queue o Pipe). Obtengo grandes mejoras de rendimiento en comparación con el uso de matrices NumPy simples.Tubería frente a cola :
En mi experiencia, IPC con Pipe es más rápido que Queue. Y eso tiene sentido, ya que Queue agrega bloqueo para que sea seguro para múltiples productores / consumidores. Pipe no lo hace. Pero si solo tiene dos procesos hablando de un lado a otro, es seguro usar Pipe o, como dicen los documentos:
sharedmem
seguridad :El principal problema con el
sharedmem
módulo es la posibilidad de pérdida de memoria al salir del programa de forma incorrecta. Esto se describe en una extensa discusión aquí . Aunque el 10 de abril de 2011 Sturla menciona una solución a la pérdida de memoria, todavía he experimentado fugas desde entonces, usando ambos repositorios, el de Sturla Molden en GitHub ( github.com/sturlamolden/sharedmem-numpy ) y el de Chris Lee-Messer en Bitbucket ( bitbucket.org/cleemesser/numpy-sharedmem ).fuente
sharedmem
embargo, la fuga de memoria parece un gran problema. ¿Alguna pista para resolver eso?sharedmem
módulo, como referencia.Si su matriz es tan grande, puede usar
numpy.memmap
. Por ejemplo, si tiene una matriz almacenada en el disco, digamos'test.array'
, puede usar procesos simultáneos para acceder a los datos en ella incluso en el modo de "escritura", pero su caso es más simple ya que solo necesita el modo de "lectura".Creando la matriz:
a = np.memmap('test.array', dtype='float32', mode='w+', shape=(100000,1000))
Luego puede completar esta matriz de la misma manera que lo hace con una matriz ordinaria. Por ejemplo:
a[:10,:100]=1. a[10:,100:]=2.
Los datos se almacenan en el disco cuando borra la variable
a
.Posteriormente podrá utilizar múltiples procesos que accederán a los datos en
test.array
:# read-only mode b = np.memmap('test.array', dtype='float32', mode='r', shape=(100000,1000)) # read and writing mode c = np.memmap('test.array', dtype='float32', mode='r+', shape=(100000,1000))
Respuestas relacionadas:
Trabajando con big data en Python y numpy, no hay suficiente RAM, ¿cómo guardar resultados parciales en el disco?
¿Es posible mapear datos no contiguos en el disco a una matriz con Python?
fuente
También puede resultarle útil echar un vistazo a la documentación de pyro, ya que si puede dividir su tarea de manera adecuada, podría usarla para ejecutar diferentes secciones en diferentes máquinas, así como en diferentes núcleos en la misma máquina.
fuente
¿Por qué no utilizar subprocesos múltiples? Los recursos del proceso principal pueden ser compartidos por sus subprocesos de forma nativa, por lo que el subproceso múltiple es, obviamente, una mejor manera de compartir objetos que pertenecen al proceso principal.
Si le preocupa el mecanismo GIL de Python, tal vez pueda recurrir al
nogil
denumba
.fuente