Descarga la aplicación para disfrutar aún más
Vista previa del material en texto
UNIVERSIDAD NACIONAL AUTÓNOMA DE MÉXICO POSGRADO EN CIENCIA E INGENIERÍA DE LA COMPUTACIÓN “ESTUDIO DE SISTEMAS MULTICORE PARA CÓMPUTO DE ALTO RENDIMIENTO” T E S I S QUE PARA OBTENER EL GRADO DE: MAESTRA EN CIENCIAS (COMPUTACIÓN) P R E S E N T A: CLARA VICENTE HERNÁNDEZ DIRECTOR DE TESIS: HÉCTOR BENÍTEZ PÉREZ. México, D.F. 2010. UNAM – Dirección General de Bibliotecas Tesis Digitales Restricciones de uso DERECHOS RESERVADOS © PROHIBIDA SU REPRODUCCIÓN TOTAL O PARCIAL Todo el material contenido en esta tesis esta protegido por la Ley Federal del Derecho de Autor (LFDA) de los Estados Unidos Mexicanos (México). El uso de imágenes, fragmentos de videos, y demás material que sea objeto de protección de los derechos de autor, será exclusivamente para fines educativos e informativos y deberá citar la fuente donde la obtuvo mencionando el autor o autores. Cualquier uso distinto como el lucro, reproducción, edición o modificación, será perseguido y sancionado por el respectivo titular de los Derechos de Autor. AGRADECIMIENTOS: Quiero expresar mi más profundo agradecimiento a: Mi director de tesis: Dr. Héctor Benítez Pérez. Por su apoyo y confianza depositados a mi persona. Por su tiempo y dedicación para realizar este trabajo. Mis sinodales: Dr. Fabián García Nocetti. Dr. Jorge Luis Ortega Arjona. Dr. Gerardo Vega Hernández. Ing. Mario Rodríguez Manzanera. Por sus valiosas aportaciones y comentarios. Mis padres y amigos: por su gran apoyo moral en los momentos difíciles. Mi esposo: por su apoyo moral y económico. Mis hijos: Mary José y José Ángel. Para educarlos en el ejemplo. Y en memoria de mi querido Dr. Tchijov. ÍNDICE: CAPÍTULO 1 INTRODUCCIÓN 1.1 Introducción. 1 1.2 Caso de estudio. 4 1.3 Objetivo general. 4 1.4 Metas. 5 1.5 Contribución y Relevancia. 5 CAPÍTULO 2 GENERALIDADES 2.1 Introducción al capítulo. 7 2.2 Sistemas con memoria compartida. 7 2.3 Sistemas con memoria distribuida. 12 2.4 Características de las arquitecturas referidas. 16 2.5 Sistema Multicore. 17 2.6 Métricas de desempeño en multiprocesamiento. 17 2.7 Ley de Amdahl . 19 2.8 Resumen de capítulo. 21 CAPÍTULO 3 ARQUITECTURA DEL SISTEMA 3.1 Introducción al capítulo. 23 3.2 Descripción de la arquitectura de hardware. 23 3.3 Descripción de la arquitectura de software. 25 3.4 OpenMP. 29 3.5 Nivel de paralelismo. 30 3.6 Directivas y construcciones de sincronización. 32 3.7 MPI. 33 3.8 Llamadas utilizadas para iniciar, administrar y finalizar comunicaciones 34 3.9 Llamadas utilizadas para transferir datos entre dos procesos. 35 3.10 Llamadas utilizadas para transferir datos entre varios procesos. 36 3.11 Ventajas. 38 3.12 Desventajas. 38 3.13 Resumen de capítulo. 39 CAPÍTULO 4 CASO DE ESTUDIO 4.1 Introducción al capítulo. 40 4.2 Descripción de la red neuronal. 40 4.3 Caso de estudio 1 Implementación híbrida. 44 4.4 Caso de estudio 2 Implementación en sistemas multicore. 52 4.5 Caso de estudio 3 Implementación en serie. 55 4.6 Resumen de de capitulo. 57 CAPÍTULO 5 MÉTRICAS Y ANÁLISIS DE RESULTADOS 5.1 Introducción al capítulo. 58 5.2 Aplicación de métricas de desempeño en sistemas multicore 58 5.3 resumen de capítulo. 68 CAPÍTULO 6 CONCLUSIONES 70 APÉNDICE. Código Hibrido. Utilizando las interfaces de programación MPI y OpenMP. 73 Código serial y/o Paralelo. 84 Referencias Bibliográficas 91 ÍNDICE DE FIGURAS. 1.1 Ejecución de instrucciones de manera secuencial. 2 1.2 Ejecución de instrucciones de manera simultánea sobre diferentes CPU’s 3 2.1 Arquitectura de procesadores con memoria compartida. 8 2.2 Diseño de una región paralela en sistemas con memoria compartida. 10 2.3 Arquitectura de un Sistema Distribuido. 12 2.4 Comunicación a través de sockets. 15 2.5 Caso ideal de aceleración para n procesadores. 19 3.1 Arquitectura híbrida de multiprocesamiento. 24 3.2 Sección Crítica. 27 3.3 Estados de los procesos en una sección crítica. 28 4.1 Algoritmo general de la Art. 43 4.2 Matriz de pesos de la red neuronal (Recurso compartido). 46 4.3 Paralelización de la red ART2A. 47 4.4 Vectores de dimensionalidad X valores mayores y posiciones correspondiente 48 4.5 Un ciclo completo de entrenamiento de la red ART2A en paralelo. 50 4.6 Hilos en acción. 52 4.7 Región paralela con hilos. 53 4.8 (a) Flujo de ejecución de hilos de manera paralela. 55 4.8 (b) Flujo de ejecución de hilos anidados. 55 4.9 Diagrama de flujo de la red ART2A de manera secuencial. 56 5.1 Aceleración de entrenamiento de la ART2A en 4 procesadores. 64 5.2 Tiempos de ciclo de ejecución del entrenamiento de la ART2A. 67 ÍNDICE DE TABLAS 5.1 Resultado de entrenamiento de la red neuronal ART2Acon varios hilos Para un valor de RO=0.4 59 5.2 Resultado de entrenamiento de la red neuronal ART2Acon varios hilos Para un valor de RO=0.1 61 5.3 Resultados de entrenamiento de la red neuronal ART2A para 3 y 4 procesadores 63 5.4 Tiempo de ejecución para 10 procesos en 4 procesadores 65 1 CAPÍTULO 1. 1.1 INTRODUCCIÓN: Dada la necesidad de contar con máquinas de mayor potencia de proceso, hoy en día es común encontrarse con computadoras que incluyen más de un procesador en su arquitectura. El utilizar varios procesadores no es nuevo, ya que desde hace tiempo existen tarjetas madre que permiten la interacción de varios procesadores (normalmente dos, cuatro u ocho) en un solo equipo, de la misma manera como se pueden agrupar computadoras en clúster de modo que actúen como si fuera una única computadora. Lo que resulta realmente novedoso es que ahora los procesadores estén encapsulados en un único chip (procesadores denominados multicore). El motivo es principalmente económico, ya que resulta más barato producir un circuito integrado con dos microprocesadores (dos cores), que dos procesadores independientes en una tarjeta madre. Por otra parte, al construirse sistemas multicore se ahorra energía, se produce menor calentamiento y al reducirse las distancias se obtiene un mejor rendimiento (performance), que en un solo procesador de rendimiento semejante. Con un solo procesador, una aplicación exigente pone a trabajar al tope al procesador, si existen diversos cores, sólo trabajará al máximo el core que soporte esa aplicación, los otros cores irán mas desahogados. Sin embargo, con los avances tecnológicos en la integración de procesadores no solo existen cambios en su arquitectura o en su comunicación interna sino que también conllevan un cambio en nuestra manera de pensar, ya que desde el inicio de la era de la computadora (1945) a la fecha, los computadores secuenciales han sido diseñados basados en el modelo introducido por John von Newman [18, 20, 21], el cual consiste en una unidad central de proceso (CPU), una memoria, y unidades de entrada y salida, debido a ello, el software y su programación se han orientado tradicionalmente a la computación en serie o secuencial, de este modo es natural que los algoritmos y programas de computadoras fueran primeramente formulados de acuerdo a 2 tal concepto. Aún antes de que la primera computadora electrónica fuera construida el concepto matemático de algoritmo fue definido como una secuencia finita de operaciones [22]. En una máquina secuencial, el procesador ejecuta una secuencia de instrucciones y operan con una secuencia de datos (fig. 1.1) [35]. Para su procesamiento unproblema es dividido en un conjunto de instrucciones, y su velocidad es limitada por dos factores: la capacidad de obtener y ejecutar dichas instrucciones y la velocidad de transferencia entre memoria y CPU. Instrucciones . . . N/N 3/N 2/N 1/N fig. 1.1 Ejecución de instrucciones de manera secuencial. Con el desarrollo de diversas tecnologías de interconexión entre unidades (unidades funcionales redundantes, pipeline, paralelismo, hiperthreding, multicore, entre otras) y la existencia de aplicaciones (científicas, técnicas, medicas, etc.) que requieren procesar grandes cantidades de datos o realizar un alto número de operaciones, se hace necesario el empleo de múltiples procesadores que cooperen en su ejecución, de manera que cada aplicación se divide en un número arbitrario de subprogramas (subconjuntos de instrucciones, o tareas independientes), con un fin determinado y estos a su vez pueden ejecutarse simultáneamente sobre diferentes procesadores en el mismo instante de tiempo[19] (fig. 1.2 ). Los procesadores se comunican unos con otros para intercambiar y combinar los resultados parciales obtenidos durante su ejecución. A este modelo tecnológico de procesamiento alternativo se le denomina cómputo en paralelo [22,3] y pretende conseguir que N procesadores trabajen de forma conjunta para resolver un problema N veces más rápido que en un único procesador [3]. En lugar de procesar instrucción por instrucción como en una computadora tradicional, en un sistema de procesamiento paralelo varias instrucciones se ejecutan simultáneamente, con el CPU Problema 3 propósito de acelerar la velocidad de procesamiento y aumentar así la eficiencia de cómputo [19,3]. No obstante, se debe considerar que el uso de múltiples procesadores en un mismo sistema computacional introduce requerimientos adicionales, es decir, para que varios procesadores puedan trabajar juntos ejecutando el mismo programa computacional, estos deben ser capaces de compartir datos y comunicarse cada uno con el otro, por lo que debe de haber una sincronización para así evitar condiciones de competencia. Problema Instrucciones . . . . . . . . . . . . N/N 3/N 2/N 1/N fig. 1.2 Ejecución de instrucciones de manera simultánea sobre diferentes CPUs El presente trabajo está dirigido hacia el estudio y análisis de la programación paralela, a tal fin se hace uso de un clúster de computadoras, cada una de ellas con arquitectura multicore. Para la programación de estas dos arquitecturas se utiliza programación híbrida, haciendo uso de las interfaces de programación MPI y OpenMP. MPI distribuye la carga entre las computadoras que conforman el clúster (comunicación inter procesos) y OpenMP maneja los cores en una misma máquina. Cabe hacer notar que la programación de estas dos arquitecturas es completamente diferente y requiere de conceptos con dos visiones diferentes (se explicaran en capitulo 2). CPU 1 TAREA 1 TAREA 2 . . . TAREA n … CPU n CPU 2 4 1.2 CASO DE ESTUDIO. El caso de estudio es el entrenamiento de una red neuronal llamada ART2A. Es una red de entrenamiento no supervisado, cuya estructura consiste de dos redes neuronales llamada ART [15, 30]. Su objetivo es la detección, localización y clasificación de defectos y fallas internas. La información proporcionada a la red para su entrenamiento, es a través de una base de datos que contiene información suficiente de cada tipo de defecto. La forma de entrenamiento es comparar las entradas presentes con categorías aprendidas seleccionadas. Las categorías aprendidas se encuentran almacenadas en una estructura multidimensional matricial llamada W [15], que es compartida por diferentes procesos. Esta es actualizada o modificada de acuerdo a un parámetro de vigilancia (ρ), que expresa el grado mínimo de similitud entre el vector de entrada y cualquier categoría definida en W. Si el grado de similitud entre la entrada y cualquier categoría es mayor o igual que ρ, entonces el prototipo actual es elegido para representar el grupo contenido. Esto dice que la red logra resonancia. Si el grado de similitud es menor que ρ entonces otra categoría debe ser seleccionada y comparada con el vector I (es el vector normalizado bajo la norma Euclidiana de los datos a ser estudiados). Si cualquier prototipo o categoría es alcanzado por el grado de similitud entonces el vector I debe ser agregado a W como un nuevo prototipo. 1.3 OBJETIVO GENERAL. El objetivo de este trabajo es estudiar el desempeño de sistemas con arquitectura multicore, con base en requerimientos de acceso restringido en recursos comunes a los procesadores, usando sistemas paralelos a través de una red de cómputo. Se requiere distribuir el procedimiento de entrenamiento de una red neuronal de tipo ART2A [15] en una arquitectura de multiprocesamiento que consta de N máquinas con características Multicore. El sistema está diseñado para N procesos1 que comparten una Red Neuronal ART2A. Este recurso debe ser actualizado o modificado dependiendo de los resultados del entrenamiento de la propia red en cada uno de los procesos. Para medir 1 Proceso. Un proceso se define como un programa en ejecución [18], y de una forma un poco más precisa como la unidad de procesamiento gestionada por el sistema operativo [35]. 5 el desempeño de un programa ejecutándose en este tipo de arquitecturas se hace uso de la Ley de Amdahl para sistemas distribuidos [23] y de algunas de las funciones de métricas de tiempo proporcionadas en las librerías del lenguaje de programación C++. 1.4 METAS. Desarrollar un sistema de doble multicore con base a una red tipo Ethernet y dos sistemas Multicore. Esto es conectando dos máquinas con arquitectura core a través de una red de computadoras. Crear un algoritmo paralelo híbrido en lenguaje de programación C++, en el que se implementarán las librerías de MPI y OpenMp. Distribuir la carga de trabajo de manera equitativa entre los procesadores tomando en cuenta el acceso a memoria de estos (multiprocesadores o multi- computadores). Evaluar el desempeño de estos sistemas así como determinar potenciales condiciones de competencia entre los procesos. 1.4 CONTRIBUCIÓN Y RELEVANCIA. En el desarrollo de la presente tesis se abordó el problema de paralelización del método de entrenamiento no supervisado ART2A para redes neuronales [15, 30]. Como es bien sabido este tipo de entrenamiento tiene importantes aplicaciones, por ejemplo, en teoría del control, robótica, reconocimiento de patrones, entre otras. El enfoque con el que se abordó este problema fue mediante el uso de multiprocesamiento híbrido en el que se emplean los esquemas de paralelización de memoria compartida (SMP) [3,5] y paso de mensajes en arquitectura distribuida (MPI) [13,14]. Para determinar el rendimiento de ejecución del programa híbrido en múltiples procesadores se utilizaron funciones de métricas de tiempo proporcionadas en las librerías del lenguaje de programación C++ [1]. 6 Las afirmaciones que podemos deducir de los resultados obtenidos en los experimentos realizados con la implementación mencionada son los siguientes: 1. En el algoritmo ART2A existe una gran cantidad de dependencia de datos inherentes al mecanismo de aprendizaje en el entrenamiento. Esto induce al hecho de que en la paralelización de dicho algoritmo siempre exista una porción de código no paralelizable, que aunque se encontró como una fracción pequeña, esta porción impide escalar el procedimiento de paralelización a un número arbitrario deprocesadores [11, 23]. Tenemos entonces que el uso de varios procesadores y procesos no es garantía de un manejo eficiente en el procesamiento de la información. De hecho el análisis de resultados obtenidos durante la ejecución de los programas en donde empleamos ambas arquitecturas (explicadas en las secciones 1.2 y 1.3) determinará la mejor aproximación para la paralelización en cuanto al flujo propio de la información. 2. Cuando la dimensión de los vectores de entrenamiento es alta, entonces la dependencia de datos en las operaciones internas del procedimiento ART2A disminuye, por ejemplo, la multiplicación de matriz por vector, producto interno, y normalización de vectores, pueden entonces descomponerse en procesos paralelos obteniendo así un mejor rendimiento que en el caso secuencial. Remarcamos el hecho de que esto ocurre cuando la dimensión de los vectores es alta, pues de otra manera, el tiempo de procesamiento de las instrucciones adicionales para conseguir paralelismo superará al tiempo ganado por el proceso de paralelización. En resumen, pudimos observar en los experimentos, que aunque es posible paralelizar el procedimiento ART2A es necesario verificar si la dimensionalidad de los vectores de entrenamiento, así como también el número de vectores clasificados en W. La dimensión de estos nos permitirá mejorar o no el rendimiento del proceso. En resumen: se pudo observar que el tamaño de los datos a paralelizar es un factor muy importante a considerar en la programación paralela. 7 CAPÍTULO 2. GENERALIDADES: 2.1 INTRODUCCIÓN AL CAPÍTULO. En este capítulo se describen las características de las tecnologías empleadas para abordar el cómputo paralelo, la forma en que se comunican e intercambian información, así como también la manera de sincronización en ambos arquitecturas. Se explica lo que es un sistema multicore y algunas de las métricas de desempeño para evaluar el rendimiento de un programa al ejecutarlo en múltiples procesadores. Para implementar el modelo de cómputo paralelo se consideran dos tipos de arquitecturas ampliamente utilizadas, las cuales difieren en la forma en la que se comunican los procesadores [22, 6]: Sistemas con memoria compartida: En una aplicación paralela lo que se pretende es ejecutar un programa más rápidamente que en una versión secuencial, para ello lo que se hace es utilizar varios procesos, que ejecutan partes de un mismo programa, de tal manera que el tiempo empleado en la ejecución paralela de todos ellos sea menor que el tiempo empleado por un único proceso en resolver el problema [18]. Sistemas con memoria distribuida: En una aplicación distribuida existen diferentes procesos, que ejecutan distintas partes de un programa, dichos procesos colaboran y se comunican entre sí para alcanzar un mismo objetivo. 2.2 SISTEMAS CON MEMORIA COMPARTIDA “MULTIPROCESADOR” Consiste en un número de procesadores idénticos dentro de una misma máquina compartiendo una memoria principal global [3,32]. (fig. 2.1). 8 BUS . . . (fig. 2.1). Arquitectura de procesadores con memoria compartida. El acceso a la memoria principal global es a través de un bus. El bus es el canal de comunicación para llevar a cabo las operaciones de solicitud de memoria simultánea por varios procesadores y asegurar que todos los procesadores sean justamente atendidos con un mínimo retardo de acceso. El encargado de arbitrar las tareas, sus prioridades y recursos disponibles es el Sistema Operativo. Una de las mayores dificultades en sistemas de multiprocesamiento de memoria compartida es la competencia por recursos, principalmente por el acceso a memoria entre los procesadores [3,27]. Esta se incrementa conforme el número de procesadores sea mayor y más procesos activos se tengan en el sistema. A mayor grado de multiprogramación se presentan mayores necesidades de memoria. Cuando varios procesadores intentan accesar a la memoria compartida dentro del un periodo de tiempo pequeño, la memoria no le es posible atender todas las peticiones simultáneamente, por lo que algunos de los procesadores tendrán que esperar mientras otros son atendidos [3, 6, 21]. La programación eficiente en este tipo de arquitecturas se realiza mediante el control de hilos paralelos [3, 6,35]. Un hilo (thread) es un flujo de control independiente dentro de un proceso. Es esencialmente una ruta de ejecución a través del código de programa en proceso (fig. 2.2). Los hilos dentro de un mismo proceso suelen compartir los recursos asignados al proceso al cual pertenecen. La manera de identificar un hilo dentro de un proceso es por su identificador (ID) que es un valor entero único dentro de cada proceso. MEMORIA COMPARTIDA PROCESADOR 2 PROCESADOR 1 PROCESADOR n 9 Dos hilos en diferentes procesos pueden tener el mismo número de ID. Sin embargo, cada uno realizará operaciones diferentes. Con procesadores multicore se reparten los diferentes hilos entre los cores, de modo que varias aplicaciones avanzan a la vez, permaneciendo menos tiempo en espera, logrando con ello agilizar la ejecución de una tarea [3,22,35]. Debido a que los hilos dentro de un mismo proceso comparten las mismas direcciones físicas de memoria, éstos comparten las mismas estructuras y datos a nivel proceso. Sin embargo, cada hilo tiene información propia que no comparte con otros hilos. Esta información se refiere fundamentalmente al contexto de ejecución, pudiéndose destacar los siguientes datos: Contador de programa. Tope de pila. Estado de registros. Estado del hilo (thread). El estado de un hilo (listo, activo, bloqueado) en cualquier instante está definido por el proceso en memoria. Cada procesador ejecuta un hilo diferente, aunque cualquier hilo podría estar corriendo en cualquier procesador en cualquier momento. En un sistema de dos procesadores cada procesador puede estar corriendo diversos hilos, los cuales pueden pertenecer al mismo proceso o a diferentes procesos. Un hilo o proceso no está atado o ligado a un procesador particular. Esto lo determina el Sistema Operativo. Para un uso eficiente en esta arquitectura se propone el empleo de múltiples hilos (multithreads MT), ya que si se tiene un solo hilo esperando por los recursos del sistema no se hace uso adecuado del hardware. Si el hilo tiene que hacer cálculos extensivos entonces todas las demás aplicaciones tendrían que esperar. El usar multihilos puede remover los cuellos de botella. Un programa MT comienza con un hilo inicial, denominado hilo maestro. Este hilo inicial crea nuevos hilos, que son considerados hilos esclavos. (fig. 2.2) 10 Región Paralela Hilo Maestro Hilo Maestro Fig. 2.2 Diseño de una región paralela. El hilo maestro crea hilos esclavos Los nuevos hilos corren una rutina independiente y suministran otras corrientes de instrucciones operativas en el mismo espacio de direccionamiento de datos. El Sistema Operativo se ocupa de asegurar que cada hilo se mantenga activo. Esto lo hace con la asistencia del hardware y una gran cantidad de registros de transacciones. Cuando el temporizador del hardware decide que un proceso ha estado ejecutándose por suficiente tiempo entonces ocurre una interrupción1. El Sistema Operativo salva el estado actual del hilo mediante la copia de los registros del hilo desde la pila hacia una estructura de contexto y la guarda para su uso posterior. Para cambiar hacia diferentes hilos, el Sistema Operativo direcciona el procesador a la memoria correspondiente al hilo del proceso, luego restaura los registros que fueron guardados en la estructura de contexto en un nuevo hilo. A este proceso es llamado intercambio de contexto (contextswich) [4,5,18,35]. No hay que olvidar que el crear hilos y hacer intercambios de contexto le toma una cantidad finita de tiempo al procesador por lo que al momento de crear hilos se debe tomar en cuenta cuantos hilos se van a manejar en la aplicación paralela y asegurar que no exista una sobrecarga en intercambio de contextos. De no tomar en cuenta esto, se puede consumir mayor tiempo con múltiples hilos al ejecutar el código. Todo esto debido al intercambio de contexto y a la introducción de instrucciones propias del modelo paralelo que no son necesarias en el programa secuencial. Cabe mencionar que la rapidez en el intercambio de contexto varía de una máquina a otra, dependiendo de la velocidad 1 A nivel físico, una interrupción se solicita activando una señal que llega a la unidad de control. Ante esta solicitud, la unidad de control realiza un ciclo de aceptación de interrupción. Este ciclo se lleva a cabo cuando termina la ejecución de la instrucción máquina que se está ejecutando y consiste en las siguientes operaciones: 1. Salva algunos registros del procesador, como son el de estado y el contador de programa (CP). 2. Eleva el nivel de ejecución pasándolo al núcleo 3. Carga un nuevo valor en el CP por lo que pasa a ejecutar otro programa. 11 de memoria y el número de registros que copiar. Por lo regular el tiempo de conmutación varía entre 1 y 1000 microsegundos [35]. Otro aspecto es el control de condiciones de competencia. Una condición de competencia ocurre cuando varios hilos compiten por un mismo recurso. Para prevenir dichas condiciones y evitar que los hilos sean interrumpidos mientras se encuentran haciendo alguna operación cuya integridad es indispensable en un procedimiento, es necesario protegerlos. Esto se hace mediante mecanismos de sincronización, que no son más que la manera de coordinar los hilos para que puedan trabajar en conjunto. La sincronización entre hilos se realiza mediante variables globales compartidas que pueden ser coordinadas usando secciones críticas, semáforos o mutex. Una Sección Crítica (SC) es una porción de código que accesa a un recurso compartido y que solo a un hilo a la vez se le permite entrar a ejecutar dicho código, convirtiéndose en una operación atómica2. Cada hilo debe pedir permiso para entrar a la sección crítica, mediante algún fragmento de código que se denomina de forma genérica entrada en la sección crítica. Cuando un hilo sale de la sección crítica debe indicarlo mediante otro fragmento de código, que se denomina salida de sección critica. Mediante la utilización de Mutex (exclusión mutua) si un hilo se encuentra en la SC, ningún otro podrá entrar. Un semáforo es un objeto que permite controlar el acceso a un recurso compartido. Está compuesto de un contador y de una cola de espera. Cuando el contador es positivo, indica que existen recursos disponibles. Cuando es negativo indica la cantidad de procesos o hilos que esperan por el recurso [3, 4, 18.]. Un ejemplo de condición de competencia ocurre cuando varios hilos hacen despliegue de datos en pantalla. Debido a que el Sistema Operativo sólo le proporciona cierta cantidad de tiempo a cada hilo, los hilos compiten por el recurso. Un determinado hilo empezará a hacer su despliegue, pero al transcurrir el tiempo asignado para su ejecución, si aún no ha terminado de hacer su despliegue, el control del dispositivo de despliegue le será 2 Una operación atómica es una porción de código que no puede ser interrumpida mientras esta en ejecución [6]. 12 quitado y se transferirá a otro hilo, teniendo como resultado una potencial inconsistencia en los datos desplegados. 2.3 SISTEMAS CON MEMORIA DISTRIBUIDA “MULTICOMPUTADORA”. Un sistema Distribuido se define como un grupo de computadoras separadas físicamente.Cada máquina tiene su propia memoria y sólo se comunican entre ellas por medio de mensajes a través de una red de computadoras [3,18,35]. Cada una de las computadoras conectadas a la red posee sus componentes de hardware y software que el usuario percibe como un solo sistema. (fig. 2.3), fig. 2.3 Arquitectura de un Sistema Distribuido. Cada máquina tiene su propio procesador (P) y memoria (M) y se comunican por medio de mensajes a través de una red de computadoras. PASE DE MENSAJES COMUNICACIÓN A TRAVÉS DE LA RED P M P M P M P M P M P M http://es.wikipedia.org/wiki/Computadores 13 Un Sistema Distribuido se define según dos puntos de vista: 1. Desde un punto de vista físico, un sistema distribuido es un conjunto de procesadores (posiblemente heterogéneos), sin memoria ni reloj común, que se encuentran conectados a través de una red de interconexión. fig. 1.4 [3, 21, 22]. 2. Desde el punto de vista lógico, un sistema distribuido es un conjunto de procesos que no comparten recursos y que se ejecutan en una o más computadoras y que colaboran y se comunican entre ellos mediante el intercambio de mensajes. De estos dos puntos de vista se desprende que uno de los aspectos más importantes de un Sistema Distribuido es comunicar diferentes procesos entre sí con independencia del lugar donde se ejecuten. La plataforma fundamental sobre la que se construyen los sistemas distribuidos es una red de computadoras, lo cual permite que puedan compartir diferentes recursos como son: periféricos, datos o incluso procesadores. Una red de computadoras es un sistema de comunicación compuesto por una serie de componentes hardware y software que proporcionan los servicios necesarios para que los procesos que ejecutan en las distintas computadoras puedan comunicarse entre sí. En este tipo de arquitectura, puesto que los diferentes procesadores no comparten una memoria común, la única forma de comunicación posible es mediante el intercambio de mensajes. Un mensaje es un objeto lógico que se intercambia entre dos o más procesos. Para transmitir un mensaje a través de una red de interconexión es necesario descomponerlo en una serie de paquetes. Un paquete es la unidad de información que se intercambian dos dispositivos de comunicación. Para que la comunicación se lleve a cabo, es necesario el empleo de protocolos de comunicación. Un protocolo es un conjunto de reglas e instrucciones que gobiernan el intercambio de paquetes y mensajes. Utilizando pase de mensajes como mecanismo de comunicación entre procesos, únicamente existir un enlace de comunicación entre ellos. Los procesos se comunican mediante dos operaciones básicas: 14 send(destino, mensaje): envía un mensaje al proceso destino. receive(origen, mensaje): recibe un mensaje del proceso origen. La comunicación entre los procesos es de manera directa o indirecta, es decir, deben tener una forma de referirse a cada uno de ellos. Sin embargo, cualquiera que sea el método utilizado, el paso de mensajes siempre se realiza en exclusión mutua. La comunicación entre dos procesos es síncrona cuando los dos procesos han de ejecutar los servicios de comunicación al mismo tiempo, es decir, el emisor debe estar ejecutando la operación send y el receptor ha de estar ejecutando la operación receive. La comunicación es asíncrona en caso contrario. Existen tres combinaciones más habituales que implementan los distintos tipos de paso de mensajes: Envío y recepción con bloqueo: En este caso, tanto el emisor como el receptor se bloquean hasta que tenga lugar la entrega del mensaje. Esta es una técnica de paso de mensajes totalmente síncrona. Envío sin bloqueo y recepción con bloqueo: Esta es la combinación generalmente más utilizada. El emisor no se bloquea hasta que tenga lugar la recepción y por tanto puede continuar su ejecución. El procesoque espera el mensaje, sin embargo, se bloquea hasta que llega. Envío y recepción sin bloqueo: Se corresponde con una comunicación totalmente asíncrona en la que ningún proceso espera. Existen dos modelos muy utilizados para el desarrollo de aplicaciones cliente-servidor en sistemas distribuidos: Los sockets y las llamadas a procedimientos remotos. 1. Los sockets aparecieron en 1981 en la versión BSD 4.2 de UNIX [18]. Un socket es una abstracción que representa un extremo en la comunicación bidireccional entre dos procesos. Ofrece una interfaz para acceder a los servicios de red en el nivel de transporte de los protocolos TCP/IP. Utilizando esta interfaz, dos procesos que deseen comunicarse deben crear un socket o extremo de 15 comunicación cada uno de ellos. Cada socket se encuentra asociado a una dirección y permite enviar o recibir datos a través de él. (fig. 2.4) Máquina A Máquina B 2.4 Comunicación a través de sockets En una llamada a procedimiento remoto (RPC, Remote Procedure Call, Birrel 1984) [33,34], el programador no necesita preocuparse de cómo realiza la comunicación entre procesos. Simplemente realiza llamadas a procedimientos que serán ejecutados en computadoras remotas. En este sentido el programador desarrolla sus aplicaciones en forma convencional descomponiendo su software en una serie de procedimientos bien definidos. En una RPC, el proceso que realiza la llamada empaqueta los argumentos en un mensaje, se los envía a otro proceso y espera resultado. Por su parte, el proceso que ejecuta el procedimiento extrae los argumentos del mensaje, realiza la llamada de forma local, obtiene el resultado y se lo envía de vuelta al proceso que realizó la llamada. Este proceso es totalmente transparente al usuario. La sincronización de procesos en sistemas distribuidos es más complicada que en sistemas con memoria compartida, debido a que los procesos no comparten una memoria ni un reloj global común. Esto dificulta la forma de ordenar los eventos que ocurren en un sistema distribuido y la resolución de problemas de sincronización clásicos como los descritos en la sección 2.2. En un sistema con memoria compartida, la ordenación de eventos es un problema trivial puesto que existe un reloj físico común que permite proceso socket NÚCLEO envía proceso socket NÚCLEO recibe R E D 16 determinar que evento ocurrió antes que el otro. Esto no ocurre de igual manera en un sistema distribuido puesto que cada computadora dispone de su propio reloj. Una forma de ordenar eventos en sistemas distribuidos que carecen de un reloj global es utilizar el concepto de relojes lógicos propuesto por Lamport en 1978. Este concepto se basa en la relación de precedencia definida también por Lamport en 1978. Según Lamport, no es necesario recurrir a un reloj físico global para asegurar el orden de eventos en un sistema distribuido. Basta con asignar unas determinadas marcas lógicas de tiempo a los eventos [25]. 2.4 CARACTERÍSTICAS DE LAS ARQUITECTURAS REFERIDAS. La diferencia más importante entre un sistema distribuido y un sistema con memoria compartida es el acceso a memoria y, por ente, la comunicación entre procesos. Los sistemas con memoria compartida permite dividir las aplicaciones en varias tareas, lo que beneficia la modularidad y con ello disminución en tiempo de ejecución. Por tanto puede atender a varios usuarios de forma eficiente, interactiva y simultánea. Una de las características más importantes de los sistemas con memoria compartida es ahorro en el tiempo de proceso, ya que la información entre los cores viaja en el bus interno, que siempre es más rápido que cualquier bus externo, además de ahorro en energía En los sistemas de memoria distribuida una de las características más importantes es que se tiene la capacidad de crecimiento. Es relativamente fácil que un sistema distribuido crezca para adaptarse a las nuevas demandas de servicio, basta con conectar más computadoras a la red. Por otra parte, si un circuito de un procesador falla, a lo más afectará a un nodo conectado a la red, por lo que no afecta a la ejecución del sistema. Con sistemas de memoria compartida, la falla de un circuito afecta de manera potencial la ejecución del sistema. 17 2.5 SISTEMAS MULTICORE: El empleo de varios procesadores dentro de una sola máquina no es un concepto nuevo. Desde hace tiempo, las tarjetas madre de algunos equipos han permitido utilizar varios procesadores, normalmente dos o cuatro. Así también se tiene que se ha hecho uso de computadoras en clúster, de modo que todos actúen como si fueran una única computadora. La novedad introducida con las nuevas tecnologías es que ahora los procesadores estén montados sobre un único chip, con 2, 4, 6 u hasta 8 núcleos dentro del mismo encapsulado. El motivo principal para esto es la economía, pues resulta más barato producir ordenadores con dos cores que dos procesadores independientes en una misma tarjeta. Además, se produce un ahorro en el tiempo de proceso, ya que la información entre los cores viaja en el bus interno, que siempre es más rápido que cualquier bus externo. Con sistemas multicore, se reparten las diversas tareas entre los diferentes cores, de manera que varias aplicaciones avanzan a la vez, permaneciendo menor tiempo posible en espera del control del procesador. La tarea de repartir los programas entre los diferentes cores es realizada por el sistema operativo, por lo que al no depender de las aplicaciones, cualquier programa, sea antiguo o actual, funciona sin problema en un core. Otra de las ventajas de sistemas multicore es el ahorro de energía. Con un único procesador, una aplicación exigente pone a trabajar al tope al procesador, generando gran cantidad de calor. Si existen diferentes cores, solo trabajará al máximo el core que soporte esa aplicación en un determinado momento. Los otros tendrán una menor carga, lo que en conjunto hace que se disipe menos energía [11,3]. 2.6 MÉTRICAS DE DESEMPEÑO EN MULTIPROCESAMIENTO. Existen diversas formas de medir el desempeño de un programa corriendo sobre procesadores paralelos. Las medidas más comúnmente usadas son: el tiempo de ejecución, precio/desempeño, el speed-up (aceleración) y la eficiencia [23]. 18 La métrica más importante para correr un trabajo sobre una máquina dada es el tiempo de ejecución [23]. Precio/desempeño de un sistema paralelo es simplemente el tiempo transcurrido por un programa dividido por el costo de la máquina que corrió el trabajo. El Speed-up (Sc) y la eficiencia son las medidas más frecuentemente usadas para conocer que tan eficientemente se está utilizando. El Sc (aceleración) es la medida generalmente usada: Para obtenerla se ejecuta el mismo programa sobre un variado número de procesadores. El Sc es entonces el tiempo transcurrido en un programa secuencial por un procesador dividido entre el tiempo de ese mismo programa paralelo en n procesadores. Así la aceleración Sc para n procesadores se define como: Sc = Ec. 2.1 Donde: = Tiempo de ejecución del programa secuencial en un único procesador. = Tiempo de ejecución del programa paralelo en n procesadores. La eficiencia E de un programa es la razón entre la aceleración de un programa y el número de procesadores utilizados para la ejecución del mismo. E = Ec. 2.2 La eficiencia está relacionada con la fracción de tiempo que un procesador está siendo utilizado para ejecutar el programa. Idealmente si n procesadores utilizan el 100 % de su tiempo solamente para operaciones de computación, la eficiencia será igual a 1. Pero como un programa paralelo requiere comunicación entre procesadores, parte del tiempo 19 de los procesadores es consumido por esta actividad, llevandoa valores de eficiencia menor a 1 (E<1). Idealmente se espera que el Sc crezca linealmente y la E = 1 para todo n. Existen casos donde Sc es superlineal o sea que k procesadores resuelven una tarea en menos que un k-esimo del tiempo de corrida secuencial. Speed-up (sp) Lineal (ideal) Super lineal Típico. Fig. 2.5 Caso ideal de aceración para n procesadores. 2.7 LEY DE AMDAHL: Una de las maneras de saber el rendimiento de nuestra aplicación paralela es la aplicación de la Ley de Amdahl [3, 11, 23]. El incremento de velocidad de un programa utilizando múltiples procesadores en computación distribuida está limitado por la fracción secuencial del programa, es decir, aquélla que independientemente de cuantos procesadores se tengan para ejecutar el programa, solo puede ser realizada por uno solo de ellos; el resto del cálculo no podrá continuar hasta que se haya completado tal parte secuencial. La Ley de Amdahl propone normalizar el tiempo que toma realizar la operación en un solo procesador al valor de 1. La fracción del cálculo que se puede realizar Procesadores (n) 20 secuencialmente será (1-P), y la parte paralelizable es P. Con estos datos, el incremento de velocidad máximo que puede obtenerse con n elementos de procesamiento está dado por la siguiente ecuación: Sc = Ec. 2.3 Como un ejemplo, si nuestra aplicación no tiene sección secuencial (es decir 1-P) y (la parte serializada) P=1, entonces el incremento de velocidad estará dado exactamente por el número de elementos de procesamiento: =n Ec. 2.4 Por otra parte, si el 50% del código es secuencial es decir (1-P)=0.5 la ecuación queda: + Ec. 2.5 Suponiendo un número infinito de procesadores, esta ecuación da como resultado dos. Si el 50% del código es secuencial, por más procesadores que se agreguen el rendimiento nunca será mayor que 2 veces con respecto a una implementación en un único procesador. Así pues, la Ley de Amdahl establece un concepto esencial para la planeación y utilización de cualquier tipo de máquina paralela: el incremento en velocidad que podemos obtener está limitado por la fracción secuencial que emplee la aplicación, y no por el número de procesadores. Si el problema lo permite, se debe buscar implementar programas que tengan la menor cantidad de código 21 secuencial, debido a que este factor tiene mucha más importancia que el número de procesadores a utilizar. 2.8 RESUMEN DE CAPÍTULO. Para que varios procesadores sean capaces de trabajar juntos sobre el mismo problema computacional, deben ser capaces de compartir datos y comunicarse cada uno con otro. A lo largo de este capítulo se han expuesto dos arquitecturas especializadas cumpliendo estos requerimientos: Sistemas con memoria compartida y Sistemas con memoria distribuida. La principal diferencia en estas dos arquitecturas es el acceso a memoria. En computadoras con memoria compartida (multiprocesadores) todos los procesadores tienen acceso a una memoria principal global, por lo que comparten el mismo espacio de direcciones. La comunicación en estas arquitecturas es a través de variables globales y la programación eficiente es mediante el manejo de múltiples hilos. Sin embargo, una de las mayores dificultades es la coordinación en el acceso a los recursos, ya que un recurso, solo puede ser poseído por un solo hilo a la vez. Para evitar que más de un hilo accese a un mismo recurso y prevenir inconsistencia de datos es necesario emplear mecanismos de sincronización, lo cual hace que los hilos se coordinen para decidir en qué momento que hilo tiene derecho a ejecutarse y así prevenir condiciones de competencia. En sistemas con memoria distribuida, tanto la sincronización como la comunicación es por medio de mensajes enviados a través de la red. La comunicación es entre proceso y puede ser de manera directa o indirecta, es decir, deben tener una forma de referirse a cada uno de ellos. El paso de mensajes siempre se realiza en exclusión mutua, teniendo en cuenta los mecanismos de sincronización (síncrona o asíncrona). Debido a que en este tipo de arquitectura no existe un reloj común entre los procesadores la coordinación entre eventos es por medio de relojes lógicos (propuesto por Lamport) [25]. Además de la diferencia en acceso a memoria, existe un punto muy importante a recordar: en una aplicación distribuida lo que se hace es dividir un problema en varios procesos para que cooperen en la solución de éste, en una aplicación paralela lo que se pretende es acelerar el procesamiento, por lo que el proceso puede ser dividido en tareas 22 más granulares y cada una seguir un flujo de ejecución diferente, reuniendo al final resultados obtenidos. Tener recursos compartidos y gran cantidad de comunicación le resta eficiencia a la programación en estas arquitecturas. 23 CAPÍTULO 3. ARQUITECTURA DEL SISTEMA 3.1 INTRODUCCIÓN AL CAPÍTULO. En este capítulo se describe la arquitectura de hardware y software para hacer la implementación de cómputo paralelo. Se da una breve descripción de la interfaz y los pragmas utilizados para realizar paralelismo en cada una de las arquitecturas mencionadas, así como también las ventajas y desventajas que se presentan durante su uso. 3.2 DESCRIPCIÓN DE LA ARQUITECTURA DE HARDWARE. Para la construcción física del sistema propuesto se integran dos máquinas multicore con base a las interfaces de programación MPI y OpenMP. MPI para distribuir la carga entre los nodos que conforman el clúster y OpenMP para el manejo de hilos entre los procesadores en una misma máquina. Se establece un caso de estudio que permite determinar regiones críticas en el acceso a un recurso común que impone las restricciones en tiempo necesarias y permite visualizar la potencial serialización entre procesos. El recurso común es una matriz de pesos de la red neuronal ART2A llamada W. El programa está diseñado para su ejecución en N máquinas agrupadas en un clúster de alto rendimiento. En la práctica, para realizar los experimentos de desempeño se utilizaron dos máquinas conectadas entre sí a través de una interface de red del tipo LAN a una velocidad de 100 Mbps. Cada máquina está equipada con un procesador Intel Core 2 Duo que es capaz de ejecutar 2 hilos paralelos simultáneamente mediante el uso de dos núcleos [3,8]. La plataforma del sistema operativo utilizado es Windows XP de 32 bits. La herramienta utilizada para hacer trabajar estas dos máquinas como un clúster es MPI [3,7,13], con esto tenemos la capacidad de controlar cada uno de los procesos participantes en la ejecución del programa y direccionarlos hacia cualquiera de los dos equipos. La arquitectura de hardware se representa en la figura 3.1. 24 Fig. 3.1 Arquitectura híbrida de multiprocesamiento. Para aprovechar la arquitectura multicore se utiliza el estándar OpenMP. El esquema de programación en tales arquitecturas se realiza mediante el control de hilos. Haciendo uso de las directivas de OpenMP en el programa, solo hay que identificar la región que no tiene dependencia de datos y puede ser paralelizada para ser ejecutada por múltiples hilos [3,4,5,6,10]. Esto sin olvidar sincronizar el trabajo de los hilos para evitar condiciones de competencia [18,21]. Debido a que los sistemas multicore cuentan con la característica de compartir memoria, se dividen las operaciones dentro de cada proceso entre los núcleos (cores) con que cuenta el sistema. Para el desarrollo del código de este programa se utilizó el IDE (Integrate Development Environment) Microsoft para la programación en lenguaje C++ y el compilador de Intel ICC (Intel Compiler Colection) para obtener la funcionalidad de las directivas de OpenMP. Como herramienta de desempeñose utiliza la librería estándar time.h del OpenMP CPU 2 MEMORIA PRINCIPAL 1 CPU 1 OpenMP CPU 2 MEMORIA PRINCIPAL 1 CPU 1 MPI MPI RED 25 lenguaje C para analizar el comportamiento que toman los hilos y su mejora con respecto a ese mismo programa de manera secuencial. Así mismo se hace uso de las métricas de desempeño como es la ley de Amdahl. Los resultados parciales obtenidos en cada proceso son comunicados a un proceso maestro dentro de la red. Este coordina a los procesos trabajadores y evalúa los resultados que le son comunicados por la interfaz MPI a través de la red. También envían una respuesta a los procesos trabajadores, indicándoles el procedimiento a seguir para la actualización o incorporación de un nuevo vector al recurso compartido. Haciendo uso de ambas arquitecturas (descritas en la sección 2.2 Y 2.3) se puede observar el desempeño del sistema y su rendimiento en base a métricas de tiempo. Esto es evaluando el tiempo consumido al final de cada uno de los procesos, así como los recursos de comunicación a través de la red empleados en promedio. 3.3 DESCRIPCIÓN DE LA ARQUITECTURA DE SOFTWARE. En los sistemas distribuidos, debido a la ausencia de memoria compartida, toda la comunicación y mecanismos de sincronización se basa en la transferencia de mensajes. Esta es la parte fundamental y básica en cualquier sistema distribuido. Tal sistema tiene la característica de poder compartir recursos como son: hardware, software o datos. Sin embargo, para obtener un buen rendimiento, es importante reducir la latencia1 de las comunicaciones ya que estas afectan en gran medida al rendimiento. Otro aspecto a tener en cuenta para mejorar el rendimiento es evitar la existencia de cuellos de botella o recursos centralizados en el sistema. Para que el sistema pueda crecer, el sistema operativo debe evitar la existencia de componentes centralizados. En los sistemas de memoria compartida, la comunicación y sincronización es a través de variables globales 1 Latencia: Mide el tiempo necesario para transferir un mensaje vacío. La latencia contabiliza la sobrecarga introducida por el software de comunicaciones. 26 compartidas. Para lograr que varios hilos participen en la solución de un mismo problema, es necesario emplear mecanismos de sincronización, es decir, la manera de coordinar para accesar a un recurso compartido y con ello evitar inconsistencia de datos, esto se realiza a través de fragmentos de código que se denomina sección crítica. Para que un hilo pueda entrar a una sección crítica este debe pedir permiso, de igual manera cuando sale debe notificar para que los demás que están en espera de accesar y ejecutar su parte correspondiente puedan accesar. La estructura por tanto, de cualquier mecanismo que pretenda resolver el problema de la sección es la siguiente: Entrada en la sección crítica Código de la sección crítica Salida de la sección crítica Una forma representativa de dicho mecanismo es el diagrama 3.2, donde se puede observar que una sección crítica es una barrera que protege a un recurso compartido, y para que cada uno de los hilos pueda accesar a dicho recurso debe pedir permiso de entrar. Una vez que un hilo entre a ejecutar código de la sección crítica, bloquea el acceso, y ningún otro podrá entrar, hasta que el hilo que la posee salga de ella. Este acceso se controla en la sección crítica mediante un mutex o un semáforo. Sin embargo, el problema está en cómo extender un orden parcial a un ordenamiento total y arbitrario, usándolo en la resolución de problemas de sincronización. 27 Como se puede observar en las figura 3.2 y 3.3, dentro de la sección crítica los procesos pueden estar accediendo y modificando variables comunes, registros de Bases de Datos, algún archivo, etc. En general cualquier recurso compartido. La característica más importante es que cuando un proceso se encuentra ejecutando código de la sección crítica, ningún otro proceso se puede ejecutar su sección. Esto es debido a que cuando un proceso entra, la sección crítica se bloquea. Los demás procesos que necesiten accesar a dicha sección quedarán en espera hasta que el proceso la libere. Y así sucesivamente, hasta que no exista proceso alguno esperando por el recurso protegido por la sección crítica. Fig. 3.2 Sección Crítica. Solo un hilo a la vez puede ejecutar el código; los demás quedan en espera de la liberación. SALIDA DE LA SECCIÓN CRÍTICA (puede accesar el siguiente proceso en espera) MECANISMOS DE ACCESO A LA SECCIÓN CRÍTICA (MUTEX, SEMÁFORO) CÓDIGO DE LA SECCIÓN CRÍTICA ACCESO A RECURSO COMPARTIDO SOLO UN PROCESO A LA VEZ Lista de procesos en espera de accesar a la Sección crítica. 0 1 2 3 n 28 La forma típica en la que una sección crítica protege datos compartidos por múltiples procesos ejecutándose simultáneamente es la Figura 3.3 Por ejemplo, cuando un proceso escribe nuevos datos a un elemento dentro de un arreglo y actualiza el índice máximo de este arreglo, puede ocurrir una inconsistencia si consideramos que otro proceso corriendo simultáneamente en otro procesador trabajando sobre el mismo arreglo realiza algún cálculo sobre cada elemento en el arreglo. Si el segundo proceso ve el nuevo valor del índice antes que el arreglo contenga un dato válido entonces el cálculo es incorrecto. La definición de una sección crítica es una solución general a este tipo de problema. Por lo que solamente un proceso está ejecutando dicha sección crítica de código a la vez. La notificación de este estado será comunicada mediante un bloqueo. El siguiente diagrama de tiempo muestra tres procesos compartiendo los mismos datos que son manipulados dentro de una sección crítica del código. Fig. 3.3 Estados de los procesos en una sección crítica (ejecutándose y en espera). Sección crítica espera ejecutando Proceso 3 Proceso 1 Proceso 2 Proc. 1 bloquea Proc. 2 espera Proc. 2 bloquea Proc. 3 bloquea Proc. 3 espera Proc. 1 libera Proc. 2 libera Tiempo 29 Solo a un proceso a la vez se le permite ejecutar el código de la sección crítica, convirtiéndose con ello en una operación atómica. Si ningún proceso está ejecutando dentro de la sección crítica, la decisión de qué proceso entra en la sección se hará sobre los procesos que desean entrar. Además esta decisión debe realizarse en tiempo finito. TIPOS DE COMUNICACIÓN. 3.4 OpenMP. Para hacer la paralelización de hilos en cada uno de los procesos, se utilizan las directivas de OpenMP. Las librerías OpenMp proporcionan una forma rápida y eficiente para dar soporte a la programación multihilos, sin tener que considerar los detalles internos del funcionamiento de los hilos en determinado sistema operativo. Esto permite al programador enfocarse en el diseño sin tener que ocuparse de la implementación a detalle de los hilos. Además de que permite construir aplicaciones paralelas portables entre diferentes plataformas [2, 3, 4, 8]. El mecanismo de OpenMp funciona básicamente a través de directivas de compilación más que en el uso de funciones, de forma que la implementación del paralelismo se realiza lo más transparente posible para el código del programa. Las directivas OpenMP se aplican sobre un bloque estructurado, es decir, un enunciado simple o compuesto que tenga un solo punto de entrada y un solo punto de salida (sin saltos hacia dentro o fuera del bloque) [3]. La secuencia típica para crear una construcción OpenMP es: Definir un bloque estructurado, el cual será ejecutado por múltiples hilos en paralelo. En OpenMP, este bloque es llamado “región paralela”. Distribuir laejecución de la región paralela entre múltiples hilos Sincronizar el trabajo de los hilos paralelos durante la ejecución de la región paralela. Controlar el ambiente de datos. 30 Cuando un hilo entra en una región paralela, se crea un conjunto de hilos. El hilo original se convierte en el hilo maestro del conjunto y tiene como identificador el numero 0, y todos los hilos incluyendo al hilo maestro ejecutan la región en paralelo. Al final, el hilo maestro recoge los resultados y éste continúa con la ejecución. 3.5 Niveles de Paralelismo: En OpenMP, las dos principales formas para asignación de trabajo en hilos son: Niveles de ciclos paralelos. Regiones paralelas. Los ciclos individuales son paralelizados con cada uno de los hilos empezando una única asignación para el índice del ciclo. A éstos se le llama paralelismo de fina granulación. Con respecto a las regiones paralelas, cualquier sección del código puede ser paralelizado no precisamente por ciclos. Esto es algunas veces llamado paralelismo de gruesa granulación. El trabajo es distribuido explícitamente en cada uno de los hilos usando el único identificador (myid) para cada hilo. Esto frecuentemente se hace por el uso de declaración de if por ejemplo: if (myid == 0) donde myid es identificador de cada thread. Para especificar el número de hilos se puede utilizar la clausula num_threads en la directiva parallel, o bien se puede usar la función omp_set_num_threads. Ejemplo: 31 omp_get_thread_num(); regresa el número del hilo dentro del conjunto de hilos que está ejecutando la función. La variable thrd_num es privada para cada hilo del grupo, al que se le pueden anidar unas dentro de los bloques de otras. Por omisión, las construcciones parallel no se anidan, esto se puede cambiar con la función omp_set_nested o con la variable de retorno OMP_NESTED. Las construcciones de compartición de trabajo distribuyen la ejecución del enunciado asociado entre el número de hilos que encuentran, pero no lanzan nuevos hilos. Tampoco existe una barrera implícita sobre la entrada de la construcción. Ejemplo para el ciclo for: Donde: var operador_logico rb es una variable entera que no debe ser modificada dentro del cuerpo del enunciado for. expresión_incremento: puede ser uno de ++var, --var, var++, var--, var+=incremento, var=var-incremento. Región a ser paralelizada. Int a[5][5]; Omp_set_num_threads(5); #pragma omp parallel { Int thrd_num=omp_get_thread_num(); Zero_init(a[thrd_num], 5); } #pragma omp for lista_de_clausulas: for (var=1b; var operador_ lógico rb; expresión_incremento) 32 lb, rb, incremento son enteros invariantes dentro del ciclo operador_lógico puede ser >,<, >=,<=. La clausula schedule de la forma: Schedule(tipo[ , tamaño_de_trozo]). Especifica cómo son divididas las iteraciones entre los hilos del grupo. Si tipo es static, las iteraciones son divididas en trozos del tamaño especificado por tamaño_de_trozo, y son asignados de acuerdo al número del hilo. Si no, se especifica tamaño_de_trozo, las iteraciones son divididas en trozo aproximadamente iguales, con un trozo asignado a cada hilo. Si tipo es dynamic, las iteraciones son divididas en series de trozos, cada una conteniendo tamaño_de_trozo iteraciones. Cada trozo es asignado a un hilo que esté esperando por una asignación. Cuando no se especifica tamaño_de_trozo por omisión es 1. Si tipo es guided, las iteraciones son asignadas a hilos en trozos con tamaños decrecientes. Cuando un hilo termina su trozo de iteración asignado, se le asigna dinámicamente otro trozo, hasta que no quede ningún trozo. Si tipo es runtime, los tamaños de los trozos se especifican en tiempo de ejecución. Esto se establece con la variable de entorno OMP_SCHEDULE. Existe una barrera implícita al final de la construcción para el ciclo for, a menos que la clausula nowait sea especificada. 3.6 Directivas y construcciones de sincronización. En adición a las barreras de sincronización implicadas a algunas construcciones está la directiva barrier. #pragma omp barrier 33 Se puede declarar explícitamente para sincronizar todos los hilos en un proceso. Cuando esta directiva se encuentra, cada hilo espera hasta que todos los otros han alcanzado este punto. Para declarar una sección crítica se emplea la siguiente directiva: #pragma omp critical [(name)] Bloque estructurado Un hilo espera al comienzo de la sección crítica hasta que ningún otro hilo esté ejecutando una región crítica. Estas son algunas de las directivas más utilizadas en OpenMP. Existen otras cuya referencia se pueden ver en [10] 3.7 MPI: MPI (“Message Passing Interface”, Interfaz de Paso de Mensajes) es un estándar para la comunicación entre procesos que define la sintáxis y la semántica de las funciones contenidas en una librería de paso de mensajes, diseñada para ser usada en programas que exploten la existencia de múltiples procesadores. Su principal característica es que no precisa de memoria compartida, por lo que es muy importante en la programación para sistemas distribuidos. Los elementos principales que intervienen en el paso de mensajes son: el proceso que envía, el proceso que recibe y el mensaje. Dependiendo de si el proceso que envía espera a que el mensaje sea recibido, se puede hablar de pase de mensajes síncrono o asíncrono. En el paso de mensajes asíncrono, el proceso emisor no espera a que el mensaje sea recibido, y continúa su ejecución, siendo posible que vuelva a generar un nuevo mensaje y a enviarlo antes de que se haya recibido el anterior. Por este motivo se suelen emplear buzones, en los que se almacenan los mensajes a espera de que un proceso los reciba. Generalmente empleando este sistema, el proceso emisor sólo se bloquea o para cuando finaliza su ejecución, o si el buzón está lleno. 34 En el paso de mensajes síncrono, el proceso que envía espera a que un proceso lo reciba para continuar su ejecución. La Interfaz de Paso de Mensajes es un protocolo de comunicación entre computadoras. Es el estándar para la comunicación entre los nodos que ejecutan un programa en un sistema de memoria distribuida. Las implementaciones en MPI consisten en un conjunto de bibliotecas de rutinas que pueden ser utilizadas en programas escritos en los lenguajes de programación como son: C, C++, Fortran y Ada. La ventaja de MPI sobre otras bibliotecas de paso de mensajes es que los programas que utilizan la biblioteca son portables [7,13,14]. 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. MPI le asigna a cada proceso una variable que se denomina rank (identificador del proceso dentro del comunicador 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, que permite determinar qué proceso ejecuta determinada porción de código. En MPI se define un comunicador como una colección de procesos que pueden enviarse mensajes entre sí. El comunicador básico se denomina MPI_COMM_WORLD. MPI también permite definir subgrupos de comunicadores a partir del comunicador básico, teniendo por tanto cada proceso un identificador único dentro de cada grupo [13]. 3.8 Llamadas utilizadas para inicializar, administrar y finalizar comunicaciones. MPI dispone de 4 funciones primordiales que se utilizan en todo programa con MPI. Estas son: 1. MPI _Init. Esta función debe ser utilizada antes de llamar a cualquier otra función de MPI y solo debe ser llamada una sola vez. Int MPI_Init(int *argc, char ***argv) 35 2. MPI_Comm_size. Permite determinar el número total de procesos que pertenecen a un comunicador. Int MPI_Comm_size(MPI_Commcomunicador, int* numprocs) 3. MPI_Comm_rank permite determinar el identificador (rank) de proceso actual. Su sintaxis es: Int MPI_Comm_rank(MPI_Comm comunicador, int* identificador) 4. MPI_Comm_Finalize. Esta función termina una sesión MPI. Debe ser la última llamada a MPI que un programa realice. Permite liberar la memoria usada por MPI. Int MPI_Init(int *argc, char ***argv) 3.9 Llamadas utilizadas para transferir datos entre dos procesos: La transferencia de datos entre dos procesos se consigue mediante las llamadas: MPI_Send permite enviar información desde un proceso a otro. MPI_Recv permite recibir información desde otro proceso. Estas llamadas devuelven un código que indica su éxito o fracaso, y 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. 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. 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 dato a ser enviado en el mensaje. 36 El count es el número de copias del dato a ser enviado. 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: 1. La fuente, identifica al proceso transmisor. 2. El destino, identifica al proceso receptor. 3. El comunicador. El comunicador especifica el grupo de procesos a los cuales pertenecen la fuente y el destino. 4. Una etiqueta (tag), permite clasificar el mensaje. El campo etiqueta es un entero definido por el usuario y se utiliza para distinguir los mensajes que recibe un proceso [13]. 3.10 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. A continuación presentamos algunas funciones empleadas en nuestra aplicación: MPI_Barrier permite realizar operaciones de sincronización. Este tipo de funció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 instrucción. MPI_Bcast permite a un proceso enviar una copia de sus datos a otros procesos dentro de un grupo definido por un comunicador. MPI_Scatter establece una operación de distribución, en la cual un dato se distribuye en diferentes procesos. Cualquier programa paralelo con MPI puede implementarse con tan solo 6 funciones, todas ellas empiezan con MPI_ y obligan a que todos los programas escritos en MPI contengan la directiva: 37 #include “mpi.h” Este archivo contiene las definiciones, macros y prototipos de función necesarios para compilar los programas escritos en MPI. MPI suministra dos tipos de operaciones de comunicación: Una operación punto a punto. Esta operación involucra a dos procesos, uno de los cuales envía un mensaje y el otro recibe el mensaje. Una operación colectiva. Involucra un grupo de procesos. Los grupos definen el alcance de las operaciones de comunicación colectivas. La fuente y el destino de un mensaje se identifican mediante el rango dentro del grupo. Para comunicación colectiva, el emisor especifica el conjunto de procesos que participan en la operación. Luego, el emisor restringe el acceso y proporciona un direccionamiento independiente de la máquina a través de los rangos. No obstante, MPI permite definir otros grupos y dentro de cada grupo cada proceso tiene un identificador (un número entero) único, permitiendo que un proceso pueda pertenecer simultáneamente a varios grupos y tener un identificador distinto en cada uno de ellos [14]. Esto es con el fin de evitar comunicación extra y solo los procesos que tengan más relación se encuentren en un mismo grupo. Esto se hace partiendo del comunicador universal mediante la siguiente función. int MPI_Comm_split (MPI_comm comm, int color, int key, MPI_Comm *newcom); MPI_Comm_split(MPI_COMM_WORLD, color, myid, &comMaTrab); MPI_COMM_WORD es un entero y es el identificador universal del grupo. La palabra clave color divide los procesos y newcom es el nombre del nuevo grupo que se creará. Cada proceso tiene un identificador único dentro de cada grupo. 38 VENTAJAS Y DESVENTAJAS 3.11 VENTAJAS: Un programa multihilos se puede correr de manera concurrente en sistemas con un solo procesador. Un programa secuencial se puede convertir a versión multihilos. Solo se necesita identificar la región a paralelizar e insertar directivas de compilador en lugares apropiados, teniendo en cuenta la dependencia de datos, y con ello evitar deadlocks. Las librerías de MPI se encargan de hacer el intercambio de la información de manera transparente, por lo que el usuario solo se enfoca en el desarrollo lógico del mensaje. Existe una implementación de dominio público llamada MPICH [27,29]. 3.12 DESVENTAJAS: OpenMP tiene una ventaja en la simplicidad de la programación y la portabilidad desde los programas seriales, aunque los sistemas de memoria-compartida en general tienen una pobre escalabilidad. Agregando procesadores adicionales a un esquema de memoria- compartida incrementa el tráfico en el bus del sistema, haciendo más lento el tiempo de acceso a memoria y demorando la ejecución de un programa. Un esquema de memoria-distribuida, sin embargo, tiene la ventaja de que cada procesador está separado con su propio bus de acceso a la memoria. Por ésta causa, son más escalables. En adición, es posible construir sistemas más grandes y baratos conectado computadoras vía red de trabajo. Por otra parte, si un procesador dentro de un sistema de memoria compartida falla, es posible la caída del sistema. Por el contrario, si esto ocurre en un sistema de memoria distribuida, el sistema puede continuar aún con los procesadores disponibles, ya que sólo afectará a un solo nodo. Debido a la compartición de variables, es necesario utilizar secciones críticas, deteniendo de alguna manera la ejecución de otros hilos que requieran accesar a algún recurso protegido por la sección crítica. 39 En sistemas distribuidos, debido a la ausencia de memoria compartida, los mecanismos de comunicación y sincronización entre los procesos son a través de la red, por lo que se puede llevar a consumir mayor tiempo en comunicacion que en procedimientos de cálculo propios del problema. 3.13 RESUMEN DE CAPÍTULO: En este capítulo se describen las arquitecturas del hardware y software para llevar a cabo cómputo paralelo en arquitectura multicore y su integración a una red de computadoras (clúster de computadoras), creando en esta forma una plataforma de multiprocesamiento híbrida basada en dos importantes modelos de memoria: compartida y distribuida. En este esquema se tienen recursos de software compartidos entre los diferentes procesos. Para la implementación de estas dos arquitecturas se propone el uso de dos interfaces de programación que permiten realizar de manera transparente la programación en paralelo: OpenMP y MPI. OpenMP en sistemas multicore para la ejecución de un proceso de manera más rápida. MPI para distribuir la carga entre los diferentes procesos y procesadores participantes en la aplicación. En el capítulo 4 se plantea la arquitectura de software que realiza la división de los procesos que entrenan la red neuronal, en un proceso administrador y n-1 procesos trabajadores. Esto seplantea con el fin de que solo exista comunicación entre los procesos que realizan entrenamiento (procesos trabajadores) y que tengan que comunicarse datos. Sin embargo, para llevar a cabo este modelo de programación en paralelo, es necesario emplear mecanismos tales como: sincronización en el acceso a los recursos compartidos y la forma en que se comunican los diferentes procesos. 40 CAPÍTULO 4 CASO DE ESTUDIO. 4.1 INTRODUCCIÓN AL CAPÍTULO. En este capítulo se describe detalladamente las etapas de la red neuronal que es el caso de estudio. Se describen tres casos diferentes: En el primer caso se describe el multiprocesamiento en ambas arquitecturas, se realiza la implementación de la interfaz MPI y los pragmas de OpenMP obteniendo una ejecución hibrida de ambas. Como segundo caso se aborda el paralelismo sólo en sistemas multicore. Debido a la transparencia en la utilización de los pragmas propios de OpenMP, solo es necesario identificar la región a paralelizar en el que no debe existir dependencia de datos. Por último, como tercer caso se hace la implementación de manera serial. En cada uno de los casos se toma tiempo de ejecución, esto para determinar la conveniencia del procesamiento paralelo. 4.2 DESCRIPCIÓN DE LA RED NEURONAL ART2A. ART2A es una red neuronal de entrenamiento no supervisado, cuya estructura consiste de dos redes neuronales ART [16]. Su objetivo de es la detección, localización y clasificación de defectos y fallas internas. La información proporcionada a la red para su entrenamiento es a través de una base de datos que contiene información suficiente de cada tipo de defecto. La forma de entrenamiento es comparar las entradas presentes con categorías aprendidas seleccionadas. Las categorías aprendidas se encuentran almacenadas en una estructura multidimensional (matriz) [15]. Su entrenamiento se basa en 3 fases: 1. La fase de pre-procesamiento. 2. La fase de búsqueda. 3. La fase de adaptación. 41 1 La fase de pre-procesamiento es la creación de un vector de entrada I. El vector de entrada I contiene información del fenómeno a ser estudiado A, bajo la siguiente ecuación: I= . . . . . . . .Ec. 4.1 Dando como resultado un vector normalizado I. Los prototipos de W pueden ser inicializados con valores aleatorios o constantes en el rango de [0,1]. El parámetro de vigilancia ρ es el grado mínimo de similitud entre el vector de entrada I, y cualquier categoría definida en que pertenece a W. El grado de aprendizaje β afecta que tan rápido el algoritmo realiza los cambios. Este es elegido para evitar que las categorías se muevan demasiado rápido y por tanto desestabilizando el proceso de aprendizaje. El parámetro de elección α є [0, ∞] define la máxima profundidad de búsqueda para un adecuado agrupamiento suministrando un desborde de punto flotante. 1. La fase de búsqueda, es una fase iterativa que puede ser dividida dentro de dos procesos: A. La elección. B. La comparación. A. La elección. Una vez que el vector de entrada I es calculado, este se debe comparar con los prototipos ya definidos en W. Este proceso está encargado de 42 encontrar la categoría óptima, definida como la categoría similar para I respecto a la conjunción difusa. I= { , }. . . . .Ec. 4.2 El valor máximo de la conjunción difusa corresponde a una alta actividad de red Tj (número de vectores aprendidos en W), donde j es la posición del vector ganador dentro de W. B. El proceso de comparación. Si el grado de similitud entre la entrada y cualquier categoría es mayor o igual a ρ, entonces el prototipo j, es elegido para representar el grupo que contiene la entrada, se dice que la red alcanza resonancia. Si el grado de similitud es ρ, entonces otra categoría debe ser seleccionada y comparada con el vector I. Si cualquier prototipo o categoría no alcanza el grado de similitud, entonces el vector I debe ser agregado a W como un nuevo prototipo. 2. La adaptación es la última fase en el algoritmo, donde el prototipo ganador tiene que ser actualizado con respecto al patrón de entrada por: = β( ) + (1 - β) Ec.4.3 El siguiente diagrama de flujo representa el entrenamiento de la red neuronal ART2A. 43 Figura 4.1 Algoritmo general de la red ART CASOS DE ESTUDIO: El caso de estudio es el entrenamiento de la red neuronal descrita. El entrenamiento es realizado en un sistema multiprocesador. El objetivo es estudiar el comportamiento de los sistemas multiprocesador cuando se tiene un recurso compartido. Para realizar el entrenamiento, se hace uso de varias técnicas. MPI para realizar la comunicación entre procesos y haciendo uso del software de Intel “OpenMP” para I, W, ρ, β, α T(j)= j=N red j = j + 1 T(J)=max{T(j): j=1, . . . . N} Y= W =[W,I] Y(J) =0 = β(I* )+(1-β) 44 manjar hilos dentro de cada proceso. La paralelización se lleva a cabo dentro de un ciclo para manejar los hilos en cada proceso y así acelerar la operación matricial. El número de hilos empleados en la aplicación se maneja de acuerdo al número de procesadores en la máquina. En este caso particular, cada máquina tiene 2 procesadores (núcleos) y creamos 2 hilos, con el fin de que cada hilo se ejecute en un núcleo diferente y evitar consumo de tiempo en intercambio de contexto [4,6]. Para obtener datos correctos se hace la sincronización de los hilos. Al final de una región paralela hay una sincronización implícita manejada de manera transparente por OpenMP. Solo al hilo maestro (0) se le permite continuar con la ejecución. El procedimiento para tratar el problema de entrenamiento de redes ART2A se detalla en las secciones siguientes: 4.3 Caso 1: “IMPLEMENTACIÓN HÍBRIDA” Se cuenta con un clúster de computadoras con arquitectura multicore. Para la programación se hace una implementación de ambas interfaces de programacion OpenMP y MPI, obteniendo un paradigma de programación híbrido. MPI para conectar las máquinas dentro del grupo para formar una máquina virtual con mayor capacidad de procesamiento. Por otra parte, se utiliza OpenMp para explotar el paralelismo proporcionado por la arquitectura de memoria compartida. Como se ha mencionado anteriormente, el entrenamiento de la red neuronal está diseñado para N procesos. En la práctica se realizó en dos máquinas con características multicore conectadas a través de una red LAN, teniendo un total de 4 procesadores para la ejecución del problema (dos por máquina). Debido al análisis realizado en el que se determinó que existe dependencia de datos, compartición de recursos y una necesaria comunicación entre los procesos, la resolución de problema se diseñó de la siguiente manera: 45 Los procesos se dividen en: proceso administrador o consumidor (proceso 0) y en procesos trabajadores o productores (n-1 procesos). I. El proceso consumidor (0), se encarga de recoger datos de los n-1 procesos productores. Este proceso no realiza tareas de cálculo matricial, solamente realiza tareas de sincronización, evaluación y envío de ordenes hacia los demás procesos que se derivan de la ponderación de los resultados recolectados. El proceso 0 contiene un buffer de recolección de datos correspondientes a la evaluación de la multiplicación de los vectores de entrada por la matriz de pesos en los procesos trabajadores. Cada proceso trabajador envía su resultado parcial al proceso 0, el cual se encarga de obtener un resultado global para el cada iteración del proceso ART2A. El proceso 0 controla las comparaciones de los resultados parciales con las siguientes variables globales: a. Una variable llamada RO (ρ) que es el valor de vigilancia dentro de la red. b. Una variable llamada ALFA (β) que es
Compartir