2012-06-01 17 views
18

He escrito un algoritmo que toma datos geoespaciales y realiza una serie de pasos. Los datos de entrada son un shapefile de polígonos y rásteres de covarianza para un área de estudio de ráster grande (~ 150 millones de píxeles). Los pasos son como sigue:Diseño multiproceso de Python

  1. puntos de muestra desde el interior de polígonos de la shapefile
  2. para cada punto de muestreo, los valores de extracto de los rasters de covarianza
  3. construir un modelo predictivo en los puntos de muestreo
  4. Extraer covariables para la matriz de objetivos de puntos
  5. Aplicar modelo predictivo para apuntar rejilla
  6. predicciones escribir en un conjunto de rejillas de salida

El proceso completo debe repetirse varias veces (digamos 100) pero cada iteración actualmente demora más de una hora cuando se procesa en serie. Para cada iteración, las partes que consumen más tiempo son los pasos 4 y 5. Debido a que la cuadrícula de destino es tan grande, he estado procesando un bloque (digamos 1000 filas) a la vez.

tengo una CPU de 6 núcleos con 32 Gb RAM, por lo que dentro de cada iteración, que tenía un ir en utilizando el módulo de Python multiprocessing con un objeto Pool para procesar un número de bloques de forma simultánea (etapas 4 y 5) y luego escribir la salida (las 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, es probablemente más lento) que procesar cada bloque en serie.

Así que mi pregunta es, ¿hay una forma más eficiente de hacerlo? Estoy interesado en la clase Queue del módulo de multiprocesamiento, pero no estoy muy 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, luego pasa los resultados a otra cola que lleva a cabo el paso 6. ¿O esto es para qué sirve Queue?

Cualquier puntero sería apreciado.

+0

¿Cuál es el RSS máximo durante todo el proceso? Tal vez sería más fácil simplemente ejecutar esto seis veces al mismo tiempo si todo encajaría en la memoria ... – sarnold

+0

¿De verdad estás atado a la CPU? Esto parece un problema de E/S encuadernado. – stark

+0

@Sarnold: No todo encajará en la memoria, ese es el problema ... – hendra

Respuesta

3

El estado actual de las capacidades multi-procesamiento de Python no son grandes para CPU enlazado tratamiento. Me temo decirle que no hay forma de hacerlo funcionar más rápido utilizando el módulo multiprocessing ni es su uso de multiprocessing ese es el problema.

El problema real es que Python todavía está obligado por las reglas del GlobalInterpreterLock(GIL) (sugiero altamente el slides). Ha habido algunos interesantes teóricos y experimental advances sobre cómo trabajar con GIL. El evento Python 3.2 contiene un nuevo GIL que resuelve algunos de los problemas, pero presenta otros.

Por ahora, es más rápido ejecutar muchos procesos de Python con un único subproceso en serie que intentar ejecutar varios subprocesos dentro de un proceso. Esto le permitirá evitar problemas de adquisición de GIL entre hilos (al tener efectivamente más GIL). Sin embargo, esto solo es beneficioso si la sobrecarga de IPC entre los procesos de Python no eclipsa los beneficios del procesamiento.

Eli Bendersky escribió un decente overview article sobre sus experiencias con el intento de hacer que un proceso encuadernado con CPU corra más rápido con multiprocesamiento.

Vale la pena señalar que PEP 371 tenían el deseo de 'lado a paso' del GIL con la introducción del módulo multiprocessing (previamente un no-estándar de empaquetado llamado pyProcessing). Sin embargo, el GIL parece jugar un papel demasiado importante en el intérprete de Python para que funcione bien con los algoritmos de CPU. Muchas personas diferentes han trabajado para eliminar/reescribir el GIL, pero nada ha hecho suficiente esfuerzo para convertirlo en una versión de Python.

+0

Sospeché que este sería el caso. Definitivamente revisaré tus enlaces, gracias. En una táctica ligeramente diferente: supongo que el módulo Parallel Python estaría sujeto a los mismos tipos de problemas. Pero, ¿qué ocurre si, por ejemplo, PP se usó para dividir la tarea entre varias computadoras? – hendra

+0

@npo cualquier actualización? –

+0

Lo sentimos, todavía no! – hendra

0

Te recomiendo que compruebes primero qué aspectos de tu código toman más tiempo, así que tendrás que crear un perfil, he usado http://packages.python.org/line_profiler/#line-profiler con mucho éxito, aunque sí requiere cython.

En cuanto a las colas, se utilizan principalmente para compartir datos/sincronización de hilos, aunque rara vez lo he usado. Yo uso multiprocesamiento todo el tiempo.

sobre todo seguir el mapa de reducir la filosofía, que es sencillo y limpio, pero tiene algo de sobrecarga importante, ya que los valores tienen que ser embalado en los diccionarios y copiado a través de cada proceso, cuando se aplica la función de mapa ...

Puede intentar segmentar su archivo y aplicar su algoritmo a diferentes conjuntos.

1

Algunos de los ejemplos de multiprocesamiento en python.org no son muy claros IMO, y es fácil comenzar con un diseño defectuoso. He aquí un ejemplo simple que hice a me refiero a 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 ejercer 3 procesadores durante 15 segundos. Debería ser fácil modificarlo para más. Quizás esto ayude a depurar tu código actual y asegurarte de que realmente estás generando múltiples procesos independientes.

Si tiene que compartir los datos debido a la RAM limitaciones, entonces sugiero esto: http://docs.python.org/library/multiprocessing.html#sharing-state-between-processes

1

como Python no sirve realmente para hacer intensiva número cunching, normalmente iniciar la conversión de partes críticas en el tiempo de un programa Python a C/C++ y acelerar mucho las cosas.

Además, el multithreading de python no es muy bueno. Python sigue utilizando un semáforo global para todo tipo de cosas. Entonces, incluso cuando usas los Threads que ofrece python, las cosas no serán más rápidas. Los subprocesos son útiles para las aplicaciones, donde los subprocesos suelen esperar elementos como IO.

Al crear un módulo C, puede liberar manualmente el semáforo global al procesar sus datos (entonces, por supuesto, no acceda a los valores de python).

Lleva cierta práctica utilizar la API C, pero está claramente estructurada y es 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.

esta manera usted puede hacer que el tiempo las partes críticas en C/C++, y las partes más lentas con el trabajo de programación más rápido en Python ...

Cuestiones relacionadas