CLUSTERS :: Definiciones
Clusters | Herramientas de Instalación Automáticas de Clusters | Herramientas de Desarrollo | Adminsitración y Planificación de Tareas


Herramientas de Desarrollo de Aplicaciones


El aparecimiento de la computación paralela permitió que emerjan métodos de programación que hicieron posible la implementación de algoritmos utilizando recursos compartidos: CPUs (Central Processing Unit), memoria, datos y servicios; dichos métodos a su vez permiten la portabilidad del código fuente a diferentes arquitecturas.

La solución a ciertos problemas computacionales y de cálculos intensivos, es provista por la implementación de tareas paralelas.

El paralelismo se puede implementar mediante una aproximación del modelo cliente–servidor, llamado maestro–esclavo (master–worker). Ésta es fácil de implementar, implica dividir el problema computacional en tareas independientes; es decir, el maestro coordina la solución del problema computacional, asignando tareas independientes a otros procesos, los cuales se denominan procesos esclavos.

Los algoritmos y mecanismos de ejecución en este esquema involucran tareas de paralelismo que contemplan varios pasos:

1. Dividir el problema computacional en tareas independientes.
2. Inicializar los procesos esclavos.
3. Especificar las tareas de comunicación que se realizarán entre el maestro y los esclavos.
4. Emitir los resultados parciales desde los esclavos, y recopilarlos en el maestro (ensamblaje de las soluciones parciales y presentación de la solución final).
5. Asegurar que se recolectaron todos los resultados, y que los procesos esclavos fueron liberados.

Si el trabajo total pudiera ser fácilmente dividido en tareas de tamaño arbitrario, el trabajo de planificación se simplificaría. Si existen n procesos esclavos, el trabajo debería ser repartido en n partes, cada una de las cuales debería ser concluida en la misma cantidad de tiempo. A esto se le conoce como planificación estática (static scheduling).

El maestro realiza la asignación inicial de tareas a los esclavos, y luego espera por la finalización del procesamiento de las tareas en los esclavos.

Desde el punto de vista de lenguajes de programación, existen dos alternativas importantes para escribir programas paralelos. Éstas son:

1. El uso de directivas basadas en un lenguaje de programación paralelo.
2. El paso de mensajes explícito mediante llamadas a librerías de lenguajes de programación estándar como: C, C++ y Fortran.

Las herramientas más prominentes de la primera alternativa son HPF (Fortran de Alto Desempeño – High Performance Fortran) y OpenMP; en las cuales el código serial se transforma a paralelo, mediante la adición de directivas. Las directivas aparecen como comentarios en el código serial y describen el comportamiento de la aplicación.

E n la segunda alternativa, es tarea del programador especificar de manera explícita la división de los datos y las tareas que debe realizar cada proceso. El modelo de paso de mensajes presenta las siguientes características:

• La computación paralela consiste de un número de procesos (locales y/o remotos), cada uno trabajando sobre datos locales. Cada proceso tiene variables locales, y no existe un mecanismo para que algún proceso acceda directamente a la memorai de otro.

• Se logra compartir los datos entre los procesos mediante el envío y recepción de mensajes.

• Este modelo involucra procesos que no necesariamente se ejecutan en diferentes procesadores.

• Este modelo puede ser implementado en distintas plataformas, como multiprocesadores de memoria compartida, redes de estaciones de trabajo y computadores personales de un sólo procesador.

PVM y MPI son especificaciones de librerías de paso de mensajes. Estas administran la transferencia de datos entre instancias de un programa paralelo las cuales se ejecutan usualmente en múltiples procesadores.


 

PVM (PARALLEL VIRTUAL MACHINE)

PVM fue desarrollado a inicios de 1990, por el Laboratorio Nacional Oak Ridge de la Universidad de Tennessee, en conjunto con la Universidad de Emory de Estados Unidos.

La distribución de PVM se obtiene en formatos de fuentes binarias o archivos empaquetados (RPMs) para ambientes UNIX o Linux. Se encuentra disponibles para sistemas Windows desde la versión 3.4 como un paquete MSI (Microsoft Installer).

PVM es un conjunto integrado de herramientas de software y librerías que conforman un framework de propósito general.

El sistema PVM está compuesto de dos partes: un proceso demonio y de librerías basadas en rutinas. El proceso demonio se denomina pvmd3. Cada vez que un usuario ejecuta una aplicación PVM, primero se crea una máquina virtual.

Cada usuario puede ejecutar varios programas PVM simultáneamente sobre su propia máquina virtual; además, se pueden configurar varias máquinas virtuales (una por cada nodo del cluster) para formar una sola máquina virtual de mejores características.

La interfaz de la librería PVM contiene las primitivas necesarias para la cooperación entre las tareas de una aplicación. Aquí se definen todas las rutinas para paso de mensajes, sincronización de tareas, creación de procesos, y configuración de la máquina virtual. El sistema PVM actualmente soporta los lenguajes C, C++, y Fortran.

