Arquitectura de Software
Arquitectura de Software
Arquitectura de Software
Historia
El interés por la computación paralela se remonta a finales de los años 50. Este interés
se vio expresado en forma de supercomputadores, que aparecieron en los años 60 y 70. Estos
computadores tenían procesadores de memoria compartida, con múltiples procesadores
trabajando lado a lado con datos compartidos.
A mediados de los 80, un nuevo tipo de computador paralelo fue creado cuando el
proyecto “Concurrent Computation” de Caltech construyó un supercomputador para
aplicaciones científicas. El sistema mostró que se podría lograr un rendimiento extremo
usando microprocesadores regulares, disponibles en el mercado.
Empezando a los finales de los 80, los clusters surgieron para competir y con los MPP.
Un cluster es un tipo de computador paralelo, construido usando múltiples computadores “off-
the-shelf”, conectados usando una red “off-the-shelf”. Hoy en día, los clusters son la
arquitectura dominante en los datacenters.
Para los MPP y clusters surgió el estándar MPI a mediados de los 90, que convergió de
otras API. Para los multiprocesadores con memoria compartida, un proceso de convergencia
similar se observó a finales de los 90, con el surgimiento de pthreads y OpenMP.
En la actualidad, la computación paralela se ha vuelto mainstream prácticamente, con
la llegada de los procesadores de varios núcleos casi por defecto en la mayoría de dispositivos
computacionales.
El software ha sido una parte activa en la evolución de la programación paralela. Los
programas paralelos son más difíciles de escribir que los programas secuenciales, ya que se
requiere que haya una comunicación y sincronización entre las tareas que se han paralelizado.
Ventajas
Resuelve problemas que no se podrían realizar en una sola CPU
Resuelve problemas que no se pueden resolver en un tiempo razonable
Permite ejecutar problemas de un orden y complejidad mayor
Permite ejecutar código de manera más rápida (aceleración)
Permite ejecutar en general más problemas
Obtención de resultados en menos tiempo
Permite la ejecución de varias instrucciones en simultáneo
Permite dividir una tarea en partes independientes
Desventajas
Mayor consumo de energía
Mayor dificultad a la hora de escribir programas
Dificultad para lograr una buena sincronización y comunicación entre las tareas
Retardos ocasionados por comunicación ente tareas
Número de componentes usados es directamente proporcional a los fallos potenciales
Condiciones de carrera
Múltiples procesos se encuentran en condición de carrera si el resultado de los
mismos depende del orden de su llegada
Si los procesos que están en condición de carrera no son correctamente
sincronizados, puede producirse una corrupción de datos
Conceptos Clave
Conceptos acerca de Tareas
Tarea:
Granularidad:
Se refiere al tamaño de cada tarea y a la independiencia de las demás tareas, se dividen en dos
categorías.
Gruesa: Cantidad relativamente grande de trabajo, alta independencia entre tareas y
poca necesidad de sincronización.
Fina: Cantidades pequeñas de trabajo, poca independencia entre tareas, y una alta
demanda de sincronización.
Scheduling:
Scheduling es el proceso en el que las tareas son asignadas a los procesos o hilos, y se les da
un orden de ejecución. Este puede ser especificado en el código, en tiempo de compilación o
dinámicamente en tiempo de ejecución. El proceso de scheduling debe tener en cuenta la
dependencia entre tareas, ya que, aunque muchas pueden ser independientes, otras pueden
requerir los datos producidos por otras tareas.
Sincronizacion:
Los programas en paralelo necesitan la coordinación de procesos e hilos, para que haya una
ejecución correcta. Los métodos de coordinación y sincronización en la programación paralela
están fuertemente asociados a la manera en que los procesos o hilos intercambian
información, y esto depende de cómo está organizada la memoria en el hardware.
Mapping:
Overhead
Sección crítica
Un proceso tiene un segmento de código llamado sección crítica cuando este puede modificar
o leer información de memoria compartida con otros procesos. Dos procesos no pueden
ejecutar su seccion critica al mismo tiempo.
Concurrencia vs Paralelismo
Concurrencia
Capacidad de operar actividades al mismo tiempo. Es decir se pueden tener varios
procesos corriendo cada uno en un procesador o puede haber varios proceso que corran
solo en un procesador
Paralelismo
Son muchas actividades teniendo lugar al mismo tiempo, “la cualidad o el estado de ser
paralelo”. El hecho de ser paralelo implica que solo se pueden tener varios procesos
corriendo cada uno en un procesador.
Componentes principales
Memoria
Unidad de control
Unidad Aritmetica Logica
Entradas/Salidas
Memoria de acceso aleatorio
Taxonomia de Flynn
Single Instruction, Single Data (SISD)
hay un elemento de procesamiento, que tiene acceso a un único programa y a un
almacenamiento de datos. En cada paso, el elemento de procesamiento carga una instrucción
y la información correspondiente y ejecuta esta instrucción. El resultado es guardado de vuelta
en el almacenamiento de datos. Luego SISD es el computador secuencial convencional, de
acuerdo al modelo de von Neumann.
Multiple Instruction, Single Data (MISD)
hay múltiples elementos de procesamiento, en el que cada cual tiene memoria privada del
programa, pero se tiene acceso común a una memoria global de información. En cada paso,
cada elemento de procesamiento de obtiene la misma información de la memoria y carga una
instrucción de la memoria privada del programa. Luego, las instrucciones posiblemente
diferentes de cada unidad, son ejecutadas en paralelo, usando la información (idéntica)
recibida anteriormente. Este modelo es muy restrictivo y no se ha usado en ningún
computador de tipo comercial.
Tipos de paralelismo
Paralelismo a nivel de bit:
Se habla de paralelismo al nivel de bit, cuando se aumenta el tamaño de la palabra
del procesador (tamaño de la cadena de bits a procesar). Este aumento reduce el
número de instrucciones que tiene que ejecutar el procesador en variables cuyos
tamaños sean mayores a la longitud de la cadena.
Ejemplo: En un procesador de 8-bits sumar dos números de 16bits tomaría dos
instrucciones. En un procesador de 16-bits esa operación requiere solo una instrucción.
Nota: este método está “estancado” desde el establecimiento de las arquitecturas de 32 y 64
bits.
Fork-Join
Fork-Join es un patrón que nos permite partir el flujo de un programa en múltiples flujos
paralelos para reunirlos luego.
Map
Map es un patrón que replica una función sobre todos los elementos de un conjunto de
entrada. La función que está siendo replicada se llama función elemental, dada que la
misma se aplica a una coleccion real de datos.
Stencil
Stencil es un generalización del patron de Map, en el cual una función elemental tiene
acceso no solo a un elemento del conjunto de entrada sino también a un conjunto de
"vecinos"
Como la estructura de datos no es infinita se deben tener en cuenta el manejo de
excepciones para los bordes de la misma.
Reducción
Una reducción combina cada elemento de una colección en uno solo utilizado una
función asociativa conocida como función combinatoria. Como es asociativa las tareas
se pueden distribuir de muchas maneras y si la función resultara ser también
conmutativa el número de posibilidides aumentaría aún más.
Scan
Scan realiza las reducciones de cada elemento perteneciente a una estructura. En otras
palabras cada elemento de salida es la reduccion de un elemento de entrada. A traves de
una función sucesora se avanza de un estado al otro haciendo folds en el proceso
asociativo.
Memoria distribuida
También llamado modelo de paso de mensajes
requieren una red de comunicación para conectar la memoria entre procesadores
Las tareas intercambian datos por medio del paso y recepción de mensajes
Los procesadores tienen su propia memoria local. Las direcciones de memoria en un
procesador no se asignan a otro procesador, por lo que no hay concepto de espacio de
direcciones global en todos los procesadores.
Debido a que cada procesador tiene su propia memoria local, funciona
independientemente. Los cambios que hace en su memoria local no tienen ningún efecto en la
memoria de otros procesadores. Por lo tanto, el concepto de coherencia de caché no se aplica.
Cuando un procesador necesita acceso a los datos de otro procesador, suele ser la
tarea del programador definir explícitamente cómo y cuándo se comunican los datos. La
sincronización entre tareas también es responsabilidad del programador.
Hibrido memoria distribuida-comopartida
Es la combinación entre memoria compartida y memoria distribuida, con sus ventajas
en común.
Su principal ventaja es su escalabilidad.
Su principal desventaja es que la complejidad de programación aumenta.
Hilos
Un proceso pesado puede convertirse en varios procesos livianos ejecutados de
manera concurrente.
Se pueden describir como una subrutina dentro del programa principal.
Se comunican entre ellos a través de la memoria global.
Datos en paralelo
También conocido como PGAS (Partitioned Global Address Space)
Una serie de tareas trabajan de manera colectiva en la misma estructura de datos
Las tareas realizan la misma operación, pero cada una en su partición pero cada tarea
trabaja en una partición diferente de ésta
Sincronización
Administrar la secuencia de trabajo y las tareas que lo realizan es una consideración
crítica del diseño para la mayoría de los programas paralelos.
Puede ser un factor significativo en el desempeño del programa, pues un diseño de
sincronización adecuado reduce el tiempo overhead.
A menudo requiere "serialización" de segmentos del programa.
Sincronización Mínima:
1. Identificar suficiente concurrencia en la descomposición del problema.
2. Decidir cómo manejarla: distribución estática o dinámica.
3. Determinar el grado de granularidad y cómo explotar la concurrencia.
4. Reducir serialización y costes de sincronización.
1. Identificar suficiente concurrencia
Técnicas estáticas:
Sincronización de eventos:
Lock / semaphore
Ciertas clases de problemas producen desequilibrios de carga incluso si los datos están
distribuidos uniformemente entre las tareas
Medidas de rendimiento
Tiempo de respuesta
Es el tiempo que tarda en ejecucion el programa A.
MIPS - MFLOPS
MIPS - Millones de operaciones por segundo. MFLOPS - Millones de operaciones de
punto flotante por segundo.
Eficiencia
La eficiencia del programa se mide en el costo de ejecucion.
SpeedUp
El SpeedUp resresenta la ganacia que se obtiene en la version paralela del programa
respecto a la version secuencial del mimo.
Ley de Amdahl
El incremento de velocidad de un programa utilizando múltiples procesadores en
computación paralela está limitada por la fracción secuencial del programa. Sea f el
porcentaje paralelizado del programa expresado en decimal, la ley de Amdahl dice que
llega un punto en el cual sin importar que el numero de procesadores sea muy alto , el
speedup se va a comportar de manera lineal ; esto de acuerdo al porcentaje que este
paralelizado el codigo. El speedup de un programa con un fragmento paralelizado se
calcula con :
Lenguajes de programacion
Aplicaciones
Computación grafica
Ciertas clases de problemas producen desequilibrios de carga incluso si los datos están
distribuidos uniformemente entre las tareas
Astronomía
Hay muchas aplicaciones de los supercomputadores a la astronomía, incluyendo el uso
de un supercomputador para simular eventos en el futuro, o pasado, para probar teorías
astronómicas. La Universidad de Minnesota Supercomputer Center simuló lo que una
explosión de supernova que se origina en el borde de una gigantesca nube de gas
molecular interestelar parecería 650 años después de la explosión.
Renderizado de imagenes y animación
OpenMP
Es una interfaz de programa de aplicación (API) que se puede utilizar para dirigir
explícitamente paralelismo de memoria compartida multi-procesos. Está compuesto por:
Directivas de compilación
Runtime Library Routines
Variables de entorno
Estructura general de OpenMP en C++
#include <omp.h>
int main () {
int var1, var2, var3;
//Código en serie...
#include <omp.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
/* Cada thread tiene una variable id privada (tid) */
int nthreads, tid;
#include <omp.h>
#include <stdio.h>
#define N 1000
#define CHUNKSIZE 100
int i, chunk;
float a[N], b[N], c[N];
#include <omp.h>
int main(int argc, char *argv[])
{
int x;
x = 0;
El comando CRITICAL especifica una región de código que debe ser ejecutada por un
solo hilo a la vez.
Si un subproceso está ejecutándose actualmente dentro de una región CRÍTICA y otro
subproceso llega a esa región CRÍTICA e intenta ejecutarla, se bloqueará hasta que el primer
subproceso salga de esa región CRÍTICA.
todos los hilos del equipo intentarán ejecutarse en paralelo. Sin embargo, debido a la
construcción CRÍTICA que rodea el incremento de x, sólo un hilo podrá leer, incrementar o
escribir x en cualquier momento
Ejemplo de Programación Paralela en Java
@Override
public void run() {
System.out.println("La cajera " + this.nombre + "
COMIENZA A PROCESAR LA COMPRA DEL CLIENTE "
+ this.cliente.getNombre() + "
EN EL TIEMPO: "
+ (System.currentTimeMillis() -
this.initialTime) / 1000
+ "seg");
for (int i = 0; i <
this.cliente.getCarroCompra().length; i++) {
this.esperarXsegundos(cliente.getCarroCompra()
[i]);
System.out.println("Procesado el producto " +
(i + 1)
+ " del cliente " + this.cliente.getNombre() +
"->Tiempo: "
+ (System.currentTimeMillis() -
this.initialTime) / 1000
+ "seg");
}
System.out.println("La cajera " + this.nombre + " HA
TERMINADO DE PROCESAR "
+
this.cliente.getNombre() + " EN EL TIEMPO: "
+
(System.currentTimeMillis() - this.initialTime) / 1000
+ "seg");
}
Supermercado
cajera1.start();
cajera2.start();
}
}
Supermercado
Se inicializan los dos hilos, luego se imprime en pantalla cada vez que se pasen los
productos en cada cajero.
Se puede observar como se realiza de forma paralela la compra en cada cajero.
Supermercado