Problemas de examen resueltos
Teoría: Tema 1 - Evaluación de las prestaciones · Tema 2 - Diseño de algoritmos paralelos · Aula: Problemas de aula resueltos
Soluciones propias, no oficiales
El boletín solo trae enunciados. Estas resoluciones las he hecho yo siguiendo el método de clase (el de Problemas de aula resueltos). Cuando el enunciado es ambiguo, lo digo y explico qué supuesto tomo. Úsalas para ver cómo se plantea cada problema; si en clase sale algo distinto, manda lo de clase.
Patrones que se repiten
- Dependencias en una sola dirección (vertical, horizontal, diagonal) → agrupa a lo largo de esa dirección y no hay comunicaciones.
- Reducción global (suma, media, máximo, norma, contador) → árbol o hipercubo: .
- Repartir/recoger datos desde un nodo con P2P en serie → aparece en y casi siempre no escala.
- atado a (p. ej. en una matriz ) → aunque , el problema debe crecer como .
- Trabajo triangular (eliminación, Gram-Schmidt) → bloques consecutivos desequilibran; distribución cíclica.
Problema 28 (oct. 2025)
Producto de matrices con , , memoria distribuida. Cada procesador tiene los mismos índices de , y . Allgather (recursive doubling): .
Supuesto
En la fórmula del allgather, es el tamaño total de lo que se reúne (por eso el término es ): recibir datos no puede costar menos de .
1. Descomposición del dominio, centrada en la salida. Una tarea por elemento : FLOPs.
2. Comunicaciones. La tarea tiene y , pero necesita toda la fila de y toda la columna de :
- allgather de entre las tareas de cada fila;
- allgather de entre las tareas de cada columna.
Comunicaciones globales (dentro de cada fila/columna), estáticas y regulares.
3. Agrupación por filas. Cada procesador tiene filas consecutivas de , y ().
- La fila de ya es local: el allgather de desaparece (se ha agrupado en esa dirección).
- Para calcular sus filas de necesita toda : un allgather de entre los procesadores, en el que cada uno aporta sus datos y todos acaban con los .
4. Asignación: estática, un bloque de filas por procesador; balanceada porque todas las filas cuestan lo mismo.
Prestaciones:
Escalabilidad ():
- : → .
- : .
Escalable, pero mal (). Además cada procesador guarda entera (): la memoria no escala. Un agrupamiento 2D (bloques, tipo Cannon/SUMMA) reduce el volumen comunicado a por procesador.
Problema 27 (oct. 2024)
Gram-Schmidt modificado (MGS), , . Solo líneas 7–14 de una iteración : para cada columna , (producto escalar, ) y (). Llamo al número de columnas pendientes.
Dato del enunciado (columnas, , P2P no optimizado, iteración 0): . Es: la columna pivote envía ( datos) a los demás uno a uno, y cada uno hace FLOPs.
1. Columnas, iteración genérica, P2P optimizado. El dueño de la columna difunde a los procesadores con columnas pendientes en árbol (árbol binomial):
2. Grafo por filas (), , , pivote en la columna 4. tiene la fila de y . Solo queda la columna 5.
- Cada tiene ya y : calcula el producto parcial (1 FLOP).
- es la suma de los 4 parciales: una reducción, y además todos necesitan el resultado para actualizar su elemento. La mejor opción es un allreduce en hipercubo (reducción con réplica), sin fase de difusión aparte:
paso 1: P1 <-> P2 P3 <-> P4 (intercambian parcial y suman)
paso 2: P1 <-> P3 P2 <-> P4 (todos tienen dtmp)
final : cada Pk: A(k,5) = A(k,5) - dtmp*Q(k,4) (2 FLOPs)
P4 guarda R(4,5) = dtmp
3. Tiempo, eficiencia, coste y sobrecarga (iteración genérica, ). El allreduce trabaja con vectores de valores (uno por columna pendiente):
Secuencial de la iteración: .
(El término en es cálculo redundante: el allreduce hace sumas donde el secuencial hace .)
4. Escalabilidad (, con ):
- : → crece como .
- (y ): → no escala: la eficiencia cae como . La pérdida es lenta (logarítmica), pero no se compensa haciendo crecer el problema.
5. Algoritmo completo.
- Filas: todas las filas participan en todas las iteraciones (cada iteración toca las filas de las columnas pendientes): la carga está balanceada durante todo el algoritmo. Coste: un allreduce por iteración.
- Columnas: el procesador de la columna queda ocioso a partir de la iteración . Con bloques consecutivos, los primeros procesadores acaban enseguida y la eficiencia global cae a (trabajo triangular). Arreglo: distribución cíclica de columnas.
Conclusión: por filas es mejor en equilibrio de carga; su límite es que solo admite procesadores (, la dimensión pequeña).
Problema 26 (oct. 2023)
Mismo MGS. El bucle externo no se paraleliza.
1. Líneas 2–5: , la norma de una columna. Es una reducción (como el producto escalar). Estrategia: divide y vencerás en árbol (árbol binomial), .
Para las líneas 8–15 en una iteración , con :
2. Tiempo secuencial:
3. Descomposición del dominio, centrada en los datos de salida: las columnas , . A cada columna se le aplica la misma operación (producto escalar con y actualización) y las columnas son independientes entre sí; solo comparten la lectura de .
4. Agrupar por columnas (bloques de columnas, ). Así el producto escalar de cada columna es local (no hace falta ninguna reducción) y la única comunicación es difundir ( datos) desde su dueño. Por filas habría una reducción por columna en cada iteración.
5. Tiempo paralelo (difusión en árbol):
6.
7. Escalabilidad ():
- : → .
- : → creciendo el número de columnas, .
Escalabilidad buena (no óptima).
8. Algoritmo completo: sí hay pérdida. Las columnas ya están terminadas, así que con bloques consecutivos los procesadores de las primeras columnas se quedan sin trabajo y el último hace casi todo. Variante: distribución cíclica (o bloque-cíclica) de columnas, columna procesador . Así todos conservan columnas activas en cada iteración.
Problema 25 (nov. 2022)
, . Contar las filas cuya proyección es igual a . Cuadrado, división, suma y raíz: 1 FLOP.
secuencial. Por fila: cuadrados + sumas + 1 división + 1 raíz + comparar/contar ≈ :
1. Descomposición del dominio, por filas: cada fila es independiente. Se podría bajar a elemento, pero entonces cada fila exigiría su propia reducción; con hay concurrencia de sobra a nivel de fila.
2. Comunicaciones: solo al final, una reducción (suma) de los resultados 0/1. Global, en árbol.
3. Agrupar en bloques de filas consecutivas (sin partir filas). Cada procesador cuenta sus filas y al final hay una reducción de un escalar.
4. Asignación estática y balanceada (todas las filas cuestan igual). Supongo, como en el resto del boletín, que la matriz ya está repartida y que lo conocen todos.
Isoeficiencia: en los tres términos → : muy buena. Es de coste óptimo mientras .
Si hubiera que contar el reparto inicial
Enviar a cada uno sus datos con P2P en serie añade a , y por tanto a : , no escalaría.
Problema 24 (nov. 2022)
, , 1 FLOP por elemento. En el grafo, cada elemento se comunica (en los dos sentidos) con 6 vecinos: arriba, abajo, izquierda, derecha y la diagonal principal (arriba-izquierda y abajo-derecha). divisibles por , reparto y recogida gratis.
1. .
2. 1D por bloques de filas consecutivas (BFC): filas por procesador. La frontera con el bloque de arriba y con el de abajo es una fila. Los vecinos vertical y diagonal de la fila frontera están en la misma fila vecina (desplazados una posición), así que basta con enviar una fila de datos a cada lado y recibir otra:
3. 1D por bloques de columnas consecutivas (BCC): columnas. Igual, pero las fronteras son columnas de datos (horizontal y diagonal caen en la misma columna vecina):
4. Relación superficie/volumen:
El volumen es el mismo y, como , BCC tiene menos superficie. Se elige BCC. Además admite hasta procesadores, frente a de BFC.
5. Escalabilidad de BCC. , :
- : → , óptimo.
- : . Si el problema crece en (la dimensión larga) con fijo, : escalabilidad óptima. Si crecen las dos dimensiones a la vez (), .
Es escalable.
Problema 23 (nov. 2021)
. Se calcula un estadístico global (máximo, media…) con todos los elementos y luego cada elemento se actualiza con él. 1 FLOP por elemento en cada fase. Descomposición del dominio, una tarea por elemento ().
1. .
Interpretación de los tres diseños
El enunciado pide tres grafos cada vez mejores. Interpreto: (2.1) centralizado, (2.2) árbol sobre los elementos y (2.3) aprovechar la estructura 2D (filas y columnas, por dimensión) con réplica, eliminando la fase de difusión.
2.1. Centralizado (cota ). Todas las tareas envían su valor a una; esta calcula el estadístico y lo devuelve una a una; cada tarea se actualiza:
Peor que el secuencial: toda la carga recae en una tarea.
2.2. Árbol sobre tareas (divide y vencerás): reducción en niveles y difusión en otros :
2.3. Filas y columnas con réplica (hipercubo en cada dimensión). Primero un allreduce en cada fila ( pasos, todas las filas a la vez): cada tarea tiene el estadístico de su fila. Luego un allreduce en cada columna ( pasos): todas tienen el global. No hace falta difusión: se ha transformado el grafo de 2.2 replicando cálculo y comunicaciones.
Técnicas usadas: divide y vencerás (de 2.1 a 2.2) y replicación (de 2.2 a 2.3, que la reduce a la mitad).
Problema 22 (dic. 2020)
Este es también el problema 3 de aula. , ordenadores mono-core, comunicación interna gratis. En cada repetición hay que normalizar por filas.
Supuesto
Normalizar = dividir cada elemento por la suma de su fila: sumas + divisiones por fila. Con la norma euclídea solo cambian las constantes ( en vez de ).
1. Descomposición y comunicaciones. Descomposición del dominio, una tarea por elemento (). Las filas son independientes entre sí. Dentro de cada fila hay una reducción y todos necesitan el resultado. Mejor modelo: allreduce en hipercubo dentro de cada fila (todas las filas a la vez):
2. Agrupación por bloques de filas ( filas por ordenador, ). Cada fila queda entera en un ordenador: cero comunicaciones (superficie 0). Por columnas habría que hacer un allreduce de un vector de sumas parciales en cada repetición.
3. Escalabilidad: → la eficiencia es 1 para cualquier : escalable. Como mucho hay ordenadores, así que para usar más hace falta (igual que "columnas" en el problema 2 de aula).
Problema 21 (oct. 2020)
1. Qué hace: normaliza cada columna de dividiéndola por la suma de sus elementos.
2. Por columna, sumas + divisiones: .
3. Por columnas ( columnas completas): en a) cada uno tiene ya sus sumas completas, así que pasa directamente a c). Sin comunicaciones:
4. Por filas ( filas):
- a) sumas parciales de las columnas con sus filas: → vector de .
- b) el gestor recibe los vectores uno detrás de otro: ; los fusiona sumándolos: ; y los reenvía a los : .
- c) dividir sus elementos: .
5.
6. El mejor es por columnas: la dependencia (la suma) va a lo largo de la columna, así que agrupando por columnas queda dentro de cada procesador. siempre, frente a , que además decrece con .
7. Columnas: → escalable (con el límite , así que ). Para comparar, en filas: ; con , el término da y los términos y dan .
Problema 20 (dic. 2019)
Matriz (), iteraciones:
4 FLOPs por elemento (1 producto, 2 sumas, 1 división): .
1. Descomposición del dominio: una tarea por elemento. Fronteras con padding.
2. Comunicaciones: locales, estáticas, regulares. Cada tarea solo depende de sus dos vecinos en diagonal: 2 envíos + 2 recepciones por iteración.
La clave
Los elementos de distintas diagonales ( constante) nunca se comunican. Cada diagonal es una cadena independiente.
3. Agrupación por diagonales: cero comunicaciones. Las diagonales tienen longitudes distintas, así que hay que juntarlas para equilibrar. Con , hay 8 diagonales de longitudes (20 elementos). Con : , 5 elementos cada uno: perfectamente balanceado.
Por comparación, por bloques de filas habría que intercambiar una fila de datos con cada vecino ( por iteración), y por columnas una de (peor, porque ). El inconveniente de las diagonales es que el almacenamiento no es contiguo (hay que reorganizar la matriz por diagonales).
Problema 19 (oct. 2019)
Entrada . Paso 1: cúbica () que produce un vector de . Paso 2: cuadrático () sobre el vector. Un equipo no puede hacer dos P2P a la vez.
A. .
B. Comunicaciones del intercambio (cada uno tiene y todos deben acabar con ):
- Todos a uno y uno a todos: recoger trozos en serie, ; difundir el vector entero a en serie, . Total .
- Todos con todos: se organiza en rondas de parejas. En cada ronda cada equipo envía y recibe un trozo (2 P2P, no simultáneas): .
La mejor es la 2: el mismo término en , pero en mueve datos por equipo frente a .
C. (paso 2 también repartido de forma balanceada)
D.
E. Isoeficiencia ():
- : → .
- : .
Escalable, no óptimo: domina por las latencias. Con la alternativa 1 el término sería y daría .
Problema 18 (dic. 2019): CUDA, coalescencia
Kernel con 1 bloque de 32 hilos; el hilo recorre la fila :
<span><span style="color: var(--shiki-token-keyword)">for</span><span style="color: var(--shiki-color-text)"> (</span><span style="color: var(--shiki-token-keyword)">int</span><span style="color: var(--shiki-color-text)"> i</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-color-text)">; i</span><span style="color: var(--shiki-token-keyword)"><</span><span style="color: var(--shiki-color-text)">N; i</span><span style="color: var(--shiki-token-keyword)">++</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (M[</span><span style="color: var(--shiki-token-constant)">threadIdx</span><span style="color: var(--shiki-token-punctuation)">.</span><span style="color: var(--shiki-color-text)">x</span><span style="color: var(--shiki-token-keyword)">*</span><span style="color: var(--shiki-color-text)">N </span><span style="color: var(--shiki-token-keyword)">+</span><span style="color: var(--shiki-color-text)"> i] </span><span style="color: var(--shiki-token-keyword)"><</span><span style="color: var(--shiki-color-text)"> FPA) M[</span><span style="color: var(--shiki-token-constant)">threadIdx</span><span style="color: var(--shiki-token-punctuation)">.</span><span style="color: var(--shiki-color-text)">x</span><span style="color: var(--shiki-token-keyword)">*</span><span style="color: var(--shiki-color-text)">N </span><span style="color: var(--shiki-token-keyword)">+</span><span style="color: var(--shiki-color-text)"> i] </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0.0</span><span style="color: var(--shiki-color-text)">;</span></span>
<span></span>
1. No es coalescente. En la iteración , el hilo 0 accede a , el 1 a , el 2 a … Hilos consecutivos acceden a posiciones separadas elementos (stride ): cada acceso del warp cae en un segmento de memoria distinto. Con de 4×4 (row-major) y 4 hilos, en la iteración 0 se leen las posiciones 0, 4, 8, 12.
2. Versión coalescente: que cada hilo recorra una columna, para que en cada iteración los hilos consecutivos lean posiciones consecutivas:
<span><span style="color: var(--shiki-color-text)">__global__ </span><span style="color: var(--shiki-token-keyword)">void</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">Caso2</span><span style="color: var(--shiki-color-text)">(</span><span style="color: var(--shiki-token-keyword)">double</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">*</span><span style="color: var(--shiki-color-text)">M</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">double</span><span style="color: var(--shiki-color-text)"> FPA</span><span style="color: var(--shiki-token-punctuation)">,</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">const</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">int</span><span style="color: var(--shiki-color-text)"> N) {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">for</span><span style="color: var(--shiki-color-text)"> (</span><span style="color: var(--shiki-token-keyword)">int</span><span style="color: var(--shiki-color-text)"> i</span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-token-constant)">0</span><span style="color: var(--shiki-color-text)">; i</span><span style="color: var(--shiki-token-keyword)"><</span><span style="color: var(--shiki-color-text)">N; i</span><span style="color: var(--shiki-token-keyword)">++</span><span style="color: var(--shiki-color-text)">)</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> (M[i</span><span style="color: var(--shiki-token-keyword)">*</span><span style="color: var(--shiki-color-text)">N </span><span style="color: var(--shiki-token-keyword)">+</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">threadIdx</span><span style="color: var(--shiki-token-punctuation)">.</span><span style="color: var(--shiki-color-text)">x] </span><span style="color: var(--shiki-token-keyword)"><</span><span style="color: var(--shiki-color-text)"> FPA) M[i</span><span style="color: var(--shiki-token-keyword)">*</span><span style="color: var(--shiki-color-text)">N </span><span style="color: var(--shiki-token-keyword)">+</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">threadIdx</span><span style="color: var(--shiki-token-punctuation)">.</span><span style="color: var(--shiki-color-text)">x] </span><span style="color: var(--shiki-token-keyword)">=</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">0.0</span><span style="color: var(--shiki-color-text)">;</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
En la iteración los 4 hilos acceden a : una sola transacción.
Problema 17 (jun. 2019)
complejas (); en cada iteración y se repite hasta que el error, calculado sobre la parte real de , sea . procesadores en línea o en malla 2D. iteraciones.
Secuencial. Por elemento: producto complejo (6) + suma compleja (2) = 8 FLOPs. Error: sumar las partes reales (), cuadrado, raíz y división ( 3). Por iteración :
Diseño. La actualización de cada elemento es independiente (sin vecinos). La única dependencia global es el error: una reducción de un escalar cuyo resultado necesitan todos para decidir si siguen. Es decir, un allreduce de 1 dato por iteración. Como no hay comunicación entre vecinos, la forma de los bloques da igual para el volumen de datos. Lo que importa es cuántos saltos cuesta el allreduce en cada topología.
| Topología | Allreduce de un escalar | |
|---|---|---|
| Línea | Reducir a lo largo de la línea y difundir: saltos | |
| Malla | Reducir por filas y luego por columna, y difundir: saltos |
Se elige la malla 2D con bloques 2D (las dimensiones son divisibles por ). Con , : 12 tareas en una malla, cada una con su bloque.
Escalabilidad ():
- Línea: → .
- Malla: → . Mejor.
Problema 16 (may. 2019)
(producto complejo, 6 FLOPs), una sola vez: .
No hay ninguna dependencia entre elementos: cualquier particionado 1D o 2D da
si no se cuenta la distribución. Escalable hasta . La eficiencia escalada es la recta .
Lo que decide de verdad es el reparto y la recogida, si se cuentan: desde un nodo, P2P en serie, hay que enviar y ( complejos = reales) y recoger ( reales): . Entonces → no escala. En una malla 2D se puede distribuir por filas de la malla y luego por columnas en paralelo (en etapas), lo que reduce el término de latencia.
Problema 15 (dic. 2018)
Filtro del problema 14 en un ordenador: CPU con núcleos () y una GPU veces más potente que la CPU (entera). , : comunicaciones CPU/GPU.
1. Reparto proporcional a la potencia: CPU , GPU , en bloques de filas. Así las dos terminan a la vez.
2.
- CPU paralela: .
- Heterogénea: cargar en la GPU su parte, iteraciones de cálculo (equilibradas), intercambiar en cada iteración las filas frontera entre CPU y GPU, y descargar:
3. Con , , y carga/descarga gratis: con una sola iteración el halo viaja con la carga inicial (gratis), así que
Cualquier GPU () compensa, con ganancia . La decisión deja de ser trivial en cuanto la transferencia cuesta: con carga/descarga de coste hace falta , es decir, y .
Problema 14 (dic. 2018)
Filtro gaussiano veces, imagen , máscara (sin separabilidad), FLOPs por píxel. Reparto/recogida gratis. Radio .
1. .
2. Descomposición del dominio, un píxel por tarea (). Cada tarea necesita los vecinos de su ventana (con , : los 8 vecinos de cada píxel; los bordes usan padding). Comunicaciones locales, estáticas, regulares, síncronas.
3. Agrupamientos 1D:
- Bloques de filas: filas. Se intercambian filas con el bloque de arriba y con el de abajo: .
- Bloques de columnas: por simetría (imagen cuadrada, máscara simétrica) sale exactamente lo mismo.
Analíticamente son equivalentes. En la práctica, con almacenamiento row-major, las filas son mejores: el halo es contiguo y se envía sin empaquetar.
Escalabilidad: ; da y : . Además .
Problema 13 (oct. 2018)
. Un ordenador central que no calcula y de cálculo: de tipo A (1 núcleo) y de tipo B (2 núcleos), todos con . Solo hay comunicación en el reparto inicial.
1. Reparto óptimo = proporcional a la potencia. Si A recibe , B recibe :
Así todos tardan .
2. El central envía en serie mensajes que suman datos: . El último en recibir empieza a calcular al final:
Hay núcleos de cálculo; los uso como recursos para coste y eficiencia:
3. : → . : → no escalable.
4. Gustafson–Barsis: la parte secuencial (el reparto, ) crece igual que el trabajo, así que la fracción secuencial no disminuye al agrandar el problema:
Según Gustafson, el algoritmo no escala: agrandar el problema no compensa.
Problema 12
Es el ejemplo de clase del clúster CPU + GPU (GPU 9×). Está resuelto en Tema 1, sistemas heterogéneos. Resultado: no escala por el término del reparto.
Problema 11
Filtro gaussiano 1D, máscara 3, en el eje vertical de , veces. Clúster de ordenadores con CPU mono-core y GPU 19× la CPU.
1. Descomposición del dominio, un elemento por tarea. Cada elemento depende de arriba y abajo. . Con ordenadores: .
2. Agrupar por bloques de columnas: las dependencias son verticales, así que cada columna las contiene todas y no hay comunicaciones. Almacenamiento column-major (columnas contiguas). A cada ordenador le tocan columnas, es decir, elementos.
3. Un ordenador. Secuencial: . Heterogéneo: CPU de sus columnas y GPU . Al ser columnas independientes, no hay halo entre CPU y GPU:
4. Con CPU/GPU gratis y sin comunicación entre ordenadores: . Equivale a CPUs sin sobrecarga: escalable (hasta ). Si se contara el reparto desde un nodo en serie (), el término de lo haría no escalable.
Problema 10
Igual que el 11 pero el filtro va en el eje horizontal y cada ordenador tiene CPU de núcleos y GPU 9× la CPU:
- Dependencias horizontales → agrupar por bloques de filas, almacenamiento row-major, elementos por ordenador, sin comunicaciones.
- En cada ordenador: CPU (repartido entre sus núcleos) y GPU , sin halo entre ellas.
- Clúster con CPU/GPU gratis: .
Problema 9
repartida por bloques de filas; ordenadores con núcleos; sincronización interna de coste 1. Calcular la media.
1. Secuencial: espacio (más auxiliar), tiempo (sumas, más 1 división).
2. Dibujo para :
[P0: suma local nm/(pk) tc | reduc. interna log k (1+tc)] [P1: ídem] [P2: ídem] [P3: ídem]
^------(ts+tw)------- P1 ^------(ts+tw)------ P3
P0: +tc P2: +tc
^----------------(ts+tw)--------------------- P2
P0: +tc, divide /(nm): tc
3.
Espacio: por ordenador. Con núcleos:
Isoeficiencia: → con : buena.
4. El enunciado dice ""; lo interpreto como . Con un algoritmo asíncrono, , y con el cálculo () es enorme frente a las comunicaciones (). Las comunicaciones quedan totalmente ocultas tras el cálculo: y el speedup es prácticamente lineal.
Problema 8
Clúster de CPUs mono-core, red de 10 Gigabit ideal, filtro gaussiano 3×3 con imágenes de tipo char, 1 FLOP por elemento, iteraciones. E/S de disco gratis.
1. bytes/s, y un char es 1 byte:
2. . Por bloques de filas (halo de 1 fila con cada vecino):
3. Con fijo:
Para no hay comunicaciones y . Para la curva decrece monótonamente, es convexa (forma hiperbólica) y tiende a 0 sin llegar nunca; vale en . Es el comportamiento de Amdahl con tamaño fijo (strong scaling).
4. Con imágenes partidas por filas, la comunicación es (la anchura) y el cálculo . Isoeficiencia en : . Si la imagen crece en altura con la anchura fija, : escalabilidad óptima. Imágenes altas y estrechas, partidas por filas (con imágenes cuadradas sale ).
Problema 7
Un equipo: CPU de núcleos, GPU 9× la CPU, bus PCIe (, ). Filtro 3×3, , 1 FLOP por elemento.
1. Proporcional a la potencia: CPU de las filas, GPU . Así acaban a la vez.
2.
- Secuencial: .
- CPU paralela: .
- Heterogénea (1 iteración): cargar la parte de la GPU con 1 fila de halo, calcular a la vez y descargar:
3. Carga/descarga gratis, , : y . Ganancia ×10 (el tiempo se reduce un 90 %).
Problema 6
, para todo .
Fíjate en el índice
El lado derecho no depende de : todos los elementos de la fila de acaban valiendo lo mismo, el producto escalar de la fila de con la fila de . Y hay que calcularlo antes de sobrescribir la fila.
1.
para i = 1..n
s = 0
para r = 1..n: s = s + A[i,r]*B[i,r]
para j = 1..n: B[i,j] = s
.
2. Las relevantes son las filas de (entrada y salida) y de .
3. Para : 3 tareas por fila (una por ) calculan , una reducción (árbol) por fila y una difusión del resultado a las tareas de la fila. Las filas son independientes.
4. Una tarea por elemento, allreduce por fila en hipercubo:
5. Bloques de filas ( por procesador): la reducción de cada fila queda local, sin comunicaciones. , .
6. → escalable, con (así que ).
7. : ahora es un producto de matrices (y en el mismo sitio). El diseño por filas sigue valiendo con cambios razonables: cada procesador necesita toda (allgather o réplica de , como en el problema 28), una copia temporal de sus filas de antes de sobrescribirlas, y el coste pasa a .
Problema 5
for i = 0..n-2
for j = n-1 down to i+1
for k = i+1..n-1: A[j,k] = funcion1(A[j,i]) // 1 FLOP
b[j] = funcion2(A[j,i]) // 1 FLOP
A[j,i] = 0.0 // 1 FLOP
1. Es la eliminación (gaussiana) hacia delante: deja ceros bajo la diagonal y obtiene un sistema triangular superior equivalente (actualizando ).
2.
3. Una tarea por elemento. En el paso , y solo necesitan , de su misma fila (así está escrito el código: funcion1 no usa la fila pivote). Grafo para : en el paso , en cada fila , envía su valor a y a , y después se pone a 0. No hay aristas entre filas distintas.
4. Agrupar por filas (con en la misma tarea): todas las dependencias quedan dentro de cada procesador, cero comunicaciones.
5. Con bloques consecutivos no está balanceado: la fila trabaja en los pasos , así que la fila 0 no hace nada y la es la que más trabaja (trabajo triangular). Solución: distribución cíclica de filas.
6. Cíclico, sin comunicaciones: , , (salvo un pequeño desequilibrio residual).
7. Es escalable (), con el límite . Sí se podía ver desde la pregunta 3: el grafo ya mostraba que las filas no se comunican entre sí.
Problema 4
Dominio (), iteraciones, 1 FLOP por punto. Según la figura 2, las dependencias son verticales (arriba y abajo).
| Particionado | Comunicación por iteración |
|---|---|
| 1D filas (bloques de filas) | |
| 1D columnas (bloques de columnas) | 0: la dependencia vertical queda dentro |
| 2D () | (solo los cortes horizontales cuestan) |
Se elige 1D por columnas:
Además , así que es la dirección que admite más procesadores (). Escalable.
Problema 3
Igual con dependencias en el eje X (horizontales):
- 1D por filas: cero comunicaciones, pero solo admite (la dimensión pequeña).
- Si hay que cortar también columnas, y cada corte vertical cuesta . Lo mejor es cortar en filas todo lo posible (gratis) y solo lo que falte en columnas: franjas de filas × columnas.
Problema 2
Volumen (), 6 vecinos (estrella 3D), nodos.
1D (láminas). Cada lámina intercambia sus 2 caras: .
- Cortando a lo largo de : cara .
- Cortando a lo largo de una : cara , más pequeña (porque ). Se elige esta.
Isoeficiencia (, fijo): : .
Problema 1
Igual que el 2 pero con nodos en malla 2D . Particionado 2D: ¿qué dos ejes se cortan?
- Las dos : bloques , 4 vecinos, las 4 caras de tamaño → comunicación .
- y una : caras de (2) y (2) → .
Se cortan las dos (igual si ):
Isoeficiencia: da ; : → escalabilidad óptima, mejor que el 1D del problema 2 (). Es la relación superficie/volumen en acción: el 2D reduce la superficie por bloque.