2010-04-13 15 views
72

Me gustaría paralelizar mi programa Python para que pueda utilizar múltiples procesadores en la máquina en la que se ejecuta. Mi paralelización es muy simple, ya que todos los "hilos" paralelos del programa son independientes y escriben su salida en archivos separados. No necesito los hilos para intercambiar información, pero es imprescindible que sepa cuándo terminan los hilos, ya que algunos pasos de mi tubería dependen de su salida.decidiendo entre subproceso, multiproceso e hilo en Python?

La portabilidad es importante, ya que me gustaría que esto se ejecute en cualquier versión de Python en Mac, Linux y Windows. Dadas estas limitaciones, ¿cuál es el módulo de Python más apropiado para implementar esto? Estoy intentando decidir entre subprocesos, subprocesos y multiprocesamiento, que todos parecen proporcionar una funcionalidad relacionada.

¿Alguna idea de esto? Me gustaría la solución más simple que sea portátil.

+0

Relacionados: http://stackoverflow.com/questions/1743293/why-does-my-python-program-average-only-33-cpu-per-process-how-can-i-make-pytho/1743312 # 1743312 (lee mi respuesta allí para ver por qué los hilos no son un iniciador para el código Python puro) –

+1

"Cualquier versión de Python" es MUY demasiado vaga. Python 2.3? 1.x? 3.x? Es simplemente una condición imposible de satisfacer. – detly

Respuesta

46

multiprocessing es un gran tipo de módulo de cuchillo de ejército suizo. Es más general que los hilos, ya que incluso puede realizar cálculos remotos. Este es, por lo tanto, el módulo que sugiero que use.

El módulo subprocess también le permitirá iniciar varios procesos, pero me pareció menos conveniente de usar que el nuevo módulo de multiprocesamiento.

Los subprocesos son notoriamente sutiles y, con CPython, a menudo se limitan a un núcleo, con ellos (aunque, como se señala en uno de los comentarios, el bloqueo de intérprete global (GIL) se puede liberar en código C llamado del código de Python).

Creo que la mayoría de las funciones de los tres módulos que cita se pueden utilizar de una manera independiente de la plataforma. En el lado de la portabilidad, tenga en cuenta que multiprocessing solo viene en estándar desde Python 2.6 (aunque existe una versión para algunas versiones anteriores de Python). ¡Pero es un gran módulo!

+1

para una asignación, acabo de utilizar el módulo "multiproceso" y su método pool.map(). pedazo de pastel! – kmonsoor

+2

¿Por qué el voto a favor? – EOL

3

Para usar múltiples procesadores en CPython su única opción es es el módulo multiprocessing. CPython mantiene un bloqueo en su interior (el GIL) que evita que los hilos en otras CPU funcionen en paralelo. El módulo multiprocessing crea nuevos procesos (como subprocess) y gestiona la comunicación entre ellos.

+4

Eso no es del todo cierto, AFAIK puede liberar el GIL usando la API C, y hay otras implementaciones de Python como IronPython o Jython que no sufren de tales limitaciones. Sin embargo, no perdoné. –

3

En un caso similar opté por procesos separados y el poco de comunicación necesaria a través del conector de red. Es muy portátil y bastante simple de usar con Python, pero probablemente no tanto (en mi caso también tuve otra restricción: la comunicación con otros procesos escritos en C++).

En su caso, probablemente optaría por multiprocesos, ya que los hilos de python, al menos cuando se usa CPython, no son hilos reales. Bueno, son subprocesos del sistema nativo, pero los módulos C llamados desde Python pueden o no lanzar el GIL y permitir que otros subprocesos los ejecuten al llamar al código de bloqueo.

1

Shell hacia fuera y dejar que el UNIX a hacer sus trabajos:

uso iterpipes para envolver subproceso y, a continuación:

From Ted Ziuba's site

INPUTS_FROM_YOU | xargs -n1 -0 -P NUM./ procesos paralelos proceso #NUM

O

Gnu Parallel también servirá

andas con GIL mientras usted envía a los chicos a cabo entre bastidores para hacer su trabajo de múltiples núcleos.

+6

"La portabilidad es importante, ya que me gustaría que esto se ejecute en cualquier versión de Python en Mac, Linux y Windows". – detly

+0

Con esta solución, ¿puede interactuar repetidamente con el trabajo? Puede hacerlo en multiprocesamiento, pero no lo creo en subprocesos. – abalter

126

Para mí esto es en realidad bastante simple:

El opción subproceso:

