Sei sulla pagina 1di 15

Unidad I - Paralelismo y Arquitecturas Paralelas

¿Qué es Paralelismo?
¿Por qué Paralelizar?
Definiciones Básicas
Paralelismo de Control
Paralelismo de Datos:
Paralelismo de Flujo
Segmentación (Pipelining):

Unidad II - Jerarquía de Memoria


Introducción
Principio de Localidad:
Jerarquía de Memoria
¿Cómo funciona?
Terminología Básica
Rendimiento:
Clasificación:
¿Que bloque debe reemplazarse en un fallo de la caché?
¿Que ocurre en una escritura?
Comparación de estrategia de escritura en caché Write Back frente a Write Through
Las tres fuentes de fallos de la Caché

Unidad III - Paralelismo en Uniprocesadores


Paralelismo a nivel de instrucción (IPL)
Procesador Segmentado
Procesador Superescalar
Procesador Very Long Instruction Word (VLIW)
Paralelismo a nivel de threads
Multithreading de grano fino
Multithreading de grano grueso
Simultaneous Multithreading
Paralelismo a nivel de datos
Limitaciones en el rendimiento
Memoria:
Ley de Moore y consumo de los procesadores:
Medidas de rendimiento
Speedup:
Eficiencia:
Ley de Amdahl:
Ley de Gustafson:
Escalabilidad:
Tipo de Escalabilidad:

Unidad IV - Multiprocesadores
Clasificación

1
Arquitecturas paralelas SIMD
Arquitecturas Paralelas MIMD
Multiprocesadores
Clasificación:
UMA
NUMA
COMA
Conexiones típicas a memoria
Basadas en bus:
Basadas en crossbar:
Basadas en Multistage (MIN)
Clasificación de las Redes MIN
Consistencia de memoria
Modelo de Consistencia:
Consistencia estricta:
Consistencia secuencial:
Condiciones suficientes para preservar la consistencia secuencial:

2
Unidad I - Paralelismo y Arquitecturas Paralelas
¿Qué es Paralelismo?
Es una estrategia para ejecutar grandes tareas complejas, descomponiéndose en tareas más pequeñas
para ser ejecutadas simultáneamente, es decir, en paralelo. El paralelismo es hecho:
● Descomponiendo las tarea en tareas más pequeñas
● Asignando las tareas más pequeñas a múltiples trabajadores para trabajar simultáneamente
● Coordinando los trabajadores

¿Por qué Paralelizar?


El paralelismo está implícito en la mayoría de los procesadores modernos y los sistemas operativos. En
otras palabras, toda computadora moderna tiene algún grado de paralelismo, pero no toda computadora
la llamamos computadora paralela porque la concurrencia en estas máquinas está oculta al
programador.

Definiciones Básicas
Procesamiento paralelo: Es el procesamiento de información que enfatiza el manejo concurrente de
conjuntos de datos por varios procesadores con el objetivo de resolver un solo problema.
Computadora paralela: Es una computadora formada por varios procesadores capaz de realizar
procesamiento paralelo.
Fuentes de Paralelismo: Todas las aplicaciones no presentan el mismo perfil de cara al paralelismo.
Es necesario considerar el factor cualitativo: ¿Qué manera se utiliza para explotar el paralelismo? Cada
técnica de explotación del paralelismo se denomina fuente, estas son:
● Paralelismo de Control.
● Paralelismo de Datos.
● Paralelismo de Flujo

Paralelismo de Control
Constatación natural: en una aplicación existen acciones que podemos hacer simultáneamente. Las
acciones pueden ejecutarse de manera más o menos independiente sobre unos recursos de cálculos.
● Caso ideal: N acciones independientes se ejecutarán N veces más rápido sobre N elementos de
proceso que sobre uno solo.
● Caso Real: acciones dependientes. Existen dos clases de dependencia:
○ Dependencia de Control de Secuencia.
○ Dependencia de Control de Comunicación.
Paralelismo de Datos:
● Ciertas aplicaciones trabajan con estructuras de datos muy regulares (vectores, matrices)
repitiendo una misma acción sobre cada elemento de la estructura.
● Los recursos de cálculo se asocian a los datos.
● Centralización de control.
● Limitaciones:
○ Necesidad de dividir datos vectoriales para adecuarlos al tamaño soportado por la
máquina.
○ La existencia de datos escalares que limitan el rendimiento.
○ La existencia de operaciones de difusión (un escalar se reproduce varias veces
convirtiéndose en un vector) y β-reducciones que no son paralelas.

3
Paralelismo de Flujo
● Ciertas aplicaciones funcionan en modo cadena: flujo de datos (semejantes) ⇒ sucesión de
operaciones en cascada.
● Los resultados de las acciones efectuadas en el instante t pasan en el instante t + 1 al PE
siguiente.
● Segmentación o ​pipeline.​
● El flujo de datos puede provenir de dos fuentes:
○ Datos de tipo vectorial ubicados en memoria.
○ Datos de tipo escalar provenientes de un dispositivo de entrada.
● Ganancia obtenida ⇒ Núm. de etapas (núm. de PE).

Segmentación (Pipelining):
La segmentación consiste en dividir la ejecución de una tarea en etapas. De esta manera, una tarea se
completa cuando se han realizado todas las etapas en las que se subdivide de manera ordenada.
El pipelining es una técnica que permite aumentar el grado de concurrencia de un algoritmo.
Un ​algoritmo pipelined ​se divide en un número de pasos, llamados Pipe Stages o segmentos
(segmentación). Cada segmento trabaja a máxima velocidad en su etapa. La salida de un segmento
corresponde a la entrada de otro segmento.
Riesgos de la Segmentación​:
● Riesgos por dependencia de datos:​ Se debe a que una instrucción depende del resultado de
otra previa que todavía se encuentra en el cauce.
● Riesgos estructurales: ​Significa que el hardware no puede soportar la combinación de
instrucciones que se quieren ejecutar en el mismo ciclo, por ejemplo dos instrucciones
accediendo a la memoria.
● Riesgos de control:​ Se debe a la necesidad de tomar una decisión basada en los resultados de
una instrucción mientras las otras se están ejecutando, por ejemplo un salto condicional.

Unidad II - Jerarquía de Memoria


Introducción
Los programas comparten en la memoria tanto su código como sus datos. La Estrategia de optimización
de rendimiento: posibilitar a la CPU el acceso ilimitado y rápido tanto al código como a los datos. El
Inconveniente: tecnológicamente, cuanto más grandes son las memorias (más capacidad) más lentas y
costosas resultan.
Los programadores quieren memorias grandes y rápidas. El Problema es que no existe hoy en día una
memoria a la vez grande y rápida a un costo razonable. Entonces, crearemos la ilusión de una memoria
de las características deseadas combinando distintos tipos de memoria -> J​erarquía de memoria.

Principio de Localidad:
En un momento concreto, los programas acceden a una parte relativamente pequeña de su espacio de
direcciones.
● Localidad temporal: ​si se consulta un dato, seguramente será consultado
próximamente. Por ejemplo Un libro de la mesa será consultado varias veces.
Bucles: datos e instrucción.
● Localidad espacial: ​si se consulta un dato, seguramente otros datos cercanos
a él serán consultados próximamente.Por ejemplo, Traemos un libro del
estante -> los libros que están próximos versarán sobre el mismo tema.
Instrucciones: ejecución secuencial
Datos: estructuras de datos como tablas y arrays.

4
Jerarquía de Memoria

¿Cómo funciona?
Sacar provecho de la​ localidad temporal de un programa:​ mantener los datos accedidos más
recientemente cerca del procesador.
Sacar provecho de la ​localidad espacial de un programa:​ moviendo bloques de varios datos
contiguos a los niveles próximos al procesador.

