esis (computación) - Heterogeneous Computing Laboratory

Anuncio
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”
E
T
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.
2009.
0
ÍNDICE:
CAPÍTULO 1
INTRODUCCIÓN
1.1
Introducción.
1
1.2
Caso de estudio.
3
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.7
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
1
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.
41
4.2
Descripción de la red neuronal.
41
4.3
Caso de estudio 1
Implementación híbrida.
45
4.4
Caso de estudio 2
Implementación en sistemas multicore.
53
4.5
Caso de estudio 3
Implementación en serie.
56
4.6
Resumen de de capitulo.
CAPÍTULO 5
58
MÉTRICAS Y ANÁLISIS DE RESULTADOS
5.1
Introducción al capítulo.
60
5.2
Aplicación de métricas de desempeño en sistemas multicore
60
5.3
Conclusión de capítulo.
70
CAPÍTULO 6
CONCLUSIONES
72
APÉNDICE.
Código Hibrido. Utilizando las interfaces de programación MPI y OpenMP.
74
Código serial y/o Paralelo.
88
2
Referencias Bibliográficas
92
Í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.
44
4.2
Matriz de pesos de la red neuronal (Recurso compartido).
47
4.3
Paralelización de la red ART2A.
48
4.4
Vectores de dimensionalidad X valores mayores y posiciones correspondiente
49
4.5
Un ciclo completo de entrenamiento de la red ART2A en paralelo.
51
4.6
Hilos en acción.
53
4.7
Región paralela con hilos.
54
4.8
(a) Flujo de ejecución de hilos de manera paralela.
56
4.8
(b) Flujo de ejecución de hilos de manera concurrente.
56
4.9
Diagrama de flujo de la red ART2A de manera secuencial.
57
5.1
Aceleración de entrenamiento de la ART2A en 4 procesadores.
67
5.2
Tiempos de ciclo de ejecución del entrenamiento de la ART2A.
69
3
ÍNDICE DE TABLAS
Tabla de resultados de entrenamiento de la red neuronal ART2A con hilos (RO=0.4)
62
Tabla de resultados de entrenamiento de la red neuronal ART2A con hilos (RO=0.1)
63
Tabla de resultados de entrenamiento de la red neuronal ART2A de 3 y 4 procesadores
66
Tabla de resultados de entrenamiento de la red neuronal ART2A de 10 procesos
67
4
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
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
1
[22]. En una máquina secuencial, el procesador ejecuta una secuencia de instrucciones y
operan con una secuencia de datos (fig. 1.1). Para su procesamiento un problema 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
...
Problema
N/N
CPU
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
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
2
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
CPU 1
TAREA 1
. . .
. . .
CPU 2
TAREA 2
.
…
. . .
.
.
CPU n
. . .
TAREA n
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).
2
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
3
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 procesos 1 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
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 el Vtune (analizador de rendimiento) en
sistemas multicore.
1
Proceso. Un proceso se define como un programa en ejecución.
4
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 multicomputadores).
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].
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].
5
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 nos impide escalar el procedimiento de paralelización a un número
arbitrario de procesadores [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 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 numero de vectores clasificados en W. La dimensión
de estos nos permitirá mejorar o no el rendimiento del proceso. En resumen: el tamaño
de los datos a paralelizar es un factor muy importante a considerar en la programación
paralela.
6
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,22]. (fig. 2.1).
7
MEMORIA
COMPARTIDA
BUS
PROCESADOR
PROCESADOR
1
2
. . .PROCESADOR
n
(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]. 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. 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.
8
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]. Debido a que los hilos dentro de un
mismo proceso comparten las mismas direcciones físicas de memoria, estos 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)
9
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ón2. 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 (context swich)
[4,5,18]. 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.
2
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.
10
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ómica 3.
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á
quitado y se transferirá a otro hilo, teniendo como resultado una potencial inconsistencia
en los datos desplegados.
3
Una operación atómica es una porción de código que no puede ser interrumpida mientras esta en
ejecución.
11
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]. 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),
P
P
P
M
M
M
PASE DE MENSAJES
COMUNICACIÓN A
TRAVÉS DE LA RED
P
P
M
M
P
M
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.
12
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:
13
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 proceso que 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
14
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
proceso
proceso
envía
recibe
socket
socket
NÚCLEO
NÚCLEO
R E D
2.4 Comunicación a través de sockets
En una llamada a procedimiento remoto (RPC, Remote Procedure Call) [Birrel, 1984], 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
15
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.
16
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 algoritmo paralelo 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].
17
La métrica más importante para correr un trabajo sobre una máquina dada es el tiempo de
ejecución.
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
18
de los procesadores es consumido por esta actividad, llevando a 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.
Procesadores (n)
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
19
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 el algoritmo que emplee nuestra 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 secuencial,
20
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
21
pretende es acelerar el procesamiento, por lo que el proceso puede ser dividido en tareas
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.
22
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.
23
OpenMP
OpenMP
CPU
1
CPU
2
CPU
1
CPU
2
MEMORIA
PRINCIPAL
MEMORIA
PRINCIPAL
1
1
MPI
MPI
RED
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ño se utiliza la librería estándar time.h del
24
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 latencia 4 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
4
Latencia: Mide el tiempo necesario para transferir un mensaje vacio. La latencia contabiliza la
sobrecarga introducida por el software de comunicaciones.
25
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.
26
n
3
2
1
0
Lista de procesos en espera
de accesar a la Sección
crítica.
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
SALIDA DE LA SECCIÓN CRÍTICA
(puede accesar el siguiente proceso en
espera)
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.
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.
27
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.
Proc. 2 espera
Proc. 3 espera
Proceso 1
Proceso 2
Proceso 3
espera
Sección
crítica
ejecutando
Proc. 1 bloquea
Proc. 1 libera
Proc. 2 bloquea
Tiempo
Proc. 3 bloquea
Proc. 2 libera
Fig. 3.3 Estados de los procesos en una sección crítica (ejecutándose y en espera).
28
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 de la forma 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 la ejecució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.
29
Controlar el ambiente de datos.
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:
30
Int a[5][5];
Omp_set_num_threads(5);
#pragma omp parallel
{
Int thrd_num=omp_get_thread_num();
Región a ser
paralelizada.
Zero_init(a[thrd_num], 5);
}
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:
#pragma omp for lista_de_clausulas:
for (var=1b; var operador_ lógico rb; expresión_incremento)
Donde:
var operador_logico rb es una variable entera que no debe ser modificada dentro
del cuerpo del enunciado for.
31
expresión_incremento: puede ser uno de ++var,
--var, var++, var--,
var+=incremento, var=var-incremento.
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 esta la
directiva barrier.
32
#pragma omp barrier
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 hayan alcanzado
este punto.
Para declarar una sección crítica se emplea la siguiente directiva:
#pragma omp critical [(name)]
Bloque estructurado
A restringir la ejecución del bloque estructurado asociado a un simple ciclo a la vez. 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.
33
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.
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].
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.
34
Int MPI_Init(int *argc, char ***argv)
2. MPI_Comm_size.
Permite determinar el número total de procesos que
pertenecen a un comunicador.
Int MPI_Comm_size(MPI_Comm comunicador, 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.
35
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.
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.
36
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:
#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.
37
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.
VENTAJAS Y DESVENTAJAS
3.11 VENTAJAS:
Las librerías OpenMp proporcionan una forma rápida y eficiente de 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.
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:
Donde OpenMP tiene una ventaja es en la facilidad de la programación y la portabilidad
de los programas seriales, aunque los sistemas de memoria-compartida en general tienen
una pobre escalabilidad. Agregando procesadores adicionales a un esquema de memoriacompartida 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 memoriadistribuida, sin embargo, tiene la ventaja de que cada procesador está separado con su
propio bus de acceso a la memoria. Por ésta causa, son mucho más escalables. En
38
adición, es posible construir más grandes y baratos clúster usando las comodidades del
sistema conectado vía red de trabajo. Por otra parte, si un circuito 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 aun con los
procesadores disponibles, ya que solo afectara 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. , ya que solo uno podrá estar dentro de esta.
En Sistemas distribuidos debido a la ausencia de memoria compartida, los mecanismos
de comunicación y sincronización entre los procesos es a través de la red, por lo que se
puede llevar a consumir mayor tiempo en dichos mecanismos 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 división de los procesos
que entrenan la red neuronal, en un proceso administrador y n-1 procesos trabajadores.
Esto se plantea con el fin de que solo exista comunicación entre los procesos que realizan
39
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
El vector resultante I es el vector normalizado.
Los prototipos iniciales o categorías en W, los parámetros como el valor de
vigilancia (ρ), aprendizaje (β) y elegido (α).
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.
42
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
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
ρ, entonces otra categoría debe ser
grado de similitud es
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
I, W, ρ, β, α
T(j)=
j=j+1
j=N
red
T(J)=max{T(j): j=1, . . . . N}
Y=
W =[W,I]
= β(I*
Y(J) =0
)+(1-β)
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
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 empleamos un paradigma de programación híbrido MPI y OpenMP,
usando MPI para conectar las máquinas dentro del grupo para formar una máquina
virtual con mayor capacidad de procesamiento. Por otra parte, utilizamos 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:
Los procesos se dividen en: proceso administrador o consumidor (proceso 0) y en
procesos trabajadores o productores (n-1 procesos).
45
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 la porción de aprendizaje de la red.
1. Al iniciar la aplicación, todos los procesos trabajadores tienen la matriz de
pesos de la red neuronal en memoria en un estado inicial “un solo vector” (fig.
4.2). Los procesos trabajadores son los que se encargan de realizar cálculos de
entrenamiento de la ART2A.
Esta matriz es el recurso compartido que está formada por vectores con categorías
aprendidas y es de tamaño MxN. Este recurso
será modificado por los procesos
trabajadores, dependiendo de resultados administrados por el proceso 0. Cada renglón en
W es un vector de pesos (entrenado) de dimensionalidad N. M es el máximo número de
vectores que puede contener la matriz, j son los vectores actuales aprendidos en W. El
límite de M está definido con anticipación, aún
cuando sus últimos renglones se
encuentren vacios durante la mayor parte del proceso.
46
Posición
Vectores
actuales
0
1
1
2
2
3
3
.
4
j
2
3
.
.
.
N
vector
.
Máximo límite
de vectores
.
M
MN
Fig. 4.2 W= Matriz de pesos de la red neuronal ART2A
(Recurso compartido)
Cada computadora en el grupo tiene una base de datos (BD) que contiene información
suficiente de las características de los defectos y fallas a cerca del fenómeno a ser
estudiado. Los diferentes procesos existentes en una misma computadora comparten la
misma BD y cada uno trabaja con un vector diferente de entrada de manera paralela.
En la figura 4.3 podemos observar la división de los procesos para realizar el
entrenamiento de la ART2A. Los procesos trabajadores que comparten W y se encargan
de realizar cálculos de entrenamiento de la red se encuentran agrupados en un
comunicador exclusivo. Esto es con el fin de evitar comunicación extra con procesos que
no tengan nada que ver con proceso de entrenamiento. Una vez realizado operaciones
correspondientes de entrenamiento en cada uno de los procesos trabajadores envían datos
al proceso 0 (esto de acuerdo a los pasos 1-4). El proceso 0 solo se encarga de administrar
datos recibidos de los procesos trabajadores. A su vez es el encargado de determinar si el
vector ganador se incorpora o se actualiza, esto es dependiendo del resultado de la
comparación con el valor de vigilancia RO (se explica a continuación).
47
Distribución
Particionamiento
Proc. administrador recibe
resultados parciales obtenidos
de proc trabajadores.
Proceso 0
Problema
Proceso 1
(Entrenamiento
de
la
red
neuronal)
Comunicación
Proceso 2
Comunicador entre procesos
trabajadores
Proceso n
Comunicación
entre procesos
trabajadores
Fig 4.3 Paralelización de la red ART2A
ART2A
El proceso de entrenamiento se detalla en los siguientes puntos.
1. Cada proceso trabajador toma datos de la BD en un vector llamado U de
dimensión N.
2. Una vez que se obtiene U[N], éste se normaliza en base a la norma Euclidiana
(Ec. 1), Los resultados obtenidos son almacenados en un nuevo vector llamado I
de dimensión N. Cada proceso trabajador realiza este paso.
Ec. 4.4
3. Continuando con el entrenamiento de la red neuronal, cada componente del
vector I multiplica a cada uno de los elementos de cada vector de W haciendo
una suma acumulada de estos productos y almacenando resultados en un nuevo
vector v que será de dimensión igual al número de renglones actuales en la matriz
W (j). El vector v queda entonces definido por la siguiente ecuación:
48
Ec. 4.5
4. Cada proceso trabajador tiene ahora un nuevo vector v[j]. En este vector se
realiza una comparación entre cada uno de los elementos que los conforman para
obtener el máximo valor junto con su posición y enviarlos al proceso
administrador (proceso 0) que está en espera de datos. Esto lo hace cada proceso
trabajador.
5.
El proceso 0 que está en espera de información de los procesos productores,
establece un intervalo de tiempo para recibir información, por lo que solo
alcanzarán a pasar X número de datos de los n-1 procesos, y los X datos son
almacenando en dos vectores de tamaño X, un vector para almacenar valores y
otro para almacenar la posición correspondiente, como se muestra en la figura
4.4.
Posición
0
1
Max. Val proc1
Posición proc 1
Max. Val proc 2
Posicion proc. 2
2
.
.
.
X
Max. val proc. x
Posición proc x
Fig. 4.4 Vectores de dimensionalidad X con valores mayores y posición correspondiente.
6. El proceso 0 ahora tiene dos vectores. Uno con los valores mayores de cada
proceso y otro con su posición correspondiente. Ya recibidos los datos, éste
proceso se da a la tarea de comparar cada uno de los valores recibidos entre ellos
para obtener un nuevo valor mayor (general) con su posición correspondiente,
obteniendo un vector I ganador. Para determinar el grado de similitud del vector
ganador I con cualquiera de las categorías aprendidas en W, este es nuevamente
comparado con un valor llamado RO (Ec 3).
49
>= RO Ec. 4.6
7. Si la comparación resulta verdadero, entonces, se envía la orden a todos los
procesos trabajadores de incrementar el número de vectores (j+1) para incorporar
el nuevo vector I ganador a W. Este vector ganador se envía a través de mensajes
al comunicador formado por los procesos trabajadores para que todos actualicen
la matriz y exista consistencia de datos. Para esto se utiliza una barrera de
sincronización. La barrera de sincronización elimina las condiciones de
competencia que pudieran existir entre los múltiples procesos, pues cada uno de
ellos detendrá su ejecución hasta que todos los procesos alcancen el punto de
ejecución en el que está indicada la barrera. La figura 4.4 muestra la forma en
que los procesos coordinan sus entradas a la sección crítica.
8. En caso de no cumplir con la Ec 4.6, el proceso 0 deberá difundir hacia todos los
procesos trabajadores la posición del vector I en la matriz sobre la cual se
realizará la actualización en base a la
Ec 4.7 esto lo hará cada proceso
trabajador. De igual manera existe una barrera de sincronización para bloquear a
los procesos y no se les permite continuar hasta que todos los procesos tengan la
matriz actualizada.
I
1
Ec. 4.7
9. Cuando un proceso trabajador logre incorporar o actualizar (punto 7 u 8) regresa
a la lectura de otro vector de la BD y ejecuta los pasos a partir de la
normalización del vector, esto se repite mientras no exista fin de archivo.
La estructura de la implementación del multiprocesamiento con el esquema anteriormente
descrito puede representarse mediante el siguiente diagrama de tiempo (fig. 4.5), que
indica el estado de cada uno de los procesos durante las etapas de sincronización,
ejecución de código y comunicación. El diagrama muestra un ciclo completo de
50
sincronización y este se repite a lo largo de la ejecución de todo el programa mientras no
exista fin de archivo de la base de datos.
Envia valor mayor
Broadcast ganador
Proceso 0
Proceso 1
Proceso 2
ejecutando
en espera
Tiempo
Despierta proc. 0
Calcula W*I
Despiertan procs.
trabajadores
Evaluación
Actualiza o
Incorpora
Fig. 4.5 Un ciclo completo de entrenamiento de la red ART2A en paralelo
DESCRIPCIÓN DEL DIAGRAMA.
En este diagrama de la fig. 4.5 se muestran tres procesos durante un ciclo de ejecución
del entrenamiento de la red neuronal descrita anteriormente. La división de los procesos
se realizó de acuerdo a la descripción en la sección 4.2. Cuando se inicia la ejecución del
programa, el proceso administrador (maestro), pasa inmediatamente a la etapa de espera
de resultados que le serán enviados de los procesos trabajadores (procesos 1 y 2). Nótese
que este proceso maestro solamente realiza tareas de coordinación para el resto de los
procesos. Los procesos 1 y 2 que se encuentran en la etapa de ejecución, realizando
51
cálculos relacionados al entrenamiento de la red neuronal ART2A, que pueden
describirse de la siguiente manera:
(1). Toman los datos a ser evaluados en un vector U[N],
(2). Cada proceso normaliza su vector de entrada con base a la norma Euclidiana
obteniendo un vector normal I[N].
(3). Cada uno de los vectores que conforman la matriz Wij es multiplicado por el
vector I[N] de cada proceso Vj=
, donde j es el número de
vectores actuales en W y se obtiene un nuevo vector V[j],
(4). El vector V[j] hace una comparación de cada uno de los elementos que lo
conforman y toma el valor mayor junto con su posición.
(5). Los valores correspondientes al mayor valor y posición le son enviados al
proceso 0, (esto lo realiza cada uno de los procesos trabajadores 1 y 2) y se pasa
a la etapa de espera.
El proceso 0, que se encuentra en la etapa de espera, recibe datos y pasa a la etapa de
ejecución. Este proceso hará nuevamente una evaluación (global) de los datos recibidos
de los procesos trabajadores y toma el valor mayor junto con la posición correspondiente
(para recibir los datos el proceso 0 establece un tiempo, por lo que solo alcanzan a
entregar datos solo un x número de procesos). Una vez obtenido el valor mayor lo
compara con el valor de vigilancia RO (Ec. 3) que es el que determina el grado mínimo
de similitud del vector ganador I con vectores aprendidos en W. El proceso 0 informa a
los procesos trabajadores el resultado de comparación mediante una operación Broadcast,
que los restaura nuevamente al estado de ejecución. Con esto los procesos incrementan el
valor j para incorporar al vector I o bien lo actualizan en la matriz W. Una vez que envía
datos a procesos trabajadores, el proceso 0 pasa nuevamente a la etapa de espera y los
procesos trabajadores pasan a la etapa de ejecución, comunicándose entre ellos el vector
I ganador junto con su posición para incorporar o actualizar.
Esto da la descripción de un ciclo completo de entrenamiento de la red neuronal
ejecutándose en múltiples procesadores. La implementación realizada en código admite
un número arbitrario de procesos trabajadores. Aunque la descripción del mecanismo de
52
paralelización dada en los párrafos anteriores involucra por cuestiones de simplicidad,
solamente dos procesos trabajadores.
4.4 Caso 2: “IMPLEMENTACIÓN EN SISTEMAS MULTICORE”
Para hacer uso de la tecnología multicore, se parte de un código secuencial. Para llevar a
cabo la paralelización sólo se identificó la región óptima a ser paralelizada, tomando en
cuenta la dependencia de datos. La región ideal a paralelizar es la multiplicación del
vector normalizado I por cada uno de los elementos de los vectores que conforman W. En
este tipo de arquitecturas, la programación eficiente es mediante el uso de múltiples hilos
de manera que se pueda acelerar el procesamiento de multiplicación matricial. La
aceleración se encuentra cuando en W el número de vectores actuales aprendidos (j) es
grande, ya que como se puede observar en la fig. 4.6, I[N] multiplica a cada uno de los
vectores en W paralelamente, por lo que se puede considerar un incremento en
procesamiento de un 50%. Estos datos los analizaremos en el capítulo 5.
HILOS
I
1
2
I
Matriz W
1
=
2
3
I*W1
1
N
=
2
3
1
2
3
N
3
N
1M
2
3
MN
N
=
I*W2
I*Wj
Fig. 4.6 Hilos en acción.
53
Se puede apreciar una bifurcación como la que se muestra en la fig. 4.7 donde el proceso
comienza con un hilo inicial y al encontrar la instrucción para paralelizar, este crea un
grupo de hilos esclavos para ejecutar un ciclo for dentro del proceso.
Región paralela
Hilo maestro
Hilo maestro
Fig. 4.7 Región paralela con hilos
Durante la etapa de ejecución se realizaron ejecuciones con múltiples hilos, mediante el
cual se determinó como caso ideal que el número de hilos empleados en la ejecución
debe ser igual al número de procesadores (núcleos) existentes. Esto es debido a que el
crear un hilo toma una cantidad finita de tiempo al procesador. Por otra parte, si se crean
varios hilos para paralelizar, hay que recordar que el sistema operativo, solo le da un
cierto tiempo de ejecución a cada hilo. Al terminar el tiempo de ejecución asignado, si el
hilo ha terminado o no, el control le es quitado para pasar al siguiente hilo en espera. Si el
hilo no ha terminado de realizar la tarea, el sistema operativo salva los registros del hilo,
para pasar al siguiente que le toque ejecutar.
El siguiente fragmento de código muestra el uso de algunas directivas de OpenMP:
El compilador al encontrar la directiva #pragma omp parallel for crea una región paralela.
La región a paralelizar debe estar entre llaves {} (que indican inicio y fin).
La directiva numproc=omp_get_num_procs(); obtiene el numero de procesadores.
Retorna en la variable entera numproc un número entero. Esta función es importante
dentro de nuestra aplicación, ya que se ha recomendado manejar hilos de acuerdo al
número de procesadores. Posteriormente la variable numproc se utiliza en otra de las
funciones para especificar el número de hilos empleados en la región paralela en la
siguiente función: omp_set_num_threads(numproc). Otra forma de especificar el número
de hilos, es poner directamente el número entero de hilos a manejar.
54
void multiplicaWI(double **W,double *I, int m,int N, double *v)
int numproc=omp_get_num_procs();
{
int i,j;
double sumWI;
omp_set_num_threads(numproc);
#pragma omp parallel for
for(i=0; i<m; i++)
{
sumWI=0;
for(j=0; j<N; j++)
sumWI+=W[i][j]*I[j];
v[i]=sumWI;
}
}
Como se puede observar, las directivas OpenMP permiten implementar mecanismos de
paralelismo y sincronización en una forma muy transparente con respecto al código
secuencial. Cada llamada en OpenMP equivale a una secuencia de llamadas a APIs del
sistema operativo que crean, sincronizan y destruyen a los objetos necesarios para la
creación del código objeto correspondiente. Liberándonos de implementar estas tareas en
el código.
Una representación de flujo de ejecución es la figura 4.8. En la figura (a) se observa
como la ejecución de un proceso comienza con un hilo inicial. Al encontrar la directiva
#pragma omp parallel for
de OpenMP automáticamente crea hilos, los cuales se
ejecutaran de manera paralela. En la fig. (b) se observa un mayor número de hilos. Si
dentro de los sistemas multicore, solo se cuenta con 2 núcleos, éstos se ejecutarán de
manera concurrente, ocurriendo intercambio de contexto, y un mayor consumo de tiempo
55
que le será tomado al procesador al crearlos. Aunque para el usuario esto se realice de
manera transparente.
Fig. 4.8 Flujo de ejecución de hilos
HILO 3
HILO 1
HILO 1
HILO 1
HILO 1
HILO 2
HILO 1
HILO 4
HILO 2
a). Flujo de ejecución de hilos de
manera paralela.
b). Flujo de ejecución de hilos de
manera concurrente
4.5 Caso 3: “IMPLEMENTACIÓN EN SERIE”
En este caso, se llevó a cabo la ejecución del programa de entrenamiento de la red
neuronal de manera secuencial (con los pasos descritos anterior mente).
Como se
muestra en la siguiente figura. Donde K es un apuntador al inicio de la Base de Datos
(BD), Kmax es el límite en la BD.
56
Figura 4.9 Diagrama de flujo de la red ART2A de manera secuencial
Inicio
1
W, BD, β, α, ρ
¿j = 0?
Si
mayor = v[j]
i:=0
K:=0
¿i < j?
i++
2
si
¿K<Kmax?
¿v[i] > v[i+1]?
mayor:= v[i]
no
Lee U de la BD
mayor = v[i+1]
“Normaliza U”
si
¿ρ >=mayor?
I=
i:=0
¿i< j?
j=j+1
i++
IAW
sumWI := 0
=β(I*
i:=0
) + (1-β)
¿i<N?
i++
sumWI: = sumWI+W[i,j]*I[i]
v[i]: = sumWI
K:= K +1
2
1
57
Para obtener el tiempo de ejecución se emplearon las funciones de tiempo de C. Esto es
con el fin de saber cuánto tiempo consume en su ejecución y con ello poder probar qué
tan conveniente o hasta que punto genera buenos resultados hacer uso de la programación
paralela. El programa serial ocupa menos espacio en memoria al necesitar menos
instrucciones, ya que se omite el código correspondiente a las pragmas y mecanismos de
comunicación y sincronización requeridos en las versiones paralelas. Así mismo se
observar que en cuanto crece el número de datos a procesar tales como: dimensión (N), el
número de renglones aprendidos en W (j) y el número de registros en la base de datos
(Kmax), el tiempo de ejecución es cada vez mayor.
4.6. RESUMEN DE CAPÍTULO:
Durante el desarrollo de este capítulo se presenta la implementación de las arquitecturas
para realizar cómputo paralelo (arquitectura multicore y sistemas distribuidos). Para el
uso de estas dos arquitecturas se
presenta un programa híbrido mediante la
implementación de funciones y pragmas de MPI y OpenMP, en el que se emplean
mecanismos típicos de la programación paralela tales como: secciones críticas y
comunicación de datos. Es muy importante tener presente que en estas dos arquitecturas
no solo se comunican de manera diferente, sino también existen grandes diferencias
durante la programación. Por lo que es necesario conocer tanto sus características como
las limitaciones en cada una de ellas, esto con la finalidad de obtener un uso adecuado y
un mejor rendimiento de la ejecución de aplicaciones en arquitecturas paralelas con
implementación híbrida.
Para la implementación se establece un caso de estudio que permite determinar regiones
críticas en el acceso a un recurso compartido. Se emplean mecanismos de sincronización
58
y comunicación en el acceso que nos impone restricciones en tiempo necesarias que
permite visualizar potenciales condiciones de competencia entre procesos e hilos.
59
CAPÍTULO 5
5.1
MÉTRICAS Y ANÁLISIS DE RESULTADOS
INTRODUCCIÓN AL CAPITULO.
En este capítulo se analizan resultados de la ejecución del entrenamiento de la red
neuronal ART2A en multiprocesadores. Se analizan resultados de los casos descritos en
el capítulo anterior, mediante lo cual se puede apreciar el rendimiento conforme al
número de procesadores participantes en la aplicación. A través de las gráficas y análisis
de resultados de la ejecución del algoritmo en 2 máquinas con arquitectura multicore (4
procesadores) se observa un incremento en la aceleración de ejecución con respecto a su
versión secuencial. Debido a no contar con equipo con características similares es que no
se pudo obtener datos conforme al número de procesadores el cual determina que:
agregar más procesadores para la ejecución del programa no garantiza una aceleración en
tiempo de ejecución. Esto es debido a la parte secuencial del programa que solo tiene que
ser ejecutada por un solo procesador independientemente del número de procesadores
que se tenga para su ejecución (Ley de Amdahl) [11].
5.2
Aplicación de Métricas de desempeño en sistemas
multicore.
Sobre un sistema multiprocesador, un problema puede ser divido en un número arbitrario
de tareas independientes para un mejor desempeño con respecto al mismo en su versión
secuencial. Un sistema con 2 procesadores puede ejecutar 1.95 veces más rápido que un
único procesador, un sistema con 3 procesadores 2.9 veces más rápido, un sistema con 4
procesadores 3.8 veces más rápido y así sucesivamente [5]. Sin embargo la aceleración
casi siempre cae conforme el número de procesadores se incremente [22]. Esto es debido
a la creación de hilos, al intercambio de contexto, la
comunicación y mecanismos de
60
sincronización que deben ser tomados en cuenta al desarrollar aplicaciones que requieran
la participación de más de 2 procesadores.
La tabla 1 muestra datos de la ejecución del programa de entrenamiento de la red
neuronal ART2A en 2 procesadores con varios hilos.
Los datos de entrenamiento son los siguientes:
N=100;
BD=10000;
W=100000;
RO=0.4.
Donde:
N
Es la dimensión del vector.
BD
Número de registro en la base de datos.
W
Es el máximo número de vectores que se pueden incorporar a W.
RO
Es el grado mínimo de similitud entre el vector de entrada I y cualquier categoría
definida en W.
El tiempo de ejecución para el entrenamiento de la red de manera serial es: 20.32800 seg.
Este programa solo se ejecuta en un único procesador.
Para que este mismo programa sea ejecutado de manera paralela, se hace la
implementación de los pragmas de OpenMP, en el que se observa mejoras en tiempo con
respecto a la versión secuencial.
Número de hilos
Tiempo seg.
Tamaño W
2
12.32800
9163
3
14.73400
9163
4
12.79600
9163
61
5
13.89000
9163
6
12.85900
9163
7
13.35900
9163
8
12.87500
9163
9
13.25000
9163
10
12.93700
9163
.
.
.
.
.
.
.
.
.
10,000
14.06200
9163
100,000
14.10900
9163
1,000,000,000,000
20.32800
9163
Tabla 5.1 Resultados de entrenamiento de la red neuronal ART2A con varios hilos para
un valor de RO=0.4
Como se puede observar, el manejo de múltiples hilos en la ejecución de un programa
presenta mejoras en tiempo comparado con la versión secuencial. Sin embargo, llega un
punto en el que crear varios de éstos ya no es conveniente, por el contrario el incremento
en tiempo es notorio, esto es debido a los mecanismos de creación y sincronización de
hilos.
Se han realizado varias ejecuciones de este mismo programa con variaciones en las
variables antes mencionadas, pudiéndose observar que la variable RO presenta un gran
62
impacto en el recurso compartido W. Esta variable es el factor de semejanza del vector a
ser estudiado comparado con los vectores clasificados en W, por lo que se pude
determinar que a valores mayores (para RO) la red permite mayor clasificación, por el
contrario, a valores pequeños se realiza una clasificación más selectiva. Por lo que el
crecimiento de W está en función de dicha variable.
Considerando que la región a ser paralelizada es la matriz W es conveniente tener
dimensiones grandes “tanto para N como para M” para que valga la pena la creación de
hilos. El crear y sincronizar hilos le toma una cantidad finita de tiempo al procesador,
por lo que si se tiene dimensiones pequeñas en la matriz solo se hará mayor consumo en
tiempo en la creación de éstos que en el proceso de entrenamiento de la red.
La tabla 2 muestra datos de la ejecución del programa con una variación en el valor de
RO=0.1. Los demás datos con el mismo valor.
El tiempo de consumo en su ejecución secuencial es: 0.156000 seg.
Número de hilos
Tiempo seg.
Tamaño W
2
0.359000
61
3
0.312000
61
4
0.343000
61
5
0.390000
61
6
0.437000
61
7
0.546000
61
8
0.562000
61
9
0.625000
61
63
10
0.656000
61
.
.
61
.
.
.
.
.
.
100
1.937000
61
200
1.968000
61
300
1.953000
61
400
2.015000
61
500
1.984000
61
1000
2.000000
61
2000
2.000000
61
3000
2.000000
61
.
.
.
.
.
.
.
.
.
10,000
1.98000
61
100,000
2.000000
61
1,000,000
2
61
64
10,000,000
2
61
100,000,000
2
61
1000,000,000
2
61
Tabla 5.2 Resultados de entrenamiento de la red neuronal ART2A con varios hilos para
un valor de RO=0.1
En estos datos se puede observar que el tiempo de ejecución de manera secuencial es
menor que el tiempo consumido en su versión paralela. Con esto se puede demostrar la
importancia que tiene la dimensión de los datos (N y M en este caso) en arquitecturas
paralelas.
Los resultados de ejecución de entrenamiento de la red neuronal haciendo uso de ambas
arquitecturas (sistemas de memoria compartida y sistemas de memoria distribuida) se
muestran a continuación:
N=100;
BD=100;
RO=0.1;
W=100;
El tiempo en su versión secuencial es: 0.93000 seg.
Este mismo programa en la versión paralela (solo haciendo uso de la arquitectura
multicore) es: 0.7800 seg. Con un Sc=1.1923 y E=0.5961
65
Para 3 y 4 procesadores, los resultados son los siguientes:
Procesadores
Tiempo
Aceleración
Eficiencia
RO=0.1
RO=0.4
seg.
(Sc)
(E)
3
0.68700
1.3537
0.5961
23
99
4
.421000
2.2090
0.5522
23
99
Tabla 5.3 Resultados de entrenamiento de la red neuronal ART2A para 3 y 4
procesadores.
Se puede observar un tiempo de ejecución mejor que la versión secuencial. Sin embargo,
al igual que el manejo de hilos el crear varios procesos para su ejecución de
entrenamiento no resulta conveniente. Esto se observa en los resultados de la siguiente
tabla (5.4). Para la ejecución con más de cuatro procesos el procesador comienza a
ejecutar cada proceso de manera concurrente, por lo que al compartir el tiempo del
procesador en cada una de las tareas y teniendo un recurso compartido no resulta
conveniente.
Para determinar el speed-up (aceleración) y la eficiencia (E) se hizo uso de la Ec. 2.1 y
2.2 respectivamente del capítulo 2 (sección 2.6), en
el que se procedió a ejecutar el
algoritmo de entrenamiento de la red neuronal ART2A de manera secuencial (en un solo
procesador) y el mismo programa en su versión paralela (en 2, 3 y 4 procesadores).
Gráficamente la aceleración se representa en la siguiente figura.
66
4
2
Sc
1
1
2
3
4 procesadores
Fig. 5.1 Aceleración de entrenamiento de ART2A en 4 procesadores.
Como se puede observar, el incremento en la aceleración es de manera lineal para 4
procesadores, obteniendo de igual manera una buena eficiencia. Pero como un programa
paralelo requiere comunicación entre procesadores, parte del tiempo de los procesos es
consumido en esta actividad, por lo que aumentar procesadores, nos lleva a valores de
eficiencia menores a 1.
Durante la prueba de entrenamiento se hicieron varias ejecuciones en el programa híbrido
en el que se fue incrementando el número de procesos (Tabla 5.4). Se puedo observar que
el tiempo de ejecución aumenta conforme aumentaba el número de procesos, esto es
debido a que los procesos se comienzan a ejecutar de manera concurrente, y debido a
que comparten un mismo recurso, esto también se ve reflejado en el tiempo que tarda un
proceso en espera del acceso a este.
Procesadores
Tiempo
Aceleración
Eficiencia
RO=0.1
RO=0.4
seg.
(Sc)
(E)
3
0.68700
1.3537
0.5961
23
99
4
.421000
2.2090
0.5522
23
99
5
.47500
1.9578
.3915
23
99
6
.75600
1.2301
.2050
23
99
67
7
.76900
1.2813
.1830
23
99
8
.92700
1.003
.1253
23
99
9
.94200
.9872
.1096
23
99
10
.97000
.9587
.0958
23
99
Tabla 5.4 Tiempo de ejecución para 10 procesos en 4 procesadores.
El diagrama 5.2 representa a 3 procesos en el que se da el tiempo de ejecución en cada
una de las etapas:
PRIMERA ETAPA (CALCULA W*I)
Se calcula el tiempo que tarda cada uno de los procesos trabajadores desde el momento
en que toma de la base de datos el fenómeno a ser estudiado, lo normaliza en base a la
norma Euclidiana obteniendo resultados en un segundo vector I[N]. Este vector
normalizado I multiplica a cada uno de los vectores contenidos en Wij, por lo que hace
una suma acumulada y el resultado lo almacena en un vector V cuya dimensión es igual
al número de vectores contenidos en W, este vector V[j] hace una comparación de cada
uno de sus elementos y elige el valor mayor con su respectiva posición. Cada uno de los
procesos obtiene su valor mayor y lo envía al proceso administrador.
SEGUNDA ETAPA (EVALUACIÓN):
El proceso establece un intervalo de tiempo para recibir información referente al dato
mayor y su respectiva posición de los procesos trabajadores, por lo que solo alcanza a
recibir X número de valores y los almacena en dos vectores, uno con valor y otro con su
posición correspondiente. Este proceso administrador hace una evaluación general y
obtiene un valor mayor general nuevamente con su posición correspondiente, el valor
68
ganador lo compara con el valor de RO para determinar el grado de similitud del vector
ganador con las categorías aprendidas en W, una vez determinado esto, este proceso
envía en mensaje para que los procesos trabajadores incorporen el vector ganador I a W o
se actualice I (explicado en capitulo 4).
TERCERA ETAPA (ACTUALIZA O INCORPORA)
En esta etapa, los procesos trabajadores que están agrupados en un comunicador
exclusivo se comunican el vector ganador I ya sea para incorporar o actualizar, esto fue
determinado por el proceso de comparación (segunda etapa).
Cada uno de estos tiempos está representado en el siguiente diagrama.
5.2 Tiempos de ciclo de ejecución del entrenamiento de la ART2A
Envía valor mayor
Broadcast ganador
Proceso 0
Proceso 1
Proceso 2
Ejecutand
o
en espera
Despierta proc. 0
Calcula
m
W*I
Despiertan
trabajadores
Evaluación
procs.
Actualiza o
Incorpora
Tiempo consumido por el proceso 1 en las etapas de:
multiplicación 0.249000 seg.
Actualización
Dando un tiempo total de ejecución para el proceso 1 de:
1.236000 seg.
1.485000 seg
69
Tiempo consumido por el proceso 2 en las etapas de:
multiplicación 0.232000 seg.
Actualización
Dando un tiempo total de ejecución para el proceso 2 de:
El tiempo total de los procesos es:
1.252000 seg.
1.484000 seg
1.485000 seg + 1.484000 seg = 2.969 seg.
El tiempo consumido por el proceso 0 en la etapa de evaluación es:
2.078000 seg.
El tiempo consumido en comunicación entre procesadores es: 53 nanoseg.
Por tanto, el tiempo total consumido para la ejecución del programa es:
Tiempo Total = Tiempo Secuencial + Tiempo Paralelo
Tiempo Total = 2.078000seg + 2.969 seg = 5.047seg
5.3 RESUMEN DE CAPÍTULO
En este capítulo se analizaron los resultados de ejecución del entrenamiento de la red
neuronal en 1,2,3 Y 4 procesadores obteniendo resultados alentadores en la arquitectura
híbrida y la ejecución en plataforma multicore con respecto al tiempo de ejecución en su
versión secuencial,. Además de obtener el tiempo de ejecución en cada una de las etapas.
Por lo que se puede concluir que el programa en su versión paralela obtiene buenos
resultados debido a la división de los procesos para que no exista comunicación extra. Sin
embargo se observa que conforme se aumenta el número de procesos, la comunicación y
sincronización aumenta el tiempo de ejecución, por lo que es importante a la hora de
programar tener en cuenta la división y participación de los procesadores en los procesos
participantes.
70
Además en este capítulo se pudo observar que el desempeño de la red neuronal es bueno,
ya que durante la etapa de ejecución se pudo observar que la red se encuentra
aprendiendo,
es decir,
que realiza la clasificación de acuerdo a resultados de
comparación del valor de similitud por lo que determina si incorpora o actualiza. Para la
variable RO (ρ), se manejaron 2 valores en el que se pudo observar que conforme al valor
sea más pequeño, este realiza una clasificación más selectiva, por lo que los renglones
(vectores) aprendidos en W incrementaran de acuerdo a esta variable. En cuanto al
desempeño del entrenamiento de la red neuronal encontramos que el parámetro de
aprendizaje Beta (β) fuee el que determinaba, como era de esperarse, la velocidad del
crecimiento de la matriz de pesos W durante las iteraciones del programa, mientras que la
selectividad se encuentra gobernada por los valores de RO, por lo que consideramos que
el esquema de paralelización aquí propuesto constituye una implementación correcta del
algoritmo ART2A.
71
CAPÍTULO 6
CONCLUSIONES.
El desarrollo de este trabajo se realizó en el estudio de los sistemas multicore en conjunto
con los sistemas de alto rendimiento (Sistemas Distribuidos). Se llevó a cabo la
implementación de ambas arquitecturas paralelas en el que se realizó un programa
híbrido haciendo uso de las interfaces de programación en este tipo de arquitecturas MPI
y OpenMP. La herramienta utilizada para hacer trabajar las máquinas como un clúster es
MPI y se emplea OpenMP para agilizar la solución del proceso en cada uno de los cores
existentes en cada máquina. 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 múltiples hilos paralelos mediante el uso de dos núcleos, la
plataforma del sistema operativo utilizado es Windows XP de 32 bits.
Con la finalidad de analizar el comportamiento de múltiples procesadores en la ejecución
de una misma aplicación, se tomó como caso de estudio el entrenamiento de la red
neuronal ART2A en el que se tiene un recurso compartido por los diferentes
procesadores participantes en la aplicación, el cual permite determinar regiones críticas
en el acceso al dicho recurso que impone las restricciones en tiempo necesarias y permite
visualizar potenciales condiciones de competencia. Una de las ventajas en este tipo de
arquitecturas el contar con herramientas como MPI y OpenMP que permiten manejar de
manera transparente estos mecanismos, por lo que solo hay que preocuparse de la
implementación adecuada de las directivas para llevar a cabo una buena distribución de
las aplicaciones y con ello determinar el mejor algoritmo paralelo en que el tiempo
consumido sea en cálculos de proceso y no en comunicación y mecanismos de
sincronización en el que los procesos puedan estar inactivos por mucho tiempo.
Durante la ejecución del algoritmo de entrenamiento de la red neuronal se observaron
resultados óptimos en tiempo con respecto a su versión secuencial, el programa se
72
ejecutó en cuatro procesadores, debido a no contar con más equipo con características
similares es que no se pudo obtener resultados que demuestren el punto en el cual
agregar más procesadores no resulta conveniente, esto es debido a la parte secuencial del
programa propuesto por la Ley de Amdahl. Por otra parte, para mostrar que el tener
múltiples procesos activos en la ejecución de un programa tampoco es garantía en cuanto
a tiempo de ejecución en el problema, esto se determinó en base a resultados obtenidos a
través de múltiples ejecuciones del programa con diferentes números de procesos,
observándose que conforme aumenta el número de procesos incrementa también el
tiempo de ejecución, esto es debido al tiempo de comunicación que consumen procesos
para intercambiar resultados, así como también el tiempo que esperan los procesos para
accesar al recurso compartido debido a las barreras de sincronización para evitar
inconsistencia de datos.
En cuanto a la aplicación del algoritmo solo en sistemas multicore se pudo observar que
el tiempo de ejecución disminuye casi a un 50%, y con ello obteniendo un grado de
eficiencia de un 90% , por lo que se recomienda ampliamente el uso de estos sistemas y
teniendo en mente que el crear hilos así como el intercambio de contexto le toma una
cierta cantidad de tiempo al procesador, por lo que a la hora de programar se recomienda
ampliamente se utilicen hilos conforme al número de procesadores disponibles en la
máquina y asegurar una ejecución paralela.
Por tanto, dados los resultados obtenidos se recomienda ampliamente el uso de la
tecnología híbrida en sistemas de control, siempre y cuando se tenga en cuenta el rango
de valores para el cual se aprovecha el paralelismo introducido en el modelo presentado.
Un vector cuya dimensionalidad es grande (N) es conveniente para la utilización de la
plataforma paralela, además de ser altamente confiable, ya que los sistemas de
clasificación suelen trabajar con una gran cantidad de características en los patrones, y
tales características suelen ser de una dimensionalidad alta por lo que en total se maneja
un número N de variables grandes.
73
APÉNDICE.
CÓDIGO HÍBRIDO UTILIZANDO LAS INTERFACES DE PROGRAMACIÓN MPI Y
OpenMP
//Librerias utilizadas por el programa
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include "mpi.h"
#include <omp.h>
#include <dos.h>
//Variables globales reconocidas por todas las funciones
const int N=1000;
//dimension de los vectores con que se trabaja
const int mMax=1000;
//máximo de vectores que se pueden incorporar a la matriz W
const double RO=0.1;
//parámetro de vigilancia, es el grado mínimo de similitud
//entre el vector de entrada I y cualquier
//categoría definida en W
const double ALFA=0.3;
int numproc=omp_get_num_procs();
//Es un pragma de OpenMP que obtiene el número de
procesadores en la PC
const long kMax=1000;
en la base de datos
//número de registros que por ahora tomamos como máximo
int procMax=300;
//Procesos máximos que pueden manejarse
//Prototipos de las funciones utilizadas
void leerU(double * u,int N, int j);
74
void normalizaU(double * I,double * U,int N);
void muestradatos(double * U,int N);
void inicia_matriz(double**, int n, int *m);
void multiplicaWI(double **W,double *I, int m,int N, double *v);
double mayor(double *v, int M, int *pos);
void incorporaW(double **W, double *I, int *m, int N);
void actualizaW(double **W, double *I,int N,int j);
//Funcion principal
void main(int argc, char *argv[])
{
//variables para toma de mediciones de tiempos de procesamiento y comunicacion
time_t hra1,hra2;
hra1=time(NULL);
clock_t inicio_cpu = clock();
int namelen,myid, numprocs;
//variables para MPI
char processor_name[MPI_MAX_PROCESSOR_NAME];
MPI_Init(&argc,&argv);
//inicia el ambiente de MPI
MPI_Comm_size(MPI_COMM_WORLD,&numprocs);
con el núm. de procesos
MPI_Comm_rank(MPI_COMM_WORLD,&myid);
//Comunicador universal en MPI
//Identificador del proceso.
MPI_Get_processor_name(processor_name,&namelen);
printf("Algoritmo ART2a consumidor 0 dirige a procesos trabajadores \n");
if (numprocs<2 || numprocs>procMax){
printf("Se necesita al menos 2 procesos en este esquema de paralelizacion\n");
printf("el limite máximo son %d procesos\n",procMax);
printf("Programa Terminado. Adios!!!\n");
MPI_Abort(MPI_COMM_WORLD, 1);
}
75
MPI_Comm comMaTrab;
//comunicador dividido en procs master y trabajadores
int color;
//color para dividir los procesos
//divide el comunicador. (Si
master, id>0 para trabajadores,
el identificador)
id=0 para
if (myid>0) color=1; else color=0;
MPI_Comm_split(MPI_COMM_WORLD, color, myid, &comMaTrab);
//Se crea un
comunicador para dividir al proceso //maestro (administrador)
de los procesos trabajadores.
MPI_Status status;
//estado de recepción de comunicación
int i;
//variable para ciclos
int pos=0;
//posición del mayor valor del vector resultante de la
//multiplicación W[i]*I en los procesos trabajadores
int k=0;
//k es el número de registro de la base de datos
//en una base de datos verdadera k puede omitirse y
un ciclo while(!eof)
double *u;
//apuntador al inicio del vector leído en la BD
double *v;
//apuntador al inicio del vector resultante de W[i]*I
double **W;
//Apuntador al inicio de apuntadores al inicio de los
vectores
//Indica el inicio de los datos correspondientes a la
matriz de pesos
int m=0;
//m es el número de renglones ocupados en la matriz
double *I;
//Apuntador al inicio del vector Normalizado
W
int ganador;
procedimiento
//id del proceso ganador en cada iteración del
clock_t inicio_evaluacion;
clock_t inicio_multiplicacion;
clock_t inicio_actualizacion;
clock_t evalcont=0;
76
clock_t multcont=0;
clock_t actcont=0;
W=new double*[mMax];
inicia_matriz(W,N, &m);
if (myid==0){
//SOY EL PROCESO CONSUMIDOR QUE RECOGE DATOS DE LOS PROCESOS
PRODUCTORES 1 Y 2
int *Posiciones;
//buffers para recibir las posiciones y
double *Valores;
//valores de los mayores resultados de WI=V
Valores=new double[procMax];
Posiciones= new int[procMax];
//printf("Soy Proceso Master\n");
while(k<kMax){
//después cambiar por un indicador leído de los
procesos
//printf("Espero Recibir\n");
//Recibir los mayores valores y posiciones de cada proceso
for (i=0;i<(numprocs-1);i++){
MPI_Recv((void
//El proceso 0 recibe valores de los
*)(Valores+i),1,MPI_DOUBLE,i+1,0,
//procesos trabajadores.
MPI_COMM_WORLD,&status);
MPI_Recv((void *)(Posiciones+i),1,MPI_INT,i+1,0,
// El proceso 0 recibe posic
MPI_COMM_WORLD,&status);
}
//printf("Ya Recibi\n");
//
{printf("Valores a evaluar en el proceso 0:");
77
//
muestradatos(Valores,numprocs-1);
//
}
/*empieza a medir el tiempo de evaluación*/
inicio_evaluacion = clock();
if (RO>=mayor(Valores,numprocs,&ganador)){
//posición a actualizar, si es igual que el 'm' de los procesos
//trabajadores, entonces se incorpora un nuevo vector
Posiciones[ganador]=m;
//aumenta el valor de m para indicar que habrá un nuevo
renglón
m++;
}
//
{
//
printf("Índice ganador %i:",ganador);
//
}
//envía la posición del vector a modificar del proceso ganador
//el id del proceso ganador es = ganador + 1
//++ganador;
for (i=0;i<(numprocs-1);i++){
MPI_Send((void *)&Posiciones[ganador],1,MPI_INT,i+1,
0,MPI_COMM_WORLD);
MPI_Send((void *)&ganador,1,MPI_INT,i+1,
0,MPI_COMM_WORLD);
}
k++;
/*termina de medir el tiempo de evaluación*/
evalcont+= clock()-inicio_evaluacion;
//
if(k%100==0)printf("Llevo %i iteraciones \n",k);
}
78
}else {
//
SOY ALGUNO DE LOS PROCESOS PRODUCTORES
v=new double[mMax];
u=new double[N];
I=new double[N];
double val;
//Inicia la semilla rand con valores distintos en cada proceso
srand((unsigned int)myid);
//printf("\nSoy un proceso trabajador\n");
while(k<kMax){
//después cambiar por while(!eof)
/*empieza a medir el tiempo de multiplicación*/
inicio_multiplicacion = clock();
leerU(u,N,k);
//
printf("U:");muestradatos(u,N);
normalizaU(I,u,N);
//#pragma omp critical
//
{sleep(myid); printf("I:");muestradatos(I,N);}
multiplicaWI(W,I,m,N,v);
//
%i:",myid);muestradatos(v,m);}
{printf("V=W*I
en
el
proceso
val=mayor(v,m,&pos);
//envía el valor y la posición donde se encontró el mayor valor
MPI_Send((void
*)&val,1,MPI_DOUBLE,0,0,MPI_COMM_WORLD);
MPI_Send((void *)&pos,1,MPI_INT,0,0,MPI_COMM_WORLD);
/*Termina de medir el tiempo de multiplicación*/
79
multcont+= clock()-inicio_multiplicacion;
/*Empieza a medir el tiempo de actualización*/
inicio_actualizacion = clock();
//recibe el id del proceso ganador y la posición a modificar
MPI_Recv((void *)&pos,1,MPI_INT,0,MPI_ANY_TAG,
MPI_COMM_WORLD,&status);
MPI_Recv((void *)&ganador,1,MPI_INT,0,MPI_ANY_TAG,
MPI_COMM_WORLD,&status);
//este punto es una barrera de sincronización para todos los
//procesos trabajadores.
MPI_Bcast((void *)I,N,MPI_DOUBLE,0,comMaTrab);
//Hasta aquí todos tienen el mismo vector I del proceso ganador
if (pos==m){
//Todos incorporan a I en su copia de W
incorporaW(W,I,&m,N);
}else{
//Todos actualizan la posición pos en us copia de W
actualizaW(W,I,N,pos);
}
//printf("\nun trabajo hecho\n");
k++;
//para la siguiente iteración
/*Termina de medir el tiempo de actualización*/
actcont+= clock()-inicio_actualizacion;
}
}
MPI_Barrier(comMaTrab);
80
//muestranos la matriz W
if (myid==1){
//
printf("\nW empieza en la dirección %p:\n",W);
//
printf("\nValor de m %d:\n",m);
printf("\nDatos de la matriz W:\n");
for (i=0;i<m;i++)
muestradatos(W[i],N);
}
if (myid == 0){
hra2=time(NULL);
clock_t fin_cpu = clock();
printf("\nTiempo %f segundos\n",difftime(hra2,hra1));
printf("\nTiempo de cpu %f segundos\n",
((double)(fin_cpu - inicio_cpu))/CLOCKS_PER_SEC);
printf("\n número de renglones de la matriz %i\n",m);
}
/*Tiempos de slots en el ciclo*/
if (myid == 0){
printf("\nTiempo evaluación %f segundos\n",
((double)evalcont)
/CLOCKS_PER_SEC);
}
for (i=1;i<numprocs;i++){
if (myid==i){
printf("\nProc %i Tiempo multiplicación %f segundos\n",
i,((double)multcont)
/CLOCKS_PER_SEC);
printf("\nProc %i Tiempo actualización %f segundos\n",
i,((double)actcont)
/CLOCKS_PER_SEC);
81
}
}
MPI_Finalize();
}
void leerU(double * u,int N, int j){
//estos serian los valores que se leen de la base de datos aquí iría el código
//para leer la base de datos por ahora se rellenan aleatoriamente con números entre -100 y
100
for (int i=0; i<N; i++){
u[i]=-100+200*rand()/RAND_MAX;
}
//aquí los valores de la base de datos se
//tendrían referenciados por el apuntador u
}
void normalizaU(double *I,double * u,int N){
double sumatoria=0;
double norma;
int i;
for (i=0; i<N; i++){
sumatoria+=u[i]*u[i];
}
norma=sqrt(sumatoria);
if (norma>0.0)
//evitar divisiones por cero
for (i=0; i<N; i++){
I[i]=u[i]/norma;
}
}
82
void muestradatos(double * U,int N)
{
printf("[");
for (int i=0; i<N; i++)
printf("%5.2f, ", U[i]);
printf("]\n");
}
void inicia_matriz(double** W, int N, int *m)
{
int j=0;
double *I=new double[N];
W[0]=new double[N];
//llena el primer renglon de W con datos
//aleatorios entre -1 y +1
for (j=0; j<N; j++){
W[0][j]=-1+2*rand()/(double)RAND_MAX;
}
//normaliza el primer renglon de W para
//que se cumpla la condicion de ||w||
normalizaU(I,W[0],N);
for(j=0; j<N; j++){
W[0][j]=I[j];
}
(*m)++;
delete [] I;
}
void multiplicaWI(double **W,double *I, int m,int N, double *v)
{
int i,j;
double sumWI;
omp_set_num_procs(numproc);
83
#pragma omp parallel for
for(i=0; i<m; i++)
{
sumWI=0;
for(j=0; j<N; j++)
sumWI+=W[i][j]*I[j];
v[i]=sumWI;
}
}
double mayor(double *v, int m, int *pos)
{
double mayor;
mayor=v[0];
*pos=0;
for (int j=0; j<m;j++)
{
if (v[j]>mayor){
mayor=v[j];
*pos=j;
}
}
return mayor;
}
void incorporaW(double **W,double *I, int *m,int N)
{
W[*m]=new double[N];
for (int j=0;j<N;j++)
W[*m][j]=I[j];
(*m)++;
}
void actualizaW(double **W, double *I,int N,int pos)
{
84
int k;
for (k=0;k<N;k++){
W[pos][k]=ALFA*W[pos][k]+(1-ALFA)*I[k];
}
}
CODIGO SERIAL Y/O PARALELO.
Este mismo código lo utilizamos en ambos casos, como se ha mencionado solo se le agregan las
directivas de OpenMp al código secuencial, además del archivo de cabecera <omp.h>.
#include <math.h>
#include <time.h>
#include <stdlib.h>
#include <iostream.h>
#include <stdio.h>
#include <omp.h>
#include <omp.h>
const int N=1000;
const int mMax=1000;
//máximo número de vectores de peso
const double RO=0.1;
const double ALFA=0.3;
const long kMax=1000;
int numproc=omp_get_num_procs();
void leerU(double * u,int N, int j);
void normalizaU(double * I,double * U,int N);
void muestradatos(double * U,int N);
void inicia_matriz(double**, int n, int *m);
void multiplicaWI(double **W,double *I, int m,int N, double *v);
double maximo(double *v, int m,int *j);
void incorporaW(double **W, double *I, int *m, int N);
85
void actualizaW(double **W, double *I,int N,int j);
int main(){
time_t hra1,hra2;
hra1=time(NULL);
clock_t inicio_cpu = clock();
double u[N];
//vector con los datos leidos de la BD
double I[N];
//vector nomalizado
double *W[mMax];
//matriz de los vectores de pesos acumulados
double *w;
//vector de pesos
double *v=new double[mMax];
double valor;
int m=0;
//cantidad de vectores de pesos al momento
int k=0;
//contador del numero de registro en la BD
int j=0;
//contador de vector de peso que se
//esta analizando actualmente en la matriz W
int i=0;
//contador de elementos por vector
srand(1);
inicia_matriz(W,N,&m);
while (k<kMax){
//ciclo principal de iteracion
leerU(u,N,k);
//printf("U:");muestradatos(u,N);
normalizaU(I,u,N);
//multiplica W por I para obtener v
multiplicaWI(W,I,m,N,v);
//printf("U:");muestradatos(u,N);
86
valor=maximo(v,m,&j);
// printf("valor mayor= %f",valor);
if (RO>=valor){
//incorpora I a W
incorporaW(W,I,&m,N);
}
else{
//actualiza w[j]
actualizaW(W,I,N,j);
}
k++;
}
//termina ciclo principal de iteracion
printf("\nDatos de la matriz W:\n");
for (i=0; i<m; i++)
muestradatos(W[i],N);
hra2=time(NULL);
printf("\nTiempo %i segundos\n", (long)difftime(hra2,hra1));
clock_t fin_cpu = clock();
printf("\nTiempo %f segundos\n", difftime(hra2,hra1));
printf("\nTiempo
de
inicio_cpu))/CLOCKS_PER_SEC);
cpu
%f
segundos\n",((double)(fin_cpu
-
printf("\nnumero de renglones de la matriz %i\n",m);
printf("\n numero de procesadores en mi CPU %d \n", numproc);
return 0;
}
void leerU(double * u,int N, int j){
//estos serian los valores que se leen
//de la base de datos aqui iria el codigo
//para leer la base de datos
//por ahora se rellenan aleatoriamente
87
//con numeros entre -100 y 100
for (int i=0; i<N; i++){
u[i]=-100+200*rand()/RAND_MAX;
}
//aqui los valores de la base de datos se
//tendrian referenciados por el apuntador u
}
void normalizaU(double *I,double * u,int N){
double sumatoria=0;
double norma;
int i;
for (i=0; i<N; i++){
sumatoria+=u[i]*u[i];
}
norma=sqrt(sumatoria);
if (norma>0.0)
//evitar divisiones por cero
for (i=0; i<N; i++){
I[i]=u[i]/norma;
}
}
void muestradatos(double * U,int N)
{
printf("[");
for (int i=0; i<N; i++)
printf("%5.2f, ", U[i]);
printf("]\n");
}
void inicia_matriz(double** W, int N, int *m)
{
88
int j=0;
double *I=new double[N];
W[0]=new double[N];
//llena el primer renglon de W con datos
//aleatorios entre -1 y +1
for (j=0; j<N; j++){
W[0][j]=-1+2*rand()/(double)RAND_MAX;
}
//normaliza el primer renglon de W para
//que se cumpla la condicion de ||w||
normalizaU(I,W[0],N);
for(j=0; j<N; j++){
W[0][j]=I[j];
}
(*m)++;
delete [] I;
}
void multiplicaWI(double **W,double *I, int m,int N, double *v)
{
int i,j;
double sumWI;
Omp_set_num_threads(numproc)
#pragma omp parallel for
for(i=0; i<m; i++)
{
sumWI=0;
for(j=0; j<N; j++)
sumWI+=W[i][j]*I[j];
89
v[i]=sumWI;
}
}
double maximo(double *v, int m, int *j)
{
double dato1, mayor;
int i;
if (m==1){
mayor=v[0];
*j=0;
}
else if (m >1){
i=0;
mayor=v[i];
while(i<m){
dato1=v[i];
//dato2=v[i+1];
if (dato1>mayor){
mayor=dato1;
*j=i;
}
i=i+1;
}
}
else if (m<0)
printf("No hay datos");
return mayor;
}
double mayor(double *v, int m, int *pos)
{
90
double mayor;
mayor=v[0];
*pos=0;
for (int j=0; j<m;j++)
{
if (v[j]>mayor){
mayor=v[j];
*pos=j;
}
}
return mayor;
}
void incorporaW(double **W, double *I, int *m,int N){
W[*m]=new double[N];
for (int j=0; j<N;j++)
W[*m][j]=I[j];
(*m)++;
}
void actualizaW(double **W, double *I,int N,int pos)
{
int k;
for (k=0;k<N;k++){
W[pos][k]=ALFA*W[pos][k]+(1-ALFA)*I[k];
}
}
91
REFERENCIAS BIBLIOGRAFICAS:
[1]
Strouptrup Bjarne, “The C++ Programming Language”. 2 nd edition,
Addison Wesley, 1991.
[2]
Chandra
Rohit,
Dagum
Leonardo.
“Parallel
Programming
in
OpenMP”,MORGAN KAUFMANN PUBLISHERS, 2001.
[3 ]
Lastovetsky Alexey L., “Parallel Computing on Heterogeneous Networks”.
University College, Dubln
[4]
Ireland, WILEY-INTERSCIENCE, 2003.
Norton Scott J., Dipasquale Mark D., “The Multithreaded Programming
Guide”. Thread Time. PRENTICE HALL PTR, 1997.
[5]
Lewis Bil, Berg Daniel J., “A Guide to Multithreaded Programing”.
Threads Primer. PRENTICE HALL, 1996.
[6]
Beveridge Jim and Wiener Robert,
“Multithreading Applications in
Win32” The complete Guide to Threads. Addison-Wesley, 2001.
[7]
J. Quinn Michael, “Parallel Programming in C with MPI and OpenMP”,
Mc Graw-Hill, 2003.
92
[8]
Intel Corporation. “Intel Core 2 Duo Processor on 45-nm Proccess
Datasheet. Intel Corporation www.intel.com, 2008.
[9]
Intel Corporation. “Intel VTune Performance Analyzer Documentation”.
www.intel.com, 2008.
[10]
www.openmp.org
[11]
Dong Hyuk Woo and Hsien-|Hsin S. Lee, “Extending Amdahl’s Law for
Energy-Efficent Computing in the Many-Core Era”, Published by the IEEE
Computer Society, December 2008.
[12]
Dan Quilan, Markus Schordan, Qin Yi, and Bronis R. de Supinski. A C++
Infrastructure for Automatic Introduction and Translation of OpenMP
Directives. WOWPAT, Berlin Heidelberg, 2003 .
[13]
Ref. dirección de mpi.org.
[14]
www.mpi.org (página official de MPI).
[15]
Moisen, M.C. Benítez-Pérez, H. Medina L. Ultrasonic (XXXX) NDT for
flaws characterisation using ARTMAP network and wavelet analysis. Int. J.
Materials and Product Technology.
[16]
Carpenter, G.A., Grossberg, S., Markuzon, N., Reynolds, J.H. and Rosen,
D.B. (1992). Fuzzy ARTMAP. A neural network architecture for
93
incremental supervised learning of analog multidimensional maps, IEE
Trans. Neural Network, Vol. 3, pp. 698 713.
[17]
Frank, T., Kraiss, K.F. and Kuhlen, T. (1998) Comparative analysis of fuzzy
ART and ART-2A network clustering performance, IEEE Trans. Neural
Network, vol. 9, pp. 544 559.
[18]
Carretero Pérez Jesús, Anasagasti Pedro de Miguel, Sistemas operativos,
una visión aplicada, Mc Graw Hill, 2001
[19]
M. Morris Mano, Arquitectura de computadoras, Prentice Hall, 2001
[20]
Von Neumann Architecture. Página Web.
http://www.hostgold.com.br/hospedagem-sites/o_que_e/vonNeumann+architecture/+.
[21]
Tanenbaum. S. Andrew, Distributed Operating Systems, Prentice Hall 1996
[22]
Bruce P. Lester, The Art of Parallel programming, Prentice Hall, 1993.
[23]
Alan H. Karp and Horace P. Flatt,
Measuring Parallel Processor
Performance, may 1990 vol 33 num 5.
[24]
Butenhof R. David, Programming with POSIX Threads, ADDISONWESLEY PROFESSIONAL COMPUTING SERIES, May 2000
[25]
Lamport Leslie, Specifying Systems, Addison-Wesley Professional, 2002
[26]
Rohit Chandra-Dagum Leonardo, Parallel Programming in OpenMP,
MORGAN KAUFMANN PUBLISHERS, 2001.
94
[27]
http://www-unix.mcs.anl.gov/mpi/mpich/.
[28]
ftp://ftp.mcs.anl.gov/pub/mpi/mpich.tar.gz.
[29]
www.mcs.anl.gov/mpi/mpich1
[30]
Gail A. Carpenter and Stephen Grossberg, Pattern Recognition by SelfOrganizing Neuronal Networks. 1991.
[31]
CARPENTER and Stephen GROSSBERG, NEURONAL NETWORKS
FOR VISION AND IMAGE PROCESSING, 1992, Edited by Gail A.
95
Descargar