subprocess es para el funcionamiento de otros ejecutables --- Es básicamente una envoltura alrededor de os.fork() y os.execve() con algún apoyo para opcional fontanería (configuración de los PIPE desde y hacia los subprocesos. (Obviamente, se pueden utilizar otros mecanismos de comunicaciones entre procesos del sistema (IPC), como sockets, memoria compartida SysV y colas de mensajes, pero normalmente se usa subproceso para ejecutar terceros partido ejecutables binarios una van a quedar atrapados con las interfaces y canales de IPC que admitan).

Comúnmente uno usa subprocess sincrónicamente --- simplemente llamando a alguna utilidad externa y leyendo su resultado o esperando su finalización (tal vez leyendo sus resultados de un archivo temporal, o después de que se haya publicado en alguna base de datos).

Sin embargo, uno puede engendrar cientos de subprocesos y sondearlos. Mi propia utilidad favorita personal hace exactamente eso. La mayor desventaja es que del módulo subprocess es que su soporte de E/S generalmente está bloqueando. Hay un borrador PEP-3145 para arreglarlo en alguna versión futura de Python 3.xy una alternativa asyncproc (Advertencia que conduce directamente a la descarga, no a ningún tipo de documentación ni a README). También encontré que es relativamente fácil importar fcntl y manipular sus descripciones de archivos PIPE Popen directamente, aunque no sé si esto es portátil para plataformas que no sean UNIX.

subprocesstiene soporte ... aunque puede utilizar el módulo de signal y las señales de UNIX/Linux llano de la vieja escuela --- matando a sus procesos en voz baja, como si fuera el manejo casi ningún caso.

El opción multiprocesamiento:

multiprocessing es para ejecutar funciones dentro de su (Python) código existente con soporte para comunicaciones más flexibles entre la familia de los procesos. En particular, es mejor construir su multiprocessing IPC alrededor de los objetos del módulo Queue donde sea posible, pero también puede usar los objetos Event y varias otras características (algunas de las cuales, presumiblemente, se basan en el soporte mmap en plataformas donde ese soporte es suficiente).

módulo de Python multiprocessing está destinado a proporcionar interfaces y funciones que son muy similares a threading al tiempo que permite CPython a la escala de su procesamiento entre múltiples CPU/núcleos a pesar de la GIL (Global intérprete de bloqueo). Aprovecha todo el esfuerzo de coherencia y bloqueo SMP de gran precisión que realizaron los desarrolladores de su kernel OS.

El roscado opción:

threading es para una bastante estrecha gama de aplicaciones que son I/O obligados (no necesite a escala a través de múltiples núcleos de la CPU) y que se benefician de la extremadamente baja latencia y sobrecarga de conmutación de la conmutación de subprocesos (con memoria central compartida) frente a la conmutación de proceso/contexto. En Linux, este es casi el conjunto vacío (los tiempos de conmutación del proceso Linux son extremadamente cercanos a sus conmutadores de subprocesos).

threading sufre de dos desventajas principales en Python.

Una, por supuesto, es específica de la implementación --- afecta principalmente a CPython. Ese es el GIL. En su mayor parte, la mayoría de los programas de CPython no se beneficiarán de la disponibilidad de más de dos CPU (núcleos) y, a menudo, el rendimiento sufrirá por la contención de bloqueo de GIL.

El problema más amplio que no es específico de la implementación es que los subprocesos comparten la misma memoria, manejadores de señal, descriptores de archivos y ciertos otros recursos del sistema operativo. Por lo tanto, el programador debe tener mucho cuidado con el bloqueo de objetos, el manejo de excepciones y otros aspectos de su código que son sutiles y que pueden matar, detener o bloquear el proceso completo (conjunto de hilos). En comparación, el modelo multiprocessing le da a cada proceso su propia memoria, descriptores de archivos, etc. Una excepción bloqueada o no controlada en cualquiera de ellos solo matará ese recurso y manejar robustamente la desaparición de un niño o hermano puede ser considerablemente más fácil que la depuración, el aislamiento y la reparación o el trabajo en torno a problemas similares en los hilos.

  • (Nota: el uso de threading con los sistemas de Python importantes, tales como NumPy, puede sufrir considerablemente menos de GIL contención a continuación, la mayor parte de su propio código Python haría Esto se debe a que han sido diseñados específicamente para hacerlo.).

La opción trenzado:

Es también digno de mención que Twisted ofrece otra alternativa, que es a la vez elegante y muy difícil de entender. Básicamente, a riesgo de simplificar en exceso hasta el punto en que los fanáticos de Twisted pueden asaltar mi casa con horquillas y antorchas, Twisted ofrece multitareas cooperativas impulsadas por eventos dentro de cualquier (único) proceso.

Para entender cómo esto es posible, uno debe leer acerca de las características de select() (que se pueden construir alrededor de la select() o poll() o similares llamadas al sistema OS). Básicamente, todo se debe a la capacidad de solicitar que el sistema operativo duerma en espera de cualquier actividad en una lista de descriptores de archivos o algún tiempo de espera.

El despertar de cada una de estas llamadas a select() es un evento --- ya sea que involucre una entrada disponible (legible) en algunos sockets o descriptores de archivos, o que el espacio de almacenamiento en búfer esté disponible en otros descriptores o sockets, algunas condiciones excepcionales (TCP paquetes fuera de banda PUSH'd, por ejemplo) o un TIEMPO DE ESPERA.

Por lo tanto, el modelo de programación Twisted se basa en el manejo de estos eventos y luego en el controlador "principal" resultante, lo que le permite enviar los eventos a sus controladores.

Yo personalmente pienso en el nombre, Twisted tan evocadora del modelo de programación ... ya que su aproximación al problema debe ser, en cierto sentido, "retorcida" de adentro hacia afuera. En lugar de concebir su programa como una serie de operaciones sobre datos de entrada y salidas o resultados, está escribiendo su programa como un servicio o daemon y definiendo cómo reacciona a varios eventos. (De hecho, el núcleo de "bucle principal" de un programa de Twisted es (por lo general? Siempre?) Un reactor().

Los principales retos para el uso de Twisted implican torcer su mente alrededor del modelo orientado a eventos y también evitando el uso de cualquier biblioteca de clases o kits de herramientas que no estén escritos para cooperar dentro del framework Twisted. Es por eso que Twisted suministra sus propios módulos para el manejo de protocolos SSH, para curses, y sus propias funciones de subproceso/popen, y muchos otros módulos y manejadores de protocolos que , a primera vista, parecería duplicar cosas en las bibliotecas estándar de Python.

Creo que es útil entender Twisted en un nivel conceptual, incluso si nunca tiene la intención de usarlo. Información sobre el rendimiento, la contención y el manejo de eventos en el procesamiento de threading, multiprocesamiento e incluso procesamiento de subprocesos, así como cualquier procesamiento distribuido que realice.

(Nota: Las nuevas versiones de Python 3.x están incluidos asyncio (E/S asíncrona) Características tales como asíncrono def, la @ async.coroutine decorador, y el esperan de palabras clave, y rendimiento del soporte futuro. Todos estos son aproximadamente similares a Twisted desde una perspectiva de proceso (multitarea cooperativa).

La opción distribuido:

Sin embargo, otro ámbito de procesamiento que no han preguntado acerca, pero que vale la pena considerar, es el de procesamiento distribuido. Existen muchas herramientas y frameworks de Python para el procesamiento distribuido y el cómputo paralelo. Personalmente, creo que el más fácil de usar es el que menos se considera en ese espacio.

Es casi trivial construir el procesamiento distribuido alrededor de Redis. El almacén de claves completo se puede usar para almacenar unidades de trabajo y resultados, los LIST de Redis se pueden usar como objeto similar al Queue(), y el soporte PUB/SUB se puede usar para el manejo de Event. Puede actualizar sus claves y usar valores, replicados en un clúster de instancias de Redis, para almacenar la topología y las asignaciones de tokens y hash para proporcionar un hash y un fail-over consistentes para escalar más allá de la capacidad de cualquier instancia para coordinar a sus trabajadores. y recopilación de datos (en escabeche, JSON, BSON o YAML) entre ellos.

Por supuesto, a medida que empiezan a construir una escala mayor y solución más sofisticada en torno Redis se vuelven a poner en práctica muchas de las características que ya han sido resueltos utilizando, Celery, Apache Spark y Hadoop, Zookeeper, etcd, Cassandra etc. . Esos también tienen módulos para el acceso de Python a sus servicios.

[Actualización: Un par de recursos para considerar si está considerando Python para sistemas intensivos computacionalmente distribuidos: IPython Parallel y PySpark. Si bien estos son sistemas de computación distribuida de propósito general, son particularmente accesibles y populares subsistemas de ciencia y análisis de datos.

Conclusión

Ahí lo tienes toda la gama de alternativas de procesamiento para Python, a partir de un solo subproceso, con simples llamadas síncronas a subprocesos, piscinas de subprocesos encuestados, roscados y multiprocesamiento, basados ​​en eventos cooperativa de múltiples la tarea y el procesamiento distribuido.

+1

Aunque es difícil de usar multiprocesamiento con clases/OOP. – Tjorriemorrie

Cuestiones relacionadas