Saltar al contenido

decidir entre subproceso, multiprocesamiento e hilo en Python?

Solución:

Para mí, esto es bastante simple:

los subproceso opción:

subprocess es para ejecutar otros ejecutables — es básicamente un envoltorio os.fork() y os.execve() con algo de soporte para plomería opcional (configuración de PIPE hacia y desde los subprocesos. Obviamente, podría utilizar otros mecanismos de comunicaciones entre procesos (IPC), como sockets o memoria compartida Posix o SysV. Pero estará limitado a lo que sea Las interfaces y los canales IPC son compatibles con los programas a los que llama.

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

Sin embargo, uno puede generar cientos de subprocesos y sondearlos. Mi clase de utilidad favorita personal hace exactamente eso.
La mayor desventaja de El subprocess módulo es que el soporte de E / S generalmente se bloquea. Hay un borrador PEP-3145 para arreglar eso en alguna versión futura de Python 3.xy un asyncproc alternativo (Advertencia que conduce directamente a la descarga, no a ningún tipo de documentación ni README). También descubrí que es relativamente fácil importar fcntl y manipula tu Popen Descriptores de archivos PIPE directamente — aunque no sé si esto es portátil para plataformas que no son UNIX.

(Actualización: 7 de agosto de 2019: soporte de Python 3 para subprocesos ayncio: subprocesos asyncio)

subprocess casi no tiene soporte de manejo de eventosaunque puedes usar el signal módulo y señales simples de UNIX / Linux de la vieja escuela — matando sus procesos suavemente, por así decirlo.

los multiprocesamiento opción:

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

Python multiprocessing El módulo está destinado a proporcionar interfaces y características que son muy Similar a threading al tiempo que permite que CPython escale su procesamiento entre múltiples CPU / núcleos a pesar del GIL (Bloqueo de intérprete global). Aprovecha todo el esfuerzo de coherencia y bloqueo SMP detallado que realizaron los desarrolladores del kernel de su sistema operativo.

los enhebrar opción:

threading es para una gama bastante reducida de aplicaciones que están vinculadas a E / S (no es necesario escalar en varios núcleos de CPU) y que se benefician de la latencia extremadamente baja y la sobrecarga de conmutación de la conmutación de subprocesos (con memoria de núcleo compartida) frente a la conmutación de proceso / contexto. En Linux, este es casi el conjunto vacío (los tiempos de cambio de proceso de Linux están extremadamente cerca de sus cambios de hilo).

threading sufre de dos grandes desventajas en Python.

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

El problema más importante, que no es específico de la implementación, es que los subprocesos comparten la misma memoria, manejadores de señales, 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 todo el proceso (conjunto de subprocesos).

En comparación, el multiprocessing El modelo le da a cada proceso su propia memoria, descriptores de archivo, etc.Una falla o excepción no controlada en cualquiera de ellos solo matará ese recurso y manejar de manera robusta la desaparición de un proceso secundario o hermano puede ser considerablemente más fácil que depurar, aislar y arreglar o solucionar problemas similares en subprocesos.

  • (Nota: uso de threading con los principales sistemas Python, como NumPy, puede sufrir considerablemente menos contención de GIL que la mayoría de su propio código Python. Eso es porque han sido diseñados específicamente para hacerlo; las porciones nativas / binarias de NumPy, por ejemplo, liberarán el GIL cuando sea seguro).

los retorcido opción:

También vale la pena señalar que Twisted ofrece otra alternativa que es tanto elegante y muy desafiante de entender. Básicamente, a riesgo de simplificar demasiado hasta el punto en que los fanáticos de Twisted pueden asaltar mi hogar con horquillas y antorchas, Twisted ofrece multitarea cooperativa impulsada por eventos dentro de cualquier proceso (único).

Para entender cómo esto es posible, uno debe leer acerca de las características de select() (que se puede construir alrededor del Seleccione() o encuesta() o llamadas de sistema operativo similares). Básicamente, todo está impulsado por la capacidad de hacer una solicitud al sistema operativo para que se suspenda a la espera de cualquier actividad en una lista de descriptores de archivos o algún tiempo de espera.

Despertar de cada una de estas llamadas a select() es un evento — ya sea que involucre entrada disponible (legible) en cierto número de sockets o descriptores de archivo, o espacio de almacenamiento en búfer disponible en algunos otros descriptores o sockets (escribibles), algunas condiciones excepcionales (TCP fuera de banda PUSH ‘ d paquetes, por ejemplo), o un TIMEOUT.

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.

Personalmente pienso en el nombre, Retorcido como evocador del modelo de programación … ya que su enfoque del problema debe ser, en cierto sentido, “retorcido” 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 demonio y definiendo cómo reacciona a varios eventos. (De hecho, el “bucle principal” central de un programa Twisted es (¿normalmente? ¿Siempre?) reactor()).

los principales desafíos para usar Twisted implica girar su mente en torno al modelo impulsado por eventos y también evitar el uso de bibliotecas de clases o kits de herramientas que no estén escritos para cooperar dentro del marco Twisted. Esta es la razón por la que Twisted proporciona 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ían duplicar cosas en las bibliotecas estándar de Python.

Creo que es útil comprender Twisted a nivel conceptual, incluso si nunca tiene la intención de usarlo. Puede brindar información sobre el rendimiento, la contención y el manejo de eventos en su subproceso, multiprocesamiento e incluso manejo de subprocesos, así como cualquier procesamiento distribuido que realice.

(Nota: Las versiones más recientes de Python 3.x incluyen funciones asíncronas (E / S asíncronas) como async def, los @ async.coroutine decorador, y el esperar palabra clave y rendimiento del futuro apoyo. Todos estos son aproximadamente similares a Retorcido desde una perspectiva de proceso (cooperativa multitarea)). (Para conocer el estado actual del soporte Twisted para Python 3, consulte: https://twistedmatrix.com/documents/current/core/howto/python3.html)

los repartido opción:

Otro ámbito de procesamiento sobre el que no ha preguntado, pero que vale la pena considerar, es el de repartido Procesando. Hay muchas herramientas y marcos de Python para el procesamiento distribuido y el cálculo paralelo. Personalmente, creo que el más fácil de usar es el que menos se considera que está en ese espacio.

Es casi trivial construir un procesamiento distribuido alrededor de Redis. Todo el almacén de claves se puede utilizar para almacenar unidades de trabajo y resultados, las LIST de Redis se pueden utilizar como Queue() como objeto, y el soporte PUB / SUB se puede utilizar para Event-como manejo. Puede aplicar hash a sus claves y valores de uso, replicados en un clúster suelto de instancias de Redis, para almacenar la topología y las asignaciones de tokens de hash para proporcionar hash y conmutación por error consistentes para escalar más allá de la capacidad de una sola instancia para coordinar a sus trabajadores. y ordenación de datos (encurtidos, JSON, BSON o YAML) entre ellos.

Por supuesto, a medida que comienza a construir una solución a mayor escala y más sofisticada alrededor de Redis, está reimplementando muchas de las características que ya se han resuelto con Celery, Apache Spark y Hadoop, Zookeeper, etcd, Cassandra, etc. Todos ellos tienen módulos para que Python acceda a sus servicios.

[Update: A couple of resources for consideration if you’re considering Python for computationally intensive across distributed systems: IPython Parallel and PySpark. While these are general purpose distributed computing systems, they are particularly accessible and popular subsystems data science and analytics].

Conclusión

Allí tiene la gama de alternativas de procesamiento para Python, desde un solo subproceso, con simples llamadas síncronas a subprocesos, grupos de subprocesos sondeados, subprocesos y multiprocesamiento, multitarea cooperativa impulsada por eventos y procesamiento distribuido.

multiprocessing es un gran módulo de tipo navaja suiza. Es más general que los subprocesos, ya que incluso puede realizar cálculos remotos. Por tanto, este es el módulo que le sugiero que utilice.

los subprocess El módulo también le permitiría iniciar múltiples procesos, pero encontré que era menos conveniente de usar que el nuevo módulo de multiprocesamiento.

Los subprocesos son notoriamente sutiles y, con CPython, a menudo está limitado a un núcleo, con ellos (aunque, como se indica en uno de los comentarios, el bloqueo de intérprete global (GIL) se puede lanzar en código C llamado desde código Python) .

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

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

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

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *