Paradigmas de Computación Paralela, Concurrente y Distribuida Dra. Veronica Gil-Costa e-mail: gvcosta@unsl.edu.ar ggvcosta@gmail.com Bibliografía • O. Bonorden et all - PUB library, Release 6.0 - User guide and function reference. 1998. • O. Bonorden et all - The Puderborn University BSP (PUB) Library- Desing, Implementation and performance. 1999. • M. Goudreau et all - Towards Efficiency and Portability: Programming with the BSP model. 1996. • J. Keller et all - Practical PRAM programming. John Wiley & Sons inc.. 2001. • C. Leopold - Parallel and Distributed Computing: A survey of models, paradigms, and approaches. 2001. • W.F. Mccoll - BSP Programming. 1994. • M. Quinn - Parallel Computing. Theory and Practice. Second Edition. McGraw-Hill. Inc. 1994. • L.G. Valiant - A Bridging Model for Parallel Computation. 1990. • General Purpose parallel Architectures. 1990. • B. Wilkinson, et all - Parallel Programming: Tecniques and Aplications using Networked Workstations and Parallel Computers. 1999. Modelo de Computación Secuencial Computadora von Neumann Clock CPU Memoria de Programa Cómo se escriben los programas? Memoria RAM .. Mi .. M1 M0 ALU Registros Contador de Programa Qué es la computación paralela? Tradicionalmente el soft. se escribe en forma serial Es el uso simultáneo de múltiples recursos de cómputos para resolver un problema Línea Temporal Sist. Paralelos Sist. Distribuidos Sist. Hibridos ARPANET 1969 1976 1987 1990 1993 1995 1999 2005 2007 tiempo ARPANET Primera red de computadoras básico Experimento militar Comienzo de la Internet Línea Temporal Sist. Paralelos Sist. Distribuidos Sist. Hibridos ARPANET Cray-1 1969 1976 1987 1990 1993 1995 1999 2005 2007 tiempo Cray-1 Procesamiento vectorial: Usa la misma instrucción para realizar la misma operación sobre varios argumentos 150millones de operaciones por segundo de punto flotante Computadores grandes, costosos Dueños: Universidades, gobierno, grandes industrias. Usuarios experimentados (tarjetas perforadas) Línea Temporal Sist. Paralelos Sist. Distribuidos Sist. Hibridos prototipo Parallel Inference Machine (PIM) ARPANET Cray-1 1969 1976 1987 1990 1993 1995 1999 2005 2007 tiempo Computación Paralela Popular a fines de los ’80 principios de los ’90 Primera Crisis Problema: el software limitado a la investigación y la milicia Los lenguajes de alto nivel para el modelo de Von- Neumann: C y Fortran Línea Temporal Sist. Paralelos Sist. Distribuidos Sist. Hibridos prototipo Parallel Inference Machine (PIM) Redes de Comp. ARPANET Cray-1 1969 1976 1987 NOW 1990 1993 Beowulf 1995 1999 2005 2007 tiempo Redes de Computadoras NOW: proyecto de la Universidad de Bekeley Primer cluster de computadoras más poderoso del mundo Beowulf: es un estilo de cluster diseñado para realizar cálculos paralelos HPC Tenía 16 computadores personales Intel 200MHz conectados por medio de un switch Ethernet Línea Temporal Sist. Paralelos Sist. Distribuidos Sist. Hibridos prototipo Parallel Inference Machine (PIM) Redes de Comp. ARPANET Cray-1 1969 1976 1987 NOW 1990 1993 GRID Beowulf 1995 1999 2005 2007 Hacer del poder computacional tan fácil como la red eléctrica Proyecto SETI: Globus Toolkit tiempo Línea Temporal Sist. Paralelos Sist. Distribuidos Sist. Hibridos prototipo Parallel Inference Machine (PIM) Redes de Comp. ARPANET PVM Cray-1 1969 1976 1987 MPI NOW 1990 1993 GRID Beowulf 1995 1999 OpenMP Multi-core 2005 2007 tiempo Línea Temporal Línea Temporal Top 500 http://www.top500.org/lists/2010/11 229376 GB 186368 Cores Intel EM64T Xeon X56xx (Westmere-EP) 2930 MHz (11.72 GFlops) Noviembre 2010 Línea Temporal Sist. Paralelos Sist. Distribuidos Sist. Hibridos prototipo Parallel Inference Machine (PIM) Redes de Comp. ARPANET PVM Cray-1 1969 1976 1987 MPI NOW 1990 1993 GRID Beowulf 1995 1999 OpenMP Cloud Multi-core Comp. 2005 2007 tiempo Cloud Computing Es un paradigma que ofrece servicios a través de internet. Los usuarios acceden sin conocimiento de la gestión de internet Utilizan cachés temporales de clientes (equipos de escritorio, portátiles, etc.). A pesar de que las capacidades de las PC han mejorado, gran parte de su potencial se desperdicia Línea Temporal Sist. Paralelos Sist. Distribuidos Sist. Hibridos prototipo Parallel Inference Machine (PIM) Redes de Comp. ARPANET PVM Cray-1 1969 1976 1987 MPI NOW 1990 1993 GRID Beowulf 1995 1999 GPU OpenMP Cloud Multi-core Comp. 2005 2007 tiempo GPU Descienden de los chips gráficos de finales de los ’80. Usualmente no tenían soporte para dibujo de figuras. La velocidad de las CPU de la década del ’90 no permitieron implementar GPUs más avanzados Popularidad reciente: alta especialización de las GPU para cáclulo con valores de punto flotantes (gráficos 3D) Computación Paralela Características del problema Aplicaciones Por qué usar computación Paralela? Computación Paralela: Características del problema Se puede dividir en partes discretas de trabajo que se pueden resolver simultáneamente Ejecutar múltiples instrucciones de programa en cualquier instante de tiempo Se resuelve en menos tiempo con múltiples recursos que con uno sólo Aplicaciones Sistema de predicción del tiempo Superficie de USA + Candada = 20.000.000 Km2 Altitud de 20 km 20 M Km2 * 20 Km = 400 M km3 …. 4 * 1013 cálculos computacionales para predecir 1 hs. Si queremos saber la predicción de 2 días y con un procesador se ejecuta 1 billón de cálculos por seg 4 * 1013 * 48 horas = 2 * 1015 cálculos 2 * 1015 cal./ 109 calc. por seg. = 2 * 106 seg. = 23 días!!! Aplicaciones Calcular las orbitas planetarias y galácticas Movimiento de las placas tectónicas ¿Qué tienen estas aplicaciones en común? Tráfico de vehículos en horas extremas Operaciones de mercado (bancos, Se caracterizan por requerir el procesamiento de una economía) gran cantidad de información en forma rápida Explotación de petróleo Por qué usar Computación Paralela? Razones principales: Ahorrar tiempo Resolver grandes problemas OtrasParalela razones: LaComp. es la evolución de la computación secuencial que intenta emular lo que sucede en el Aprovechar el uso de recursos no locales mundo real, donde varios eventos Ahorrar costos: usar varias computadoras complejos e interrelacionados suceden simultáneamente económicas Evitar problemas de memoria Paradigmas de Computación Paralela, Concurrente y Distribuida Introducción a los Sistemas Paralelos Terminología Tarea: programa o un conjunto de instrucciones (tipo-programa) que se ejecuta en un procesador. Paradigmas de computación Paralelas Computación Concurrente Es la simultaneidad en la ejecución de múltiples tareas interactivas. Es la base de la Comp. Paralela. Son tareas que se ejecutan en el mismo instante. Es una colección de procesos o hilos secuenciales que se ejecutan (lógicamente) en paralelo. Pseudo-paralelismo: cuando las tareas se ejecutan en una máquina secuencial. Como se puede implementar la concurrencia? Multiprogramación: Multiprocesamiento: Los threads multiplexan su ejecución sobre una máquina secuencial Los threads multiplexan su ejecución sobre multicores o multiprocesadores Procesamiento distribuido: Los procesos multiplexan su ejecución sobre diferentes máquinas Coherencia y seguridad Si los threads acceden a recursos compartidos se logra seguridad si: Todos los accesos no tienen efecto sobre los recursos (ej. variables) Sólo un acceso a la vez (exclusión mutua) Problema de “Too much milk” Tiempo Tu 3:00 Llegas a casa 3:05 Miras en la heladera no hay leche Vas al mercado 3:10 3:15 compañero de cuarto Llega a casa 3:20 Llegas al mercado Mira la heladera, no hay leche Hay que SINCRONIZAR y actualizar datos correctamente 3:25 Compras la leche Va al mercado 3:30 3:35 3:45 Llegas a casa Pones en la heladera Llega al mercado Compra la leche Llega a casa, Oh! No!!! Problema: “filósofos cenando” Hay 5 filósofos sentados en una mesa redonda Entre cada filósofo hay un chopstick. Cada filósofo hace dos cosas: pensar y comer El filósofo piensa por un rato Cuando tiene hambre Agarra el chopstick derecho e izquierdo No puede comer sin los 2 chopstick, sino espera Cuando termina de comer libera los chopstick. Problemas: Competencia, coherencia y seguridad por los recursos semáforos, mutex, etc. Paradigmas de computación Paralelas Computación Paralela vs. Computación Distribuida Divide una aplicación en tareas que se ejecutan al mismo tiempo Divide una aplicación en tareas que se ejecutan usando diferentes recursos Paradigmas de computación Paralelas Computación Paralela vs. Computación Distribuida diferentes recursos Divide una aplicación en tareas Utiliza físicamente separados que se ejecutan simultáneamente (fuertemente acoplado) Se consideran varias aplicaciones Se considera una aplicación por a la vez (pueden pertenecer a diferentes usuarios). vez. Objetivo: acelerar la ejecución de Objetivo: permitir que varios usuarios trabajen en forma una aplicación cooperativa Se ejecutan sobre arquitecturas Se ejecutan sobre arquitecturas abiertas y homogéneas con memoria heterogéneas, dinámicas compartida Sistema Distribuido Cada procesador tiene su propia memoria: La dirección de memoria de un procesador no mapea en otro procesador (no hay direcciones globales a través de los procesadores). Cada procesador opera independientemente El acceso a los datos de otro procesador lo define explícitamente el usuario (pasaje de mjes, sync., etc.) Sistema Distribuido Ventajas: Memoria escala con el número de procesadores Cada procesador accede a su propia memoria sin interferencia ni overhead causado por mantener la coherencia de la memoria Desventajas: El programador es responsable de la comunicación Difícil mapear datos basados en memoria global Paradigmas de computación Paralelas Sin embargo tienen muchas características en común: Múltiples procesadores Los procesadores están interconectados por alguna red Múltiples procesos están en progreso al mismo tiempo y cooperan unos con otros Paradigmas de computación Paralelas Multiplicación de un vector por una matriz un procesador 0 1 0 x 1 2 3 2 Si para cada resultado 5 t. 4 5 6 5 7 8 9 8 Total 15 t. en 1 procesador Multiplicación de un vector por una matriz varios procesadores 0 1 0 x 0*1+1*2+0*3 Proc0 1 2 3 4 5 6 0*4+1*5+0*6 Proc1 7 8 9 0*7+1*8+0*9 Proc2 Si para cada resultado 5 t. Total 5 t. en 3 procesadores Terminología Tarea Paralela: se ejecuta en varios procesadores en forma segura (resultados correctos). Terminología Comunicación: A través de pasaje de mensajes A través de memoria compartida Acceso a memoria remota Sincronización: Coordinación de tareas paralelas en tiempo real. Terminología El tamaño de un proceso puede ser descrito por su granularidad TAMAÑO DE COMPUTACIÓN ENTRE DOS PUNTOS CONSECUTIVOS DE COMUNIACIÓN Y SINCRONIZACIÓN Terminología Granularidad gruesa: Comunicación y sincronización Cómputo Comunicación y sincronización Mucho computo. Mas oportunidad para mejorar la performance. Terminología Granularidad fina: Comunicación y sincronización Cómputo Comunicación y sincronización Cómputo Comunicación y sincronización Cómputo Comunicación y sincronización Más overhead de comunicación. Menos oportunidad para mejorar la performance. Granularidad Como métrica de granularidad se puede utilizar una relación entre computación y comunicación: Granularidad = Tcomp/Tcomm Aumentar la granularidad: • Reduce los costos de creación de procesos y comunicación entre procesos • Reduce el número de procesos concurrentes y la cantidad de paralelismo Terminología Speed-up: aceleración del código paralelo respecto del código secuencial speed-up( P ) T1/ T(P) Speed-up Super-lineal Speed-up lineal Speed-up típico Numero de procesadores P Eficiencia: se define como el ratio entre el speed-up y el número de procesadores Ef( P ) = speed-up(P)/P Limites del Paralelismo No todos los programas son fácilmente paralelizables Los programas tienen una parte secuencial y una parte paralelizable Parte secuencial Dependencia de datos? Parte paralela a = b + c; d = a + 1; e = d + a; for (i=0; i < e; i++) M[i] = 1; Ley Amadahl’s Todo cómputo posee una porción serial “s” que no puede ser paralelizable. Sin importar la cantidad de procesadores P que se utilicen, el tiempo de ejecución no puede ser menor que “s”. Tpo secuencial f No se puede paralelizar: Límite de la mejora que se puede obtener Ley Amadahl’s tiempo Usando 5 procesadores para el trabajo paralelo 25 seg. + 50 seg. + 25 seg. 100 seg. secuencial paralelo 25 seg. secuencial + 10 seg. 25 seg. secuencial secuencial 60 seg. Speed- up = Tsec/Tpar = 100 seg./ 60 seg. = 1.67 Administración de tareas Overhead agregado por la administración de un conjunto de tareas Overhead: Tiempo requerido para coordinar las tareas paralelas (opuesto al hacer trabajo útil) Tiempo de Start-up Sincronización Comunicación de datos Software overhead, threads, Tiempo de finalización de las tareas Balance de carga Es difícil dividir el trabajo uniformemente entre los P procesadores No basta con asignar el mismo trabajo a todos los procesadores porque el tamaño de las tareas puede variar dependiendo de los datos que use. Ejemplo: guía telefónica Cómputo - Comunicación Balance de Carga Los procesos que terminan antes deben esperar al proceso que más se demora Tiempo ocioso Menor utilización Balance de Carga Estática El programador decide a priori la cantidad de trabajo que se le asigna a cada proceso Funciona bien en sistemas homogéneos Todos los procesadores tienen las mismas características Cada proceso tiene la misma cantidad de trabajo No funciona bien en sistemas heterogéneos Balance de Carga Dinámica Cuando un proceso finaliza su tarea, saca más trabajo del proceso más cargado Ideal para sistemas heterogéneos con carga de trabajo no uniforme No funciona bien en sistemas distribuidos Terminología Escalabilidad: Habilidad de demostrar un incremento en el speed-up paralelo Término impreciso: se puede usar para describir el hardware o el software. Escalabilidad Hardware Al incrementar el número máquinas mejora la performance del sistema Escalabilidad con tamaño de problema fijo Analizar cómo varía el tiempo paralelo y la eficiencia al aumentar P ¿Cuántos P necesito para obtener una eficiencia del 50%? Escalabilidad Software Al aumentar el tamaño del problema no decrece la performance del sistema Tamaño del problema: Tamaño del input o número de pasos en el mejor algoritmo secuencial Terminología Latencia: tiempo que toma en transmitir un mensaje de un lugar a otro. (Demora) Bandwidth: cantidad de bits que pueden ser transferidos por segundo. Costo alto en sistemas heterogéneos. Costo de comunicación = Latencia + Bandwith Terminología Portabilidad Características de ciertos programas que les permite ser utilizados en distintas arquitecturas sin que precise Modificaciones de importancia Clasificación de las Computadoras Paralelas Aspectos del Hardware Clasificación de Flynn (1972) SISD: único flujo de instrucciones aplicado a un único flujo de datos SIMD: único flujo de instrucciones aplicado a múltiples flujos de datos MIMD: múltiplo flujo de instrucciones aplicado a múltiple flujo de datos SISD Todas las computadoras con un único procesador, desde computadoras personales hasta supercomputadoras PC, Workstations Programa IS CU Procesador Datos DS PU MM SIMD PU1 DS1 DS2 PU2 CU PUN IS load Ai Load Bi Ci = Ai*Ci MM2 IS DSN Ejemplo: MM1 SINCRONISMO MMN MIMD IS1 CU IS2 CU CU ISN PU1 DS1 DS2 PU2 DSN PUN ISN ASINCRONO MM1 MM2 MMN IS1 MIMD MIMD C. Distribuidas y Paralelas Memoria Distribuida (privada) Memoria Compartida Multiprocesador BUS Switch Multicomputador BUS Switch Paradigmas de computación Paralelas Memoria Compartida Memoria Distribuida Mecanismos de locks y Sincronización. Responsabilidad del programador Mecanismos de comunicación Todos los procesadores ven los mismos datos Cada procesador ve datos diferentes Distribución de datos M. Compartida vs. M. Distribuida M.Compartida Se puede compartir datos rápidamente entre las tareas Acceso a datos en forma amigable para el programador Escalabilidad pobre entre CPUs y memoria. Son caras M.Distribuida Escalable No existe problemas de coherencia de memoria. Costo bajo. Puede ser difícil mapear estructuras de datos en diferentes procesadores (SAT-tree). MIMD- Hardware Topologías de M. Compartida Arquitectura basada en BUS Existe saturación del bus Coherencia de cache Performace limitada al bandwidth del bus Arquitecturas basadas en Switch Puede ser visualizada como una grilla de cables con SW en las intersecciones y memoria y procesadores en las terminales. Muy cara MIMD- Hardware Topologías de M. Distribuida Redes basadas en BUS Muy sencillas No escalables Redes basadas en SW Rápidas Jerarquía de SW: Fat-tree Fat-Tree Como programar en M. Compartida? Los procesadores 1.. N requieren la variable x Sólo hay un lugar donde buscar Condiciones de competencia: Usar Sync. Para evitar conflictos Como programar en M. Compartida? Un único proceso puede crear múltiples threads concurrentes Cada thread encapsula su propia ruta de ejecución Cada thread tiene estados locales y recursos compartidos Los threads se comunican a través de recursos compartidos como la memoria Como programar en M. Distribuida? Los procesadores 1.. N requieren la variable x Hay n lugares donde buscar Cada procesador tiene su propia X El valor de X puede variar Para que el P1 busque X en P2: P1 envía una petición de X a P2 P2 envía una copia de X a P1 P1 recibe la copia P1 la almacena en su memoria Pasaje de Mensajes Y Creación de Procesos Creación de procesos Estática: Todos los procesos se especifican antes de la ejecución El sistema ejecutará un número fijo de procesos El programador lo determina por línea de comando SPMD y MPMD(Master-Slave) Creación de procesos Dinámica: Un proceso puede ser creado e inicializado durante la ejecución de otro proceso También pueden ser destruidos La creación y destrucción puede realizarse en forma condicional (varía el número de procesos) MPMD tiempo spawn() Proceso 1 Proceso 2 Pasaje de mensajes Las arquitecturas con memoria distribuida usan comunicación explícita para intercambiar datos El intercambio de datos requiere sincronización (cooperación) entre los senders-receivers Cómo se describen los datos? Cómo se identifican los procesos? Qué significa que se complete un envío o recepción? Ejemplo Se quiere calcular la distancia entre cada punto de A[1..4] y B[1..4] y guardar los resultados en C[1..4][1..4] Ejemplo Se quiere calcular la distancia entre cada punto de A[1..4] y B[1..4] y guardar los resultados en C[1..4][1..4] Podemos dividir el Trabajo entre dos procesadores P1 envía datos a P2 P1 y P2 computan P2 envía datos a P1 Ejemplo Patrones de comunicación Al usar pasaje de mensajes los programas deben entender el cómputo y organizar la comunicación correctamente Punto a Punto Broadcast (1 a todos) Reduce (todos a 1) All to all Scatter (1 a varios) Gather (varios a 1) Point-to-Point Método básico de comunicación entre 2 procesos El procesador origen envía un mensaje al procesador destino El procesador destino recibe el mensaje y lo procesa El mensaje generalmente incluye Datos Longitud del mensaje Dirección destino y posiblemente un tag Synchronous vs. Asynchronous Synchronous No utiliza buffers El receptor notifica cuando recibe el mensaje El proceso que envía espera a que el destino reciba todo el mensaje tiempo tiempo send() Solicita envío Acknowledgement send() Mensaje recv() recv() Synchronous vs. Asynchronous Asynchronous No existe confirmación Un usa buffers Blocking vs. Non-Blocking Blocking = Sync && Non-Blocking = Async MPI son diferentes tiempo send() recv() Blocking vs. Non-Blocking No Bloqueante Permite ejecutar la próxima instrucción sin importar que la rutina send() haya sido localmente completada Es responsabilidad del programador que los datos que se envían no sean modificados en las próximas instrucciones Fuente de Deadlock - Bloqueante Si no hay suficiente capacidad en el buffer, las operaciones send esperan hasta que haya suficiente espacio disponible Qué sucede con este código: Depende del tamaño del buffer Soluciones Incrementar el tamaño del buffer Ordenar las operaciones send y recv en forma más cuidadosa Broadcast Un proceso envía la misma información a otros procesadores Se identifican los procesos que van a participar en el broadcast Mediante un nombre de grupo que es usado como parámetro por la función de broadcast El proceso que hace broadcast también puede recibir el mensaje Incluye send+sync Broadcast Si queremos paralelizar: Scatter Envía un conjunto de elementos de un arreglo desde el procesador origen a un conjunto de procesos Process 0 Process 1 dato dato Process N dato Acción buf Código scatter scatter scatter Gather Un procesador recolecta los valores de otros procesadores Se utiliza luego de realizar cómputos en forma paralela Process 0 Process 1 dato dato Process N dato Acción buf Código scatter scatter scatter Gather - Ejemplo Processor 0..N { … int r = rand(); int k = 0; int buff[N]; Gatther( 0, &buff, r, pid ); for (i=0;i<N;i++) k+=buff[i]; … } P0 P1 P2 P3 P4 4 7 8 19 30 11 K= 433 27 8 3 P5 3 r Reduction Cada procesador comienza con un valor y necesita conocer la suma de los valores almacenados en otros procesadores Un “reduction” combina datos de todos los procesadores y los retorna a través de una operación Algunas operaciones que se pueden aplicar son: ADD, OR, AND, MAX, MIN, etc. Ningún procesador puede finalizar un “reduction” antes que los otros procesadores hayan contribuido con sus valores Ejemplo Ejemplo en Paralelo Paralelismo de datos Paralelismo de tareas y Diseño de software paralelo Paralelismo de Datos Se caracteriza por la ejecución paralela de las mismas operaciones sobre diferentes partes de un conjunto de datos suficientemente grande Tipos de paralelismo de datos: SPMD y MPMD Paralelismo de Datos Es simple de programar Existe una única estructura de control o ejecución El paralelismo aparece via pasos de paralelismo de datos Forma de particionar los datos Paralelismo de Datos for all i: A[i]=2*A[i] P0 4 2 P1 8 7 1 P2 0 4 5 3 4 8 1 Paralelismo de datos 8 4 16 14 2 0 8 10 6 8 16 2 for all i>0: A[i]=A[i]+A[i-1] 8 12 20 30 16 2 8 Suma = 0…. 18 16 14 24 18 Paralelismo de datos secuencial Paralelismo de Tareas o funcional Ejecuta diferentes operaciones sobre los mismos o diferentes datos Se enfoca en la división de tareas Paralelismo de Tareas v = alpha(); w = beta(); x = gamma(v, w); y = delta(); printf ("%6.2f\n", epsilon(x,y)); alpha Se puede ejecutar alpha, beta, Y gamma, delta en paralelo. beta gamma delta INDEPENDENCIA DE DATOS epsilon Ejemplo 2 Procesamiento de Señales: Un conjunto signal-data de audio se transmite a través de tres filtros computacionales distintos. Cada filtro es un proceso independiente. El primer segmento de datos debe pasar por el primer filtro antes de pasar al segundo. Cuando lo hace, el segundo segmento de datos pasa por el primer filtro. En el momento en el tercer segmento de datos se encuentra en el primer filtro, las tres tareas están ocupadas. Divide y Vencerás Es un método elegante para resolver problemas Se divide el problema en problemas más pequeños, luego se resuelven las partes en forma separada y se combinan los resultados parciales para obtener el resultado final Se utiliza recursivamente para dividir el problema hasta alcanzar un punto en el que el problema es trivial X0 …. Xn/m-1 xn/m…. X2n/m-1 …. suma x(m-1)n/m-1….. Xn-1 Divide y Vencerás MASTER s = n/m; //cantidad de elementos para los slaves for ( i=0, x=0; i<m; i++, x = x+s ) send( &numbers[x], s, Pi ); //envía s números al slave Pi result = 0; for( i=0;i<m; i++ ) recv( &part_sum, Pany); sum += part_sum; //Espera resultados de los slaves //Acumula suma parciales SLAVE recv( numbers, s, Pmaster ); part_sum = numbers[0]; for( i=0; i< s; i++ ) part_sum += numbers[i]; send( &part_sum, Pmaster ); //Recibe s números del master //Realiza la suma //Envía resultados Con broadcast? Divide y Vencerás: Análisis El algoritmo secuencial requiere n sumas, con una complejidad de O(n). El algoritmo paralelo utiliza m+1 procesos. Dividimos el análisis en etapa de cómputo y comunicación Etapa 1: Comunicación: m slaves leen n/m números con un costo de tcomm1=m*(tstartup+(n/m)tdata) Etapa 2: Cómputo: Los procesos esclavos suman n/m números requiriendo n/m sumas. tcomp1 = n/m Divide y Vencerás: Análisis Etapa 3: Comunicación: Se envían los resultados parciales de las sumas tcomm2 = m(tstartup+tdata) Etapa 4: Cómputo: Suma final de m sumas parciales tcomp2 = m; Costo total tp = tcomm1 + tcomm2 + tcomp1 + tcomp2 = m(tstartup+(n/m)tdata)+m(tstartup+tdata) + n/m + m = 2mtstartup+(n+m) tdata + n/m + m Divide y Vencerás: Recursivo Esta formulación crea un árbol binario de procesos Al inicio, el problema raíz del árbol toma un problema y lo divide en dos partes más chicas La división del problema continúa hasta que las hojas del árbol han recibido los problemas más básicos Cada hoja retorna la solución de su problema al nodo padre Cada proceso padre combina y/o aplica alguna operación a la solución entregada por los nodos hijos Eventualmente la raíz genera la solución al problema original Divide y Vencerás: Recursivo add( int *s) { if( numbers(s)<= 2 ) return (s[0] + s[1]); else { Divide (s, s1, s2 ); //Divide s en dos grupos part_sum1 = add(s1); part_sum2 = add(s2); return( part_sum1 + part_sum2); } Problema inicial } Divide el problema Tareas finales Divide y Vencerás: Implementación Paralela En una implementación secuencial se visita un nodo del árbol por vez Una implementación paralela permite visitar varios nodos simultáneamente Si se asigna un proceso a cada nodo P0 Cada proceso solo P1 estará activo en un nivel del árbol P3 2m sub-tareas 2m+1-1 procesos P2 P4 P5 P6 Divide y Vencerás: Implementación Paralela una solución mas eficiente es re-usar los procesos existentes P0 P0 P4 P0 P0 P2 P1 P2 P4 P3 P4 P6 P5 P6 P7 También se puede aplicar a tareas que son divididas en más de dos partes Bucket sort: Secuencial La mayoría de los algoritmos de ordenación secuencial se basan en las operaciones de “comparar” e “intercambiar” Bucket sort: Es un algoritmo de partición Funciona bien si los números se distribuyen uniformemente en un intervalo [0..a-1] El intervalo se divide en m regiones denominadas buckets Para ubicar un número en un bucket, i/m y usar el resultados para identificar el bucket -------------------------------------------- buckets sort merge Bucket sort: Secuencial Algoritmo secuencial: Se divide el número i por el número de buckets y me da el identificador del bucket donde lo debería ubicar. Es decir los números se distribuyen uniformemente en buckets. Si los números están uniformemente distribuidos, en cada bucket hay n/m números. Eso permite balancear la carga de trabajo asignada a cada bucket. luego se ordenan en forma secuencial los números de cada buckets y finalmente se aplica una función de merge -------------------------------------------- buckets sort merge Bucket sort: Paralelo Alternativa 1: Asignar un bucket a cada procesador (todos los procesadores mantienen el arreglo de números desordenados) Números desordenados -------------------------------------------- Buckets grandes sort merge Bucket sort: Paralelo Alternativa 2: Se divide la secuencia en m regiones, una para cada procesador. Cada procesador genera buckets más pequeños y distribuye los números en esos buckets. Estos buckets pequeños se vacían en P buckets mas grandes para ser ordenados. Para ello c/procesador envía un bucket chico a c/u de los otros procesadores (bucket i al procesador i) Números desordenados -------------------------------------------procesadores Buckets mas pequeños Buckets grandes sort merge D&V Paralelo Tipos de Algoritmos (Input-Output) Los problemas se pueden clasificar según sus datos de entrada y salida Común-Común (CC): las variables paralelas de entrada y salida son comunes (replicadas) a todos los procesadores del grupo Común-Privado (CP): las variables paralelas de entrada son comunes (replicadas) pero las variables resultado quedan privadas en cada procesador Privada-Común (PC): las variables paralelas de entrada son privadas de cada procesador y las variables resultado son replicadas Privada-Privada (PP): las variables de entrada y salida son privadas Caso de estudio: Algoritmo shortest path Un grafo G(V,E), donde V es el conjunto de vértices y E es el conjunto de arcos conectando vértices en V. En un grafo dirigido cada arco tiene una dirección Un grafo puede ser representado como una matriz de adyacencia Aij = 0 si no hay un arco que une i con j Aij = 1 si hay un arco que une i con j Caso de estudio: Algoritmo shortest path Es un problema importante en la teoría de grafos y tiene gran aplicación en problemas de comunicación y otros problemas computacionales El problema involucra encontrar el paso más corto entre todos los pares de vértices 0 3 1 2 0 1 2 3 0 1 2 3 0 1 0 0 0 0 1 1 0 0 0 0 1 0 1 0 Matriz de Adyacencia: dice si hay un arco que une 2 vértices Algoritmo shortest path La idea es determinar si el paso desde el vértice vi al vértice vj vía vk, es más corto que el menor paso conocido vk vi vj Algoritmo shortest path El paso desde el vértice vi la vértice vj es la secuencia de arcos que los une y ningún arco se repite 2 veces. El problema requiere que encontremos el camino mas corto El algoritmo toma como entrada una matriz de adyacencia A (NxN) y computa una matriz S(NxN) Sij tendrá la longitud del paso más corto entre vi y vj, o -1 Algoritmo shortest path El algoritmo obtiene la matriz S en N pasos, construyendo en cada paso k una matriz intermedia I(k) que contiene el paso más corto conocido entre cada par de vértices. Inicialmente I(0) contiene la longitud entre (vi,vj), si existe un arco, de lo contrario será 0. Durante el k-ésimo paso se evalúa nuevamente Iij para determinar si: El mejor paso conocido entre vi, vj es más largo que las longitudes combinadas desde vi a vk y desde vk a vj. De ser así se actualiza Iij Algoritmo shortest path Algoritmo shortest path: Paralelo Repartir las filas en P procesadores en forma consecutiva J=0; for (i=0;i<P;i++) { for (;j< (N/P); j++) send(i, fila j); if(i==(P-1)) send(i, fila j+1) } Asigna un bloque contiguo a cada procesador Г Cada tarea es root de al menos un broadcast Los datos requeridos en el késimo paso no le pertenecen al procesador Computación Pipelined Pipelined T0 T1 T2 T3 El problema se divide en tareas que se completan una después de otra. En la programación paralela cada tarea se ejecuta en un procesador diferente Cada etapa contribuye a resolver el problema y pasa información necesaria para la siguiente etapa Pipelined Es posible incrementar la velocidad de ejecución: 1. 2. 3. Si se ejecuta mas de una instancia del problema Si una serie de datos se procesan con múltiples operaciones Si los datos pueden ser enviados a las siguientes etapas antes que la etapa actual finalice Pipelined Si se ejecuta mas de una instancia del problema procesos 1. P4 Inst.1 Inst.2 P3 Inst.1 Inst.2 Inst.3 P2 Inst.1 Inst.2 Inst.3 Inst.4 P1 Inst.1 Inst.2 Inst.3 Inst.4 Inst.5 tiempo Se utiliza para ejecutar simulaciones con diferentes parametros para obtener resultados comparativos. Pipelined 1. Si una serie de datos se procesan con múltiples operaciones Elementos de vector 2. d4 P0 P1 P0 P1 P2 P1 P2 P3 P1 P2 tiempo P3 P4 d3 d2 d1 P0 P0 Cada proceso realiza una operación diferente sobre los elementos Se utiliza para cálculos aritméticos, como multiplicar elementos de vectores 1. 2. 3. Pipelined Si los datos pueden ser enviados a las siguientes etapas antes que la etapa actual finalice P4 Se transfiere suficiente inf. para comenzar la siguiente etapa P4 P3 P3 P2 P2 P1 P1 P0 P0 tiempo tiempo Ejemplo Problema: Sumar una lista de números almacenados en diferentes procesadores ∑ P1 ∑ ∑ P2 P3 ∑ P4 recv( Pi-1, acumulation ); accumulation += number; send( Pi+1, accumulation) P5 Tipos de Paralelismos Threads La estructura más común para especificar procesos concurrentes es FORK-JOIN Programa principal FORK FORK FORK JOIN JOIN JOIN Sistema de memoria compartida. Threads IP Son rutinas livianas que comparten la misma zona de memoria y las variables globales code Heap IP code Heap stack threads Rutinas de Interrupción stack IP Files Files stack Proceso Rutinas de Interrupción Threads La creación de threads es 3 ordenes de magnitud menor a la creación de un proceso La sync de los threads se puede realizar más eficientemente que la sincronización de procesos Threads: Ejemplo Threads: memoria compartida Instrucción x = x+1; Proceso 1 read x compute x+1 write x tiempo Proceso 2 read x compute x+1 write x Variable x write write read read +1 +1 Proceso 1 Proceso 2 Secciones Críticas - Locks El problema de variable compartida puede ser generalizada a cualquier recurso. Por ej. I/O dispositivos Locks Locks: son variables de 1 bit que se coloca en 1 para indicar que el proceso ha ingresado a la sección critica. Y se coloca en 0 para indicar que no hay procesos en la sección critica. Estos mecanismos generalmente se implementan por hardware. Pthread El programa principal es un thread. Un thread separado puede ser creado y destruido utilizando: pthread_t thread1; pthread_create( &thread1, NULL, (void*)proc1, (void*) &arg); pthread_join( thread1, void *status); thread1 Main program pthread_create( &thread1, NULL, (void*)proc1, (void*) &arg); pthread_join( thread1, void *status); proc1(&arg) { …. return(*status); } Pthread #include <pthread.h> pthread_cancel(&thread1) for (j=0;j<NUM_THREADS;j++) Detached thread: threads que se crean y se terminan sin la necesidad de usarifun(threads[j] join se llaman!= thread tid)detached pthread_cancel(threads[j]); pthread_self pthread_mutex_t mutex1; void print_it(void *arg) pthread_mutex_init(&mutex1,NULL); { pthread_mutex_destroy(&mutex1); pthread_t tid; /* get the calling thread's ID */ tid = pthread_self(); pthread_mutex_lock( mutex1 ); printf("Thread %d \n", tid); pthread_mutex_unlock( mutex1 ); } Pthread Destroying Mutexes struct obj thread_mutex_t foo_mutex; { void foo() pthread_mutex_t om; { int refcnt; ... pthread_mutex_init(&foo_mutex,NULL); }; obj_done(struct obj *op) pthread_mutex_lock(&foo_mutex); /* Do{work. */ pthread_mutex_lock(&op->om); x = x+1; if (--op->refcnt == 0) pthread_mutex_unlock(&foo_mutex); { } pthread_mutex_unlock(&op->om); pthread_mutex_destroy(&op->om); free(op); } else pthread_mutex_unlock(&op->om); } Hardware? Todas las computadoras modernas tienen cache Hyperthreading. Cada núcleo dispone ahora de dos hilos de procesamiento, con lo que el sistema ve el microprocesador como si tuviera ocho núcleos en vez de cuatro L2: 8Mb Hardware • Si un therad en un core requiere un dato se trasfiere desde la memoria hasta la cache del core (se hace una copia). • Posteriormente cuando el thread quiere obtener el dato accede a su cache. • Si otro thread ubicado en otro core quiere el mismo dato, se hace una nueva copia del dato en la cache del nuevo core que lo requiere. (hay 2 copias). • El problema surge cuando un thread modifica el contenido del dato. Luego se debe usar un protocolo de coherencia para asegurar la correctitud de los datos. Hardware Protocolo de coherencia de cache Política de actualización: se actualiza todas las copias del dato en todas las cachés Política de invalidar (más común): cuando se modifica una copia en una caché, las otras copias son invalidadas utilizando un bit de validación. Hardware La caché se organiza en bloques de direcciones continuas caché Code: a+=2; b+=2; c+=a+b; bloques ab c RAM False sharing: un bloque puede ser pedido por varios threads, pero cada thread accede a datos diferentes. Cuando se actualiza un dato partes de las copias del bloque en otros cores deben ser actualizados o invalidados False-sharing RAM bloque Address tag 7 6 5 4 3 2 1 0 cache cache Core 1 Bloque en cache Core 2 Ejemplo sum int sum, a[1000]; sum = 0; for( i=0;i<1000; i++) sum += a[i]; a ------------ Solución 1 Proceso 1 sum1 = 0; for( i=0; i<1000; i+=2 ) sum1 += a[i]; sum += sum1 Proceso 2 sum2 = 0; for( i=1; i<1000; i+=2 ) sum2 += a[i]; sum += sum2 Ejemplo sum N procesos tomando números de a[] a ------------ global_index int a[array_size]; int global_index; int sum = 0; pthread_mutex_t mutex1; Int nth = 10; main() { int i; pthread_t thread[nth]; for( i=0; i<array_size; i++ ) a[i] = i+1; for(i=0;i< nth; i++ ) pthread_create(&thread[i],NULL, slave, NULL); for(i=0; i<nth; i++ ) pthread_join(thread[i],NULL); } Void *slave( void *ignorado) { int local_index, partial_sum = 0; do { pthread_mutex_lock(&mutex1); local_index = global_index; global_index++; pthread_mutex_unlock(&mutex1); if (local_index < array_size) partial_sum += a[local_index]; }while(local_index < array_size ) pthread_mutex_lock(&mutex1); sum += partial_sum; pthread_mutex_unlock(&mutex1); } OpenMP #include <omp.h> g++-4.2 main.cc -o main.out –fopenmp Las directivas se especifican con #pragma #pragma omp directive-name[clause[,] clause]... Al final de una región paralela hay una sincronización implícita. Solo el thread master continúa la ejecución. Cuando un thread encuentra un constructor paralelo, un grupo de threads es creado OpenMP: clausuras Private (lista de variables) En esta la lista de variables puede ser separada por ",“ y para cada una de ellas se genera una copia en cada thread, esta copia no tiene relación con la original y no es inicializada a menos que se utilice firstprivate. Shared (lista de variables) En esta las variables de la lista son comunes a todos los threads y cada uno de ellos puede modificarla afectándola en forma global. Threadprivate (lista de variables) Hace que la lista sea privada a cada thread pero globales dentro de un thread. Reduction (operador:lista de variables) Realiza una operación de reducción sobre las variables que aparecen en la lista utilizando el operador/intrínseco especificado. El operador puede ser: +, *, -, &(and), |(or), ^(eqv), &&(neqv). El intrínseco puede ser: || (max), (min), (and), (or). if(expresión escalar): los threads se crean si al evaluar la expresión es verdadera. OpenMP int omp_get_threads_num(void) Devuelve el identificador del thread. int omp_get_num_threads(void) Devuelve cuántos threads se están utilizando int omp_get_num_procs(void) Devuelve el número de procesadores accesibles void omp_set_num_threads(int) Indica el número de threads a utilizar int omp_get_max_threads(void) Devuelve el máximo posible de threads OpenMP: Variables de entorno OMP_NUM_THREADS Indica el número de threads a usar. export OMP_NUM_THREADS=4 setenv OMP_NUM_THREADS 4 set OMP_NUM_THREADS=4 OpenMP: Ejemplo Si no quieren usar variables de entorno pueden tener el mismo efecto usando omp_set_num_threads(NT); OpenMP: Tipos de constructores DO / for – Comparten las iteraciones de un loop. Representa el “paralelismo de datos" SECTIONS – Divide el trabajo en secciones discretas. Cada sección se ejecuta por un thread. Paralelismo de tareas Ejemplo #pragma omp for[clause[,] clause]... { ciclo for } FOR SCHEDULE(type, chunck): Describe cómo se dividen las iteraciones de un loop entre los threads. STATIC: Las iteraciones se dividen en chunks y asignados estáticamente a los threads DYNAMIC: Las iteraciones se dividen en chunks y asignados dinámicamente a los threads. Cuando un thread termina un chunk se le asigna otro Ejemplo static #include <opm.h> #define SIZE 4 #define N 8 int main( ) { int i,chunk; float a[N], b[N], c[N]; for( i=0;i<N ;i++) a[i] = b[i] = i*1.0; chunck = SIZE; #pragma opm parallel shared(a,b,c) private(i) { #pragma opm schedule( static, chunk ) for( i=0;i<N;i++) c[i] = a[i]+ b[i]; } } Ejemplo SECTIONS SECTIONS / END SECTIONS Debe incluirse en una región paralela. Las secciones se dividen entre los “threads”. Cada “thread” ejecuta una sección diferente. Es posible realizar paralelismo a nivel de tarea. SECTION define cada una de las secciones. OpenMP SECTION OpenMP: Directivas #pragma omp single: sólo un thread ejecuta el código #pragma opm parallel shared(a,b,c) private(i) { BLOCK1 #pragma opm single { BLOCK2 } BLOCK3 } OpenMP: Directivas #pragma omp master: el código a continuación lo ejecuta solo el thread master #pragma opm parallel shared(a,b,c) private(i) { BLOCK1 #pragma opm master { BLOCK2 } BLOCK3 } OpenMP: Directivas #pragma omp barrier: barrera de sync. #pragma opm parallel shared(a,b,c) private(i) { BLOCK1 #pragma opm barrier BLOCK3 } OpenMP: Directivas #pragma omp critical: regiones críticas #pragma opm parallel shared(a,b,c) private(i) { BLOCK1 #pragma opm critical (zona1) { x = x+1; } BLOCK2 }