En los lenguajes C y C++, las implementaciones de PVM utilizan una interfaz con funciones basadas en las convenciones del lenguaje C, que permiten acceder a sus diferentes librerías. Los argumentos de estas funciones son una combinación de parámetros y punteros. En el lenguaje Fortran, la funcionalidad de PVM se implementa como subrutinas en lugar de funciones.

[SUBIR]


 

OpenMP

OpenMP ofrece un API para escribir programas paralelos para sistemas multiprocesadores, cada uno de ellos con acceso a los mismos recursos de memoria, denominados sistemas de memoria compartida.

OpenMP es un conjunto de librerías para C y C++, regidas por las especificaciones ISO/IEC (International Standard Organization / International Engineering Consortium), basado en el uso de directivas para ambientes paralelos.

OpenMP tiene soporte para diferentes sistemas operativos como UNIX, Linux y Windows.

Un programa escrito con OpenMP inicia su ejecución, en un solo hilo activo, llamado maestro. El hilo maestro ejecuta una región de código serial antes de que la primera construcción paralela se ejecute. Bajo el API de OpenMP la construcción paralela se obtiene mediante directivas paralelas. Cuando se encuentra una región de código paralelo, el hilo maestro crea (fork) hilos adicionales, convirtiéndose en el líder del grupo. El hilo maestro y los nuevos hilos e jecutan de forma concurrente la sección paralela (realizan trabajo compartido). Unirse al grupo (join) es el procedimiento donde al finalizar la ejecución de la región de código paralelo los hilos adicionales se suspenden o se liberan, y el hilo maesto retoma el control de la ejecución. Este método se conoce como fork & join.

La diferencia entre el modelo de paso de mensajes y el modelo de memoria compartida, se debe al número de procesos o hilos activos. En un modelo de paso de mensajes, durante la ejecución del programa, todos los procesos se encuentran activos. Por otra parte, en un modelo de memoria compartida, sólo existe un hilo activo al iniciar y al finalizar el programa; sin embargo, durante la ejecución del programa, la cantidad de hilos activos puede variar de forma dinámica.


Modelo de ejecución fork & join
Modelo de ejecución fork & Join

[SUBIR]


 

MPI (MESSAGE PASSING INTERFACE)

La estandarización de MPI empezó en Abril de 1992. La primera versión del estándar MPI aparece en Mayo de 1994. La Versión 1.1 agregó algunas aclaraciones y refinamientos y fue publicada a mediados de 1995. Las Versiones 1.0 y 1.1 fueron diseñadas para los lenguajes C y Fortran 77.

En Marzo de 1995, se extendió la versión original, y culminó con la creación del estándar MPI Versión 2. La Versión 2 incluye la implementación para C++ y Fortran 90.

MPI no es un lenguaje de programación, es un conjunto de funciones y macros que conforman una librería estándar de C y C++, y subrutinas en Fortran.

MPI ofrece un API, junto con especificaciones de sintaxis y semántica que explican como sus funcionalidades deben añadirse en cada implementación que se realice (tal como almacenamiento de mensajes o requerimientos para entrega de mensajes). MPI incluye operaciones
punto a punto y colectivas, todas destinadas a un grupo específico de procesos.

MPI realiza la conversión de datos heterogéneos como parte transparente de sus servicios, por medio de la definición de tipos de datos específicos para todas las operaciones de comunicación. Se pueden tener tipos de datos definidos por el usuario o primitivos.


FUNDAMENTOS DE MPI
Con MPI el número de procesos requeridos se asigna antes de la ejecución del programa, y no se crean procesos adicionales mientras la aplicación se ejecuta.

A cada proceso se le asigna una variable que se denomina rank, la cual identifica a cada proceso, en el rango de 0 a p-1, donde p es el número total de procesos.El control de la ejecución del programa se realiza mediante la variable rank; la variable rank permite determinar que proceso ejecuta determinada porción de código.

En MPI se define un comunicator como una colección de procesos, los cuales pueden enviar mensajes el uno al otro; el comunicator básico se denomina MPI_COMM_WORLD y se define mediante un macro del lenguaje C. MPI_COMM_WORLD agrupa a todos los procesos activos durante la ejecución de una aplicación.

Las llamadas de MPI se dividen en cuatro clases:

  1. Llamadas utilizadas para inicializar, administrar y finalizar comunicaciones.
  2. Llamadas utilizadas para transferir datos entre un par de procesos.
  3. Llamadas para transferir datos entre varios procesos.
  4. Llamadas utilizadas para crear tipos de datos definidos por el usuario.

La primera clase de llamadas permiten inicializar la librería de paso de mensajes, identificar el número de procesos (size) y el rango de los procesos (rank). La segunda clase de llamadas incluye operaciones de comunicación punto a punto, para diferentes tipos de actividades de envío y recepción. La tercera clase de llamadas son conocidas como operaciones grupales, que proveen operaciones de comunicaciones entre grupos de procesos. La última clase de llamadas provee flexibilidad en la construcción de estructuras de datos complejos.En MPI, un mensaje está conformado por el cuerpo del mensaje, el cual contiene los datos a ser enviados, y su envoltura, que indica el proceso fuente y el destino.

Envoltura del mensaje MPI

