Desarrollo de un mecanismo para paralelizar código Python usando contenedores Docker

La etapa actual en el desarrollo de la tecnología, incluida la tecnología informática, nos muestra el crecimiento de los volúmenes de datos y la necesidad de computadoras cada vez más potentes. El desarrollo de procesadores centrales siempre se ha basado en la tecnología de aumentar el número de transistores en un chip de microprocesador. La conocida ley de Moore dice: "Si esta tendencia continúa, el poder de los dispositivos informáticos puede crecer exponencialmente en un período de tiempo relativamente corto (24 meses)



". ”, En el que admitió que el crecimiento exponencial de las cantidades físicas durante mucho tiempo es imposible. Solo la evolución de los transistores y sus tecnologías de fabricación hicieron posible extender la ley por varias generaciones más.



En 2007, Moore declaró que la ley aparentemente pronto dejaría de operar debido a la naturaleza atómica de la materia y la limitación de la velocidad de la luz. Actualmente, el tamaño máximo de un transistor en un procesador es de 5 nanómetros. También hay muestras de prueba del procesador de 3 nm, pero su lanzamiento no comenzará hasta 2021. Esto sugiere que pronto se detendrá un aumento adicional en el número de transistores en un chip (hasta que se descubra un nuevo material o se actualice radicalmente el proceso tecnológico).



Una de las soluciones a este problema es la computación paralela. Este término se entiende como una forma de organizar los cálculos informáticos, en la que los programas se desarrollan como un conjunto de procesos computacionales interactivos que funcionan en paralelo (simultáneamente).



La computación paralela por el método de sincronización se divide en dos tipos.



En la primera variante, los procesos interactúan a través de la memoria compartida: se lanza un hilo de ejecución independiente en cada procesador del sistema multiprocesador. Todos los hilos pertenecen a un proceso. Los subprocesos intercambian datos a través de un área de memoria compartida para un proceso determinado. El número de subprocesos corresponde al número de procesadores. Los flujos se crean mediante un lenguaje de programación (por ejemplo, Java, C #, C ++ desde C ++ 11, C desde C11) o utilizando bibliotecas. En este caso, es posible crear subprocesos explícitamente (por ejemplo, en C / C ++ usando PThreads), declarativamente (por ejemplo, usando la biblioteca OpenMP) o automáticamente, usando herramientas de compilación integradas (por ejemplo, High Performance Fortran). La variante descrita de programación paralela generalmente requiere alguna forma de captura de control (mutex, semáforos,monitores) para coordinar los flujos entre ellos.



En la segunda variante, la interacción se realiza mediante transmisión de mensajes. Se inicia un proceso de un solo subproceso en cada procesador en un sistema multiprocesador y se comunica con otros procesos que se ejecutan en otros procesadores mediante mensajes. Los procesos se crean explícitamente llamando a la función apropiada del sistema operativo y los mensajes se intercambian usando una biblioteca especial (por ejemplo, la implementación del protocolo MPI) o usando herramientas de lenguaje (por ejemplo, High Performance Fortran, Erlang u occam).



Además de las dos descritas anteriormente, también se utiliza una opción híbrida: en sistemas multiprocesador con memoria distribuida (DM-MIMD), donde cada nodo del sistema es un multiprocesador con memoria compartida (SM-MIMD), se puede utilizar el siguiente enfoque. Se lanza un proceso de subprocesos múltiples en cada nodo del sistema, que distribuye subprocesos entre los procesadores de este nodo. El intercambio de datos entre subprocesos en un nodo se lleva a cabo a través de la memoria compartida y el intercambio de datos entre nodos, a través de la transferencia de mensajes. En este caso, el número de procesos está determinado por el número de nodos y el número de subprocesos está determinado por el número de procesadores en cada nodo. El método híbrido de programación paralela es más complicado (se requiere reescribir el programa paralelo de una manera especial), pero es más eficiente en el uso de los recursos de hardware de cada nodo del sistema multiprocesador.



En este artículo, propongo adaptar un enfoque híbrido para paralelizar los cálculos en Python. La característica clave del trabajo es el uso de tecnología docker-container. El marco en desarrollo tendrá una arquitectura cliente-servidor, que incluye los siguientes elementos.



Del lado del cliente:



  1. Serializador: de acuerdo con el nombre, serializa funciones y sus variables (es decir, permite guardarlas en un dispositivo externo o red y luego cargarlas en memoria en el mismo u otro nodo). También cabe destacar el decorador paralelo, que es una función contenedora que permite utilizar el serializador para funciones de varios tipos.
  2. Clases para la configuración de conexión de servidor / clúster
  3. Herramientas de lenguaje adicionales para marcar funciones a paralelizar.


Lado del servidor:



  1. Deserializador: en consecuencia, deserializa los datos recibidos (ver arriba).
  2. Executor es una clase que procesa datos deserializados (funciones y sus argumentos) y también instala las bibliotecas necesarias en el entorno virtual del intérprete de Python.


La arquitectura general del sistema en desarrollo se muestra en la figura.



imagen



Para la comunicación entre el cliente y el servidor se pueden utilizar sockets o el framework twisted, cuya interacción se realizará a través de la API desarrollada.



La implementación de este sistema asume el uso de tecnología Docker. Esto le permite brindar conveniencia y alta velocidad de configuración de software para comenzar: simplemente inicie el clúster docker-swarm, implemente la imagen de la ventana acoplable en el servidor seleccionado y establezca el número de replicaciones.



Otras ventajas importantes del uso de la tecnología Docker son la creación de un entorno informático homogéneo mediante la virtualización de un sistema similar a UNIX (Ubuntu - ligero Alpine Linux), así como la presencia de un modo enjambre, que le permite ejecutar múltiples contenedores en diferentes servidores y equilibrar rápidamente la carga transfiriendo trabajos a contenedores libres. ...



El marco desarrollado se puede utilizar en varias áreas donde se requiere realizar grandes cantidades de cálculos en el lenguaje Python, incluso para tareas de aprendizaje automático y análisis profundo de datos, así como para tareas más simples, por ejemplo, para la verificación de decisiones distribuidas durante las olimpiadas de programación.



All Articles