Diseño multiproceso Python.
iteration multiprocessing (4)
He escrito un algoritmo que toma datos geoespaciales y realiza varios pasos. Los datos de entrada son un shapefile de polígonos y rásteres covariados para un área de estudio grande (~ 150 millones de píxeles). Los pasos son los siguientes:
- Puntos de muestra dentro de polígonos del shapefile
- Para cada punto de muestreo, extraiga valores de los rásteres covariados
- Construir un modelo predictivo en los puntos de muestreo.
- Extraer covariables para los puntos de la cuadrícula de destino
- Aplicar el modelo predictivo a la cuadrícula de destino.
- Escribir predicciones a un conjunto de rejillas de salida.
Todo el proceso debe repetirse varias veces (por ejemplo, 100), pero cada iteración actualmente lleva más de una hora cuando se procesa en serie. Para cada iteración, las partes que más tiempo consumen son los pasos 4 y 5. Debido a que la cuadrícula de destino es tan grande, lo he estado procesando un bloque (por ejemplo, 1000 filas) a la vez.
Tengo una CPU de 6 núcleos con 32 Gb de RAM, así que dentro de cada iteración probé a usar el módulo de multiprocessing
de Python con un objeto de Pool
para procesar varios bloques simultáneamente (pasos 4 y 5) y luego escribo la salida (el predicciones) al conjunto común de cuadrículas de salida utilizando una función de devolución de llamada que llama a una función de escritura de salida global. Esto parece funcionar, pero no es más rápido (en realidad, probablemente sea más lento) que procesar cada bloque en serie.
Así que mi pregunta es, ¿hay una manera más eficiente de hacerlo? Estoy interesado en la clase Queue
del módulo multiprocesamiento, pero no estoy realmente seguro de cómo funciona. Por ejemplo, me pregunto si es más eficiente tener una cola que lleva a cabo los pasos 4 y 5 y luego pasa los resultados a otra cola que lleva a cabo el paso 6. ¿O es esto incluso para qué sirve la cola?
Cualquier indicador sería apreciada.
Algunos de los ejemplos de multiprocesamiento en python.org no son muy claros en la OMI, y es fácil comenzar con un diseño defectuoso. Aquí hay un ejemplo simplista que hice para que comience un proyecto:
import os, time, random, multiprocessing
def busyfunc(runseconds):
starttime = int(time.clock())
while 1:
for randcount in range(0,100):
testnum = random.randint(1, 10000000)
newnum = testnum / 3.256
newtime = int(time.clock())
if newtime - starttime > runseconds:
return
def main(arg):
print ''arg from init:'', arg
print "I am " + multiprocessing.current_process().name
busyfunc(15)
if __name__ == ''__main__'':
p = multiprocessing.Process(name = "One", target=main, args=(''passed_arg1'',))
p.start()
p = multiprocessing.Process(name = "Two", target=main, args=(''passed_arg2'',))
p.start()
p = multiprocessing.Process(name = "Three", target=main, args=(''passed_arg3'',))
p.start()
time.sleep(5)
Esto debería ejercitar 3 procesadores durante 15 segundos. Debería ser fácil modificarlo por más. Quizás esto ayude a depurar su código actual y asegurar que realmente esté generando múltiples procesos independientes.
Si debe compartir datos debido a las limitaciones de RAM, sugiero esto: http://docs.python.org/library/multiprocessing.html#sharing-state-between-processes
Como Python no tiene la intención de hacer un intenso cuncheo de números, normalmente comienzo a convertir partes críticas de un programa de python a C / C ++ y acelero mucho el proceso.
Además, el multiproceso de python no es muy bueno. Python sigue utilizando un semáforo global para todo tipo de cosas. Así que incluso cuando uses los subprocesos que ofrece Python, las cosas no se harán más rápidas. Los subprocesos son útiles para aplicaciones, donde los subprocesos normalmente esperarán cosas como IO.
Al crear un módulo C, puede liberar manualmente el semáforo global cuando procesa sus datos (entonces, por supuesto, ya no tendrá acceso a los valores de python).
Se necesita algo de práctica para usar la API de C, pero es claramente estructurado y mucho más fácil de usar que, por ejemplo, la API nativa de Java.
Consulte ''extender e incrustar'' en la documentación de python.
De esta manera, puede hacer que las partes críticas de tiempo en C / C ++ y las partes más lentas con programación más rápida trabajen en python ...
El estado actual de las capacidades de multiprocesamiento de Python no es excelente para el procesamiento vinculado a la CPU. Temo decirle que no hay forma de hacerlo correr más rápido usando el módulo de multiprocessing
, ni es su uso del multiprocessing
el problema.
El problema real es que Python aún está sujeto a las reglas de GlobalInterpreterLock(GIL) (sugiero altamente las slides ). Ha habido algunos avances teóricos y experimentales emocionantes en el trabajo en torno a la GIL. El evento Python 3.2 contiene un nuevo GIL que resuelve algunos de los problemas, pero introduce otros.
Por ahora, es más rápido ejecutar muchos procesos de Python con un solo subproceso en serie que intentar ejecutar muchos subprocesos dentro de un proceso. Esto le permitirá evitar los problemas de adquisición de GIL entre subprocesos (al tener efectivamente más GIL). Sin embargo, esto solo es beneficioso si la sobrecarga de IPC entre sus procesos de Python no eclipsa los beneficios del procesamiento.
Eli Bendersky escribió un artículo de información general decente sobre sus experiencias con el intento de hacer que un proceso vinculado a la CPU se ejecute más rápido con multiprocesamiento.
Vale la pena señalar que PEP 371 tuvo el deseo de "hacer un lado" de la GIL con la introducción del módulo de multiprocessing
(anteriormente un paquete no estándar llamado pyProcessing
). Sin embargo, GIL todavía parece tener un rol demasiado importante en el intérprete de Python para que funcione bien con algoritmos de CPU. Muchas personas diferentes han trabajado para eliminar / reescribir el GIL, pero nada ha hecho suficiente tracción para convertirlo en una versión de Python.
Le recomiendo que primero verifique qué aspectos de su código se están demorando más tiempo, por lo que tendrá que hacer un perfil, he usado http://packages.python.org/line_profiler/#line-profiler con mucho éxito, aunque requiere cython.
En cuanto a las colas, se usan principalmente para compartir datos / sincronizar hilos, aunque rara vez lo he usado. Yo uso multiprocesamiento todo el tiempo.
Principalmente, sigo la filosofía de reducción de mapas, que es simple y limpia, pero tiene una gran sobrecarga, ya que los valores se deben incluir en diccionarios y copiar en cada proceso, al aplicar la función de mapa ...
Puede intentar segmentar su archivo y aplicar su algoritmo a diferentes conjuntos.