El cuerpo del mensaje en MPI se conforma por tres piezas de información: buffer, tipo de dato y count. El buffer, es la localidad de memoria donde se encuentran los datos de salida o donde se almacenan los datos de entrada. El tipo de dato, indica el tipo de los datos que se envían en el mensaje. En casos simples, éste es un tipo básico o primitivo, por ejemplo, un número entero, y que en aplicaciones más avanzadas puede ser un tipo de dato construido a través de datos primitivos. Los tipos de datos derivados son análogos a las estructuras de C. El count es un número de secuencia que junto al tipo de datos permiten al usuario agrupar ítems de datos de un mismo tipo en un solo mensaje. MPI estandariza los tipos de datos primitivos, evitando que el programador se preocupe de las diferencias que existen entre ellos, cuando se encuentran en distintas plataformas.

La envoltura de un mensaje en MPI típicamente contiene la dirección destino, la dirección de la fuente, y cualquier otra información que se necesite para transmitir y entregar el mensaje. La envoltura de un mensaje en MPI, consta de cuatro partes: la fuente, el destino, el comunicator y una etiqueta. La fuente identifica al proceso transmisor. El destino identifica al proceso receptor. El comunicator especifica el grupo de procesos a los cuales pertenecen la fuente y el destino. La etiqueta (tag) permite clasificar el mensaje.El campo etiqueta es un entero definido por el usuario que puede ser utilizado para distinguir los mensajes que recibe un proceso. Por ejemplo, se tienen dos procesos A y B. El proceso A envía dos mensajes al proceso B, ambos mensajes contienen un dato. Uno de los datos es utilizado para realizar un cálculo, mientras el otro es utilizado para imprimirlo en pantalla. El proceso A utiliza diferentes etiquetas para los mensajes. El proceso B utiliza los valores de etiquetas definidos en el proceso A e identifica que operación deberá realizar con el dato de cada mensaje.

Llamadas utilizadas para inicializar, administrar y finalizar comunicaciones
MPI dispone de 4 funciones primordiales que se utilizan en todo programa con MPI. Estas funciones son MPI_Init, MPI_Comm_size, MPI_Comm_rank y MPI_Finalize.MPI_Init permite inicializar una sesión MPI. Esta función debe ser utilizada antes de llamar a cualquier otra función de MPI. MPI_Finalize permite terminar una sesión MPI. Esta función debe ser la última llamada a MPI que un programa realice. Permite liberar la memoria usada por MPI. MPI_Comm_size permite determinar el número total de procesos que pertenecen a un comunicator.

MPI_Comm_rank permite determinar el identificador (rank) del proceso actual.

Llamadas utilizadas para transferir datos entre dos procesos
La transferencia de datos entre dos procesos se consigue mediante las llamadas MPI_Send y MPI_Recv. Estas llamadas devuelven un código que indica su éxito o fracaso.MPI_Send permite enviar información desde un proceso a otro. MPI_Recv permite recibir información desde otro proceso. Ambas funciones son bloqueantes, es decir que el proceso que realiza la llamada se bloquea hasta que la operación de comunicación se complete.Las versiones no bloqueantes de MPI_Send y MPI_Recv son MPI_Isend y MPI_Irecv, respectivamente. Estas llamadas inician la operación de transferencia pero su finalización debe ser realizada de forma explícita mediante llamadas como MPI_Test y MPI_Wait. MPI_Wait es una llamada bloqueante y retorna cuando la operación de envío o recepción se completa. MPI_Test permite verificar si la operación de envío o recepción ha finalizado, esta función primero chequea el estado de la operación de envío o recepción y luego retorna.


Llamadas utilizadas para transferir datos entre varios procesos
MPI posee llamadas para comunicaciones grupales que incluyen operaciones tipo difusión (broadcast), recolección (gather), distribución (scatter) y reducción. Algunas de las funciones que permiten realizar transferencia entre varios procesos se presentan a continuación.MPI_Barrier permite realizar operaciones de sincronización. En estas operaciones no existe ninguna clase de intercambio de información. Suele emplearse para dar por finalizada una etapa del programa, asegurándose de que todos los procesos han terminado antes de dar comienzo a la siguiente.MPI_Bcast permite a un proceso enviar una copia de sus datos a otros procesos dentro de un grupo definido por un comunicator.

MPI_Bcast

MPI_Scatter establece una operación de distribución, en la cual un dato (arreglo de algún tipo de datos) se distribuye en diferentes procesos.

MPI_Scatter

MPI_Gather establece una operación de recolección, en la cual los datos son recolectados en un sólo proceso.

MPI_Gather

MPI_Reduce permite que el proceso raíz recolecte datos desde otros procesos en un grupo, y los combine en un solo ítem de datos. Por ejemplo, se podría utilizar una operación reducción, para calcular la suma de los elementos de un arreglo que se distribuyó en algunos procesos.

MPI_Reduce

Llamadas utilizadas para crear tipos de datos definidos por el usuario
Para definir nuevos tipos de datos se puede utilizar la llamada MPI_Type_struct para crear un nuevo tipo o se puede utilizar la llamada MPI_Pack para empaquetar los datos.

[SUBIR]