Terminología Básica
Bloque: unidad mínima de información en una jerarquía de 2
niveles.
Acierto (hit): el elemento requerido por la CPU está en el nivel
superior.
Fallo (miss): el elemento requerido por la CPU no está en el
nivel superior ) Se accede al nivel inferior para recuperar el
bloque que contiene al elemento requerido.
Frecuencia o Tasa de aciertos (hit rate): porcentaje de
accesos a memoria encontrados en el nivel superior.
Frecuencia o Tasa de fallos (miss rate): ​(1 – tasa de
aciertos). Porcentaje de accesos a memoria no encontrados en
el nivel superior.
Tiempo de acierto: tiempo necesario para acceder al nivel superior de la memoria, incluyendo el
tiempo para determinar si el acceso es un acierto o un fallo.
Tiempo de fallo: tiempo necesario para sustituir un bloque de nivel superior por el correspondiente
bloque de nivel más bajo, más el tiempo necesario para proporcionar este bloque al dispositivo que lo
solicitó (generalmente la CPU.
Penalización por fallo:​ tiempo de fallo + tiempo de acierto

Rendimiento:
Usaremos el tiempo medio de accesos a memoria como medida de rendimiento de la jerarquía de
memoria.
Tiempo medio de accesos = Tiempo de acierto + Frecuencia de fallos * Penalización de fallo

5
Clasificación:
Usaremos términos que trascienden los niveles de los que estamos hablando y referirse a cualquier
nivel:
● Ubicación del bloque:​ ¿Dónde puede ubicarse un bloque en el nivel superior?
● Identificación del bloque:​ ¿Cómo se encuentra un bloque en el nivel superior?
● Sustitución de bloque:​ ¿Qué bloque debe reemplazarse en caso de fallo?
● Estrategia de escritura:​¿Qué ocurre en una escritura?

6
● Directa: Cada bloque de la memoria principal tiene su posición en la caché y siempre en el
mismo sitio. Su inconveniente es que cada bloque tiene asignada una posición fija en la
memoria caché y ante continuas referencias a palabras de dos bloques con la misma
localización en caché, hay continuos fallos habiendo sitio libre en la caché.
● Asociativa: Los bloques de la memoria principal se alojan en cualquier bloque de la memoria
caché, comprobando solamente la etiqueta para verificar acierto. Su principal inconveniente
es la cantidad de comparaciones que realiza.
● Asociativa por conjuntos: Cada bloque de la memoria principal tiene asignado un conjunto
de la caché, pero se puede ubicar en cualquiera de los bloques que pertenecen a dicho
conjunto. Ello permite mayor flexibilidad que la correspondencia directa y menor cantidad de
comparaciones que la totalmente asociativa.
¿Que bloque debe reemplazarse en un fallo de la caché?
Ante un fallo de cache es necesario traer un bloque nuevo y ubicarlo en algún lugar del nivel superior.
● Si existe algún bloque de cache con datos no válidos, el reemplazo se hace en ese lugar
● Debido a la alta frecuencia de acierto de cache es necesario tomar estrategia de reemplazo

7
Determina qué bloque de memoria caché debe abandonarla cuando no existe espacio disponible para un
bloque entrante. Básicamente hay cuatro políticas:
● Aleatoria​: el bloque es reemplazado de forma aleatoria.
● FIFO​: usa el algoritmo (primero en entrar primero en salir) para determinar qué bloque debe
abandonar la caché. Este algoritmo generalmente ​es poco eficiente​.
● Usado menos recientemente (​LRU)​ : Sustituye el bloque que hace más tiempo que no se ha
usado en la caché, traeremos a caché el bloque en cuestión y lo modificaremos ahí.
● Usado con menor frecuencia (​LFU)​ : Sustituye el bloque que ha experimentado menos
referencias.
¿Que ocurre en una escritura?
La escritura no es buena para las caches
● Exigen acceso a la memoria para actualizar los datos, por lo que es más lento
○ Un acierto en lectura no necesita acceso a memoria
● Afortunadamente, la escritura es 10 veces frecuente que la lectura(el código se lee, pero no se
escribe)
Dos estrategias básicas(POLITICA DE ACTUALIZACION)​:
● Write Througn(actualización inmediata): ​Se escribe a la vez en Memoria caché y Memoria
principal. ​Desventaja:​ genera cuello de botella.
● Write Back(Actualización postergada): ​La información se escribe solo en el bloque de la
caché. Se añade un bit de Modificación(dirty bit) a la caché:
○ Si está a 1, es porque se ha escrito en esa línea
○ En el momento en que haya que desalojar la línea de la caché, se comprueba el bit, y si
está a 1, se escribe la línea en memoria.
Comparación de estrategia de escritura en caché Write Back frente a Write Through
● Ventaja de Write Back: menos tráficos con memoria, puede cambiar varios datos en una línea
sin un costoso accesos a memoria. En general es frecuente modificar datos contiguos a unos
anteriormente modificado(localidad espacial), pero cuidado, aunque solo cambie un byte, tengo
que escribir la línea entera
● Inconvenientes de Write Back: ​Más compleja de implementar y Posibles inconsistencia cache -
MP. Algunos dispositivos pueden acceder a MP y leer un datos que está cambiado

8
Las tres fuentes de fallos de la Caché
Forzosos: el primer acceso a un bloque no está en la caché; así que el bloque debe ser traído. Estos
también se denominan fallos de arranque en frío o de primera referencia. Los fallos forzosos son
independientes de la caché.
Capacidad: s​ i la caché no puede contener todos los bloques necesarios durante la ejecución de un
programa, se presentarán fallos debido a los bloques que se descartan y luego se recuperan. Los fallos
de capacidad disminuyen cuando la capacidad aumenta.
Conflicto: si la estrategia de ubicación de bloques es ​asociativa por conjuntos o de
correspondencia directa,​ los fallos de conflicto ocurrirán, ya que se puede descartar un bloque y
posteriormente recuperarlo si a un conjunto le corresponden demasiados bloques. Estos fallos también
se denominan fallos de colisión. Los fallos de conflicto dependen de la asociatividad de la memoria: si
es totalmente asociativa no existen conflictos, en la de mapeo directo los conflictos aumentan hasta su
máximo posible.

Unidad III - Paralelismo en Uniprocesadores


Paralelismo a nivel de instrucción (IPL)

Procesador Segmentado
División de la ejecución de una instrucción en varias
etapas, donde ​cada etapa ​normalmente ​se realiza en un
ciclo de CPU​. Con la segmentación de la ejecución de las
instrucciones se consigue aumentar el IPC (Instructions
per cycle)
Podemos estar ejecutando en paralelo tantas
instrucciones como número de etapas tenga la ejecución
de las instrucciones, y después de rellenar todas las
etapas con tantas instrucciones como etapas haya en la
segmentación, conseguiremos finalizar una instrucción
por ciclo.

Procesador Superescalar
Los procesadores Superescalares aparecieron por primera vez en
1987, estos tipo de procesador tienen unidades funcionales duplicadas,
y por consiguiente, podía lanzar más de una instrucción a la vez. La
estructura típica de un procesador superescalar consta de un ​pipeline
con las siguientes etapas:
● Lectura (fetch). ● Lanzamiento (dispatch).
● Decodificación (decode). ● Ejecución (execute).
● Escritura (writeback)
El procesador maneja más de una instrucción en cada etapa. El
número máximo de instrucciones en una etapa se denomina grado, así un procesador superescalar de
grado 4 en lectura (fetch) es capaz de leer como máximo cuatro instrucciones por ciclo. Un procesador
superescalar suele tener unidades funcionales independientes de los tipos siguientes:
➢ Unidad aritmético lógica (ALU)
➢ Unidad de lectura/escritura en memoria (Load/Store Unit)
➢ Unidad de coma flotante (Floating Point Unit)
➢ Unidad de salto (Branch unit)
Un procesador superescalar es capaz de ejecutar más de una instrucción simultáneamente únicamente
si las instrucciones no presentan algún tipo de dependencia (hazard).

9
La dependencia es aquella que se produce cuando una instrucción necesita un operando que se está
calculando por una instrucción previa en el programa, y que está siendo ejecutada.
Nos podemos encontrar que el pipeline del procesador queda vacío durantes varios ciclos, o que
algunas de nuestras unidades funcionales no se están usando
durante un ciclo concreto. Estos casos se conocen, en inglés,
como vertical waste y horizontal waste respectivamente. La figura 3
muestra las cuatro vías (una por columna) de ejecución en un
procesador superescalar.
Cada fila representa los ciclos de ejecución en dicho procesador.
Cada recuadro por fila representa una unidad funcional, y que esté
coloreada significa que está ocupada. Como podemos observar, se
está desaprovechando del procesador tanto vertical como
horizontalmente (cuadrados no coloreados). De esta forma, que el
procesador superescalar no se está aprovechando al máximo.
Una opción alternativa es dejar al compilador el trabajo de buscar
las instrucciones que se pueden ejecutar a la vez en el procesador. El compilador organizará el binario
de tal forma que aquellas instrucciones que pueden ejecutarse a la vez estarán consecutivas en el
programa. Así, ​el procesador podrá leerlas de una vez, como si fuera una instrucción larga, formada por
varias instrucciones, y mandarlas a ejecutar sin tener que mirar si hay dependencia o no. ​Estos
procesadores se conocen como Very Long Instruction Word o VLIW

Procesador Very Long Instruction Word (VLIW)


Estos procesadores tienen la ventaja de ahorrarse el hardware dedicado a la reordenación en tiempo de
ejecución de las instrucciones, dependiendo mucho del tipo de optimizaciones que haga el compilador.
Normalmente también disponen de instrucciones dedicadas para controlar el flujo de ejecución del
programa.

Paralelismo a nivel de threads


El paralelismo a nivel de instrucción (ILP), se pierde en el momento en que nos encontramos con fallos
de acceso a los diferentes niveles de memoria caché. Esto es debido a que el procesador debe parar la
ejecución de nuevas instrucciones hasta que el dato (línea de caché) se reciba.
Una solución para conseguir que el procesador continúe ejecutando instrucciones es que pueda
ejecutar otro programa o thread, enmascarando esta situación de bloqueo.

Multithreading de grano fino


Este tipo de paralelismo a nivel de threads intenta ocultar los
bloqueos del procesador realizando una ejecución en round
robin (política de asignación de ciclos del procesador en el que
se le dan un número fijo de ciclos a un thread detrás de otro.) de
las instrucciones de threads diferentes, en ciclos consecutivos.
El procesador cambia de hilo en cada instrucción. La ejecución
de los hilos se hace de manera intercalada, el cambio de hilo se
hace siguiendo una distribución round robin.
En los casos en los que un hilo se encuentra bloqueado, por
ejemplo esperando datos de memoria, se salta y se pasa al
siguiente.
La ventaja de esta opción es que puede amortiguar bloqueos
cortos y largos de los hilos que se están ejecutando, dado que
en cada ciclo se cambia de hilo. La desventaja principal es que
se reduce el rendimiento de los hilos de manera individual.
10
Multithreading de grano grueso
Los cambios de hilos se hacen cuando en el hilo que está
corriendo sucede un bloqueo de larga duración. Un ejemplo
de este tipo de bloqueo es un error en la memoria caché de
último nivel, saltos, conflicto de datos, esto implicaría
muchos ciclos de bloqueo.
La ventaja de esta opción es que no se reduce el
rendimiento del hilo individual, ya que solo cambia de hilo
cuando este queda bloqueado por un acontecimiento que
requiere una larga latencia para ser procesado. La
desventaja es que no es capaz de sacar rendimiento
cuando los bloqueos son más cortos.
Con esta estrategia de dejar ejecutar más de un ciclo a un
thread no son necesarios tantos threads activos como
pasaba con el multithreading de grano fino con tal de
aprovechar al máximo el procesador.
En contrapartida, como siempre se espera a que haya un
bloqueo para cambiar de thread, los ciclos que se necesiten
para darse cuenta del bloqueo y cambiar de thread se perderán. En cualquier caso, si tenemos un
número suficiente de threads activos, podremos conseguir mejor rendimiento que con el grano fino.

Simultaneous Multithreading
Los procesadores con Simultaneous Multithreading reducen el desaprovechamiento horizontal de las
unidades funcionales, permitiendo que dos threads diferentes puedan ejecutarse en el mismo ciclo de
procesador.
Se puede considerar como un refinamiento del grano grueso, de
tal forma que si en un ciclo del procesador una instrucción de un
thread se bloquea, una instrucción de otro thread se puede usar
para mantener el procesador y todas sus unidades funcionales
ocupadas.
Esto quiere decir que diferentes hilos están compartiendo en un
mismo instante de tiempo diferentes recursos del procesador.
También es posible que una instrucción de un thread pudiera
quedar bloqueada porque hay un número limitado de unidades
funcionales de un tipo. En este caso también se podría tomar una
instrucción de otro thread.
La ​figura 7 ​muestra un ejemplo de ejecución de 4 threads en un
sistema Simultaneous Multithreading. En este caso, en cada ciclo
de ejecución puede haber instrucciones de diferentes threads.
E​l primer procesador que incorpora el Simultaneous
Multithreading, conocido como hyperthreading, fue el Pentium 4.
Para el sistema operativo, un procesador con Simultaneous Multithreading es como un procesador con
dos cores, que comparten caché y memoria.

Paralelismo a nivel de datos


El paralelismo a nivel de datos se refieren básicamente a la posibilidad de operar sobre dos o más datos
con una única instrucción. Un uniprocesador con instrucciones SIMD (Single Instruction Multiple Data)
es capaz de realizar una misma operación, en paralelo, con más de un dato a la vez.

11
El soporte hardware necesario para poder ejecutar instrucciones SIMD incluye tener registros más
grandes, buses hacia memoria que permitan el acceso a datos del tamaño de los registros, y unidades
funcionales que soportan operar con más de un dato a la vez.
Para explotar este tipo de paralelismo se necesita que el compilador y/o el programador utilice las
instrucciones SIMD.
Las primeras en aparecer fueron las conocidas como MMX (Intel). Estas instrucciones solo operaban
con enteros de 32 bits, no soportando instrucciones de tipo float.

Limitaciones en el rendimiento
Hay dos factores que limitan el rendimiento final que se puede obtener con los procesadores. Uno de
ellos es el acceso a memoria y el otro es el consumo energético y la ley de Moore.
Memoria:
La diferencia entre la velocidad a la que el procesador puede procesar datos y la que la memoria puede
suministrar estos datos es un factor que limita el rendimiento potencial de las aplicaciones. En concreto,
tanto la latencia como el ancho de banda de acceso a memoria pueden influir en el rendimiento de los
programas.
Latencia:​ es el tiempo necesario para obtener un dato de memoria.
Ancho de banda: cantidad de bytes que la memoria puede suministrar por unidad de tiempo
(segundos).
Ley de Moore y consumo de los procesadores:
Moore ​afirmó que la tecnología tenía futuro, que el número de transistores por unidad de superficie en
circuitos integrados se duplica cada año y que la tendencia continuaría durante las siguientes dos décadas​.
Y más tarde, hizo una revisión del ratio de aumento en la complejidad de los circuitos. En esta ocasión,
Moore predijo que esta complejidad se aumentaría cada 18 meses. Esta curva es la que conocemos
como ley de Moore.
Sin embargo, aunque el número de transistores que podemos integrar en un circuito aumenta cada año,
hay un consumo energético máximo que el chip puede soportar. De hecho, se llegó a un punto en que
este consumo comenzaba a afectar negativamente al rendimiento de las aplicaciones.
Esta pérdida de rendimiento hizo que, a partir del año 2000 aproximadamente, los diseñadores de
procesadores empezaron a optar por no intentar incrementar su velocidad(Mhz o Ghz), y en cambio,
aumentar el número de cores dentro de un chip (multicores)

Medidas de rendimiento
Speedup:
El speedup conseguido por un programa paralelo con respecto al programa en secuencial
se define como:
Podemos definir speedup como la
relativa reducción de tiempo de ejecución, al procesar
un tamaño fijo de datos cuando usamos P
procesadores, con respecto al tiempo de ejecución del
programa secuencial.
La curva del speedup debería ser idealmente una
función lineal de pendiente 1, es decir, que fuéramos P
veces más rápido con P procesadores.
La figura muestra las tres situaciones con las que nos
podemos encontrar cuando hacemos un programa
paralelo y analizamos el speedup.
La ​situación normal es que nuestros programas, una
vez paralelizados, consigan un speedup menor que el lineal, ya que los costes de comunicación,
12
sincronización y creación/destrucción de tareas suelen hacer que el coste de paralelización no sea a
coste cero.
Sin embargo, se pueden dar casos en los que obtenemos un speedup ​superior al lineal​. Este es el
caso de programas paralelos que ayudan a explotar la jerarquía de memoria, o los registros del
procesador, cosa que antes no se podía con el programa secuencial.
Eficiencia:
La eficiencia se define como la medida de la fracción de tiempo en la que cada
procesador es usado para resolver el problema en cuestión de forma útil. Si nuestros
procesadores se utilizan de forma eficiente, eso significa que el tiempo dedicado por
cada procesador por el número de procesadores debería ser T1.
Ley de Amdahl:
En la paralelización de una aplicación, normalmente hay alguna parte que no se puede paralelizar. Esta
fracción de tiempo invertida en esta parte secuencial va a limitar la mejora de rendimiento que vamos a
obtener de la paralelización, y por consiguiente, la eficiencia obtenida.
En la figura mostramos el tiempo que tardamos en un código totalmente ejecutado en secuencial
(izquierda) y un código en el que hay una parte, definida como paralela, que se ha paralelizado
perfectamente entre 5 procesadores. Si calculamos el speedup que podemos conseguir, obtenemos que
SP = 100/60 = 1,67, aun habiendo conseguido una eficiencia de 1 en parte paralela del programa.

La ley de Amdahl ​indica que la mejora de rendimiento está limitada por la fracción de tiempo que
el programa está ejecutándose en paralelo​. Si llamamos a esta fracción, tendríamos que:

De tal forma que si el número de procesadores tiende a infinito, el speedup máximo que se podrá
obtener únicamente dependerá de la fracción que no se puede paralelizar:

Ley de Gustafson:
La ley de Gustafson establece que cualquier problema suficientemente grande puede ser eficientemente
paralelizado.
S(P) = P – a . (P – 1)
donde ​P​ es el número de procesadores, ​S​ es el speedup y ​a​ la parte no paralelizable del proceso.
La ley de Gustafson aborda las limitaciones de la Ley de Amdahl, la cual no escala la disponibilidad del
poder de cómputo a medida que el número de máquinas aumenta.

13
La ley de Gustafson propone que los programadores establezca el tamaño de los problemas para
utilizar el equipamiento disponible en su solución en un tiempo práctico.
Es decir que al aumentar el número de procesadores no me a mejorar los tiempo de cómputos (Ley de
amdahl) pero al hacerlo me proporciona resultados más preciso de cálculos más complejos.
Por consiguiente, si existe equipamiento más rápido disponible, mayores problemas se pondrán resolver
en el mismo tiempo.
Escalabilidad:
Es una medida de cómo se comporta un programa paralelo cuando aumentamos el tamaño del
problema proporcionalmente al número de procesadores, o aumentamos el número de
procesadores sin variar el tamaño del problema a tratar.

Tipo de Escalabilidad:
● Escalabilidad fuerte: El objetivo es resolver un problema lo más rápidamente posible. El workload
de la aplicación se mantiene constante Se incrementa el número de procesos.
● Escalabilidad débil: Incrementa el tamaño del workload proporcionalmente al número de
procesos. La carga de trabajo por el proceso se mantiene constante.

Unidad IV - Multiprocesadores
Consistencia de memoria
Cuando varios procesadores acceden a un dato para escritura se nos plantea la pregunta siguiente: ¿en
qué orden se deben ver estas escrituras por parte del resto de procesadores si éstos hacen una lectura
de los datos de forma paralela?
Hay muchas respuestas posibles, ya que unos accesos se pueden adelantar a los otros, siendo el
resultado de las lecturas impredecible. Todo esto se agrava cuando tenemos cachés, ya que entonces
es posible que varios procesadores tengan copias de lecturas previas.
Estos conflictos suceden sólo cuando la memoria es compartida.
Ante la escritura de un programa paralelo se quiere asegurar que una lectura devuelve el valor de una
determinada escritura. Es decir, establecer un orden entre una escritura y una lectura.
Normalmente se usa alguna forma de evento de sincronización para asegurar esta dependencia.
El acceso a la memoria compartida puede ser visto como un contrato entre el hardware y el software de
memoria. Si el software acepta cumplir con ciertas reglas, la memoria conviene en entregar ciertos
resultados. La discusión se centra en la naturaleza de las reglas. Estas reglas se denominan “modelos
de consistencia”.
14
Modelo de Consistencia:
Es el conjunto de normas que nos indica qué valores deberían ver los procesadores en sucesivas
lecturas de una o varias posiciones de memoria. ​“Un modelo de consistencia de memoria especifica
cómo se resuelven los conflictos de acceso a la memoria”.
A continuación detallaremos algunos de los modelos de consistencia conocidos:
● Consistencia estricta
● Consistencia secuencial
Consistencia estricta:
"Cualquier lectura de una posición de memoria X devuelve el valor de la escritura más de X".
En la práctica este modelo es imposible de implementar de otra manera que no sea tener un solo
módulo de memoria que atiende todas las solicitudes bajo un régimen primero llegar primero se atiende,
sin uso de cachés ni repetición de datos.
Una implementación así convertir a la memoria en un enorme cuello de botella y por ello este modelo no
es recomendable.
Consistencia secuencial:
Un sistema multiprocesador es secuencialmente consistente si el resultado de cualquier ejecución es el
mismo que se obtendría si las operaciones de todos los procesadores se ejecutase en algún orden
secuencial, y las operaciones de cada procesador individual aparecen en esa secuencia en el orden
indicado por el programa.
En presencia de múltiples solicitudes de lectura y escritura, el hardware escoge (de forma no
determinista) cierta intercalación de todas las solicitudes, pero todas las CPU perciben el mismo orden.

Un multiprocesador es secuencialmente consistente si el resultado de cualquier ejecución es la misma


que si las operaciones de todos los procesadores se realizarán en algún orden secuencial, y las
operaciones de cada procesador individual ocurren en esta secuencia en el orden especificado por el
programa.
“No es importante en qué orden se emiten realmente las operaciones a la memoria o incluso cuándo se
completan, lo que importa es que parezcan completarse en un orden que no viole la consistencia
secuencial”
Existen dos restricciones para la implementación de la consistencia secuencial.
● La primera es que las operaciones de memoria de un proceso se hagan visibles en el orden del
programa.
● El segundo requisito es que las operaciones aparezcan atómicas; es decir, que parezca que una
operación se ha completado con respecto a todos los procesos antes que la siguiente operación
en el orden total haya sido emitida.
Condiciones suficientes para preservar la consistencia secuencial:
1. Todo proceso emite las peticiones a la memoria en el orden especificado por el programa.
2. El proceso que la emitió espera ha que se haya realizado la escritura antes de emitir su siguiente
operación.
3. El proceso que la emitió espera a que la lectura se haya completado, y también espera a que se
complete la escritura que generó el valor devuelto por la lectura, antes de emitir su siguiente
operación.

15

Potrebbero piacerti anche