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


Problema 28 (oct. 2025)

Producto de matrices C=A⋅BC = A \cdot B con A,B,C∈Rn×nA, B, C \in \mathbb{R}^{n\times n}, T(n)=2n3tcT(n) = 2n^3 t_c, memoria distribuida. Cada procesador tiene los mismos índices de AA, BB y CC. Allgather (recursive doubling): Tallgather≅log⁡(p) ts+n twT_{allgather} \cong \log(p)\,t_s + n\,t_w.

1. Descomposición del dominio, centrada en la salida. Una tarea por elemento C(i,j)=∑kA(i,k) B(k,j)C(i,j) = \sum_k A(i,k)\,B(k,j): 2n2n FLOPs.

2. Comunicaciones. La tarea (i,j)(i,j) tiene A(i,j)A(i,j) y B(i,j)B(i,j), pero necesita toda la fila ii de AA y toda la columna jj de BB:

  • allgather de AA entre las nn tareas de cada fila;
  • allgather de BB entre las nn tareas de cada columna.

Comunicaciones globales (dentro de cada fila/columna), estáticas y regulares.

3. Agrupación por filas. Cada procesador tiene n/pn/p filas consecutivas de AA, BB y CC (p≤np \le n).

  • La fila de AA ya es local: el allgather de AA desaparece (se ha agrupado en esa dirección).
  • Para calcular sus filas de CC necesita toda BB: un allgather de BB entre los pp procesadores, en el que cada uno aporta sus n2/pn^2/p datos y todos acaban con los n2n^2.

4. Asignación: estática, un bloque de filas por procesador; balanceada porque todas las filas cuestan lo mismo.

Prestaciones:

T(n,p)=2n3ptc+log⁡(p) ts+n2twT(n,p) = \frac{2n^3}{p}t_c + \log(p)\,t_s + n^2 t_w
C(n,p)=2n3tc+plog⁡(p) ts+p n2twT0=plog⁡(p) ts+p n2twC(n,p) = 2n^3 t_c + p\log(p)\,t_s + p\,n^2 t_w \qquad T_0 = p\log(p)\,t_s + p\,n^2 t_w
E=11+plog⁡(p) ts+p n2tw2n3tcE = \frac{1}{1 + \dfrac{p\log(p)\,t_s + p\,n^2 t_w}{2n^3 t_c}}

Escalabilidad (W=n3W = n^3):

  • tst_s: n3∝Kplog⁡pn^3 \propto K p\log p → W∈θ(plog⁡p)W \in \theta(p\log p).
  • twt_w: n3∝Kp n2⇒n∝Kp⇒W∈θ(p3)n^3 \propto K p\,n^2 \Rightarrow n \propto Kp \Rightarrow W \in \theta(p^3).

Escalable, pero mal (θ(p3)\theta(p^3)). Además cada procesador guarda BB entera (n2n^2): la memoria no escala. Un agrupamiento 2D (bloques, tipo Cannon/SUMMA) reduce el volumen comunicado a ≈n2/p\approx n^2/\sqrt{p} por procesador.


Problema 27 (oct. 2024)

Gram-Schmidt modificado (MGS), A,Q∈Rm×nA, Q \in \mathbb{R}^{m\times n}, m≤nm \le n. Solo líneas 7–14 de una iteración ii: para cada columna j>ij > i, dtmp=Q(:,i)⋅A(:,j)dtmp = Q(:,i)\cdot A(:,j) (producto escalar, 2m2m) y A(:,j)−=dtmp⋅Q(:,i)A(:,j) \mathrel{-}= dtmp\cdot Q(:,i) (2m2m). Llamo c=n−i−1c = n - i - 1 al número de columnas pendientes.

Dato del enunciado (columnas, p=np = n, P2P no optimizado, iteración 0): T≅4m tc+p(ts+m tw)T \cong 4m\,t_c + p(t_s + m\,t_w). Es: la columna pivote envía Q(:,i)Q(:,i) (mm datos) a los demás uno a uno, y cada uno hace 4m4m FLOPs.

1. Columnas, iteración genérica, P2P optimizado. El dueño de la columna ii difunde Q(:,i)Q(:,i) a los cc procesadores con columnas pendientes en árbol (árbol binomial):

T(n,m,p)≅4m tc+log⁡2(c+1) (ts+m tw)T(n,m,p) \cong 4m\,t_c + \log_2(c+1)\,(t_s + m\,t_w)

2. Grafo por filas (p=mp = m), m=4m = 4, n=5n = 5, pivote en la columna 4. PkP_k tiene la fila kk de AA y QQ. Solo queda la columna 5.

  • Cada PkP_k tiene ya Q(k,4)Q(k,4) y A(k,5)A(k,5): calcula el producto parcial Q(k,4) A(k,5)Q(k,4)\,A(k,5) (1 FLOP).
  • dtmpdtmp 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, p=mp = m). El allreduce trabaja con vectores de cc valores (uno por columna pendiente):

T=c tc⏟productos+log⁡m (ts+c tw+c tc)⏟allreduce+2c tc⏟actualizacioˊn=3c tc+log⁡m (ts+c tw+c tc)T = \underbrace{c\,t_c}_{\text{productos}} + \underbrace{\log m\,(t_s + c\,t_w + c\,t_c)}_{\text{allreduce}} + \underbrace{2c\,t_c}_{\text{actualización}} = 3c\,t_c + \log m\,(t_s + c\,t_w + c\,t_c)

Secuencial de la iteración: 4mc tc4mc\,t_c.

E=4c tc3c tc+log⁡m (ts+c tw+c tc)E = \frac{4c\,t_c}{3c\,t_c + \log m\,(t_s + c\,t_w + c\,t_c)}
C=3mc tc+mlog⁡m (ts+c tw+c tc)T0=mlog⁡m (ts+c tw)+mc(log⁡m−1) tcC = 3mc\,t_c + m\log m\,(t_s + c\,t_w + c\,t_c) \qquad T_0 = m\log m\,(t_s + c\,t_w) + mc(\log m - 1)\,t_c

(El término en tct_c es cálculo redundante: el allreduce hace mlog⁡mm\log m sumas donde el secuencial hace mm.)

4. Escalabilidad (W=mcW = mc, con p=mp = m):

  • tst_s: mc∝Kmlog⁡m⇒c∝log⁡pmc \propto K m\log m \Rightarrow c \propto \log p → crece como θ(plog⁡p)\theta(p\log p).
  • twt_w (y tct_c): mc∝Kmlog⁡m⋅c⇒1∝Klog⁡pmc \propto K m\log m\cdot c \Rightarrow 1 \propto K\log p → no escala: la eficiencia cae como 1/log⁡p1/\log p. 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 mm 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 jj queda ocioso a partir de la iteración jj. Con bloques consecutivos, los primeros procesadores acaban enseguida y la eficiencia global cae a ≈1/2\approx 1/2 (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 p≤mp \le m procesadores (m≤nm \le n, la dimensión pequeña).


Problema 26 (oct. 2023)

Mismo MGS. El bucle externo no se paraleliza.

1. Líneas 2–5: ∑jA(j,i)2\sqrt{\sum_j A(j,i)^2}, la norma de una columna. Es una reducción (como el producto escalar). Estrategia: divide y vencerás en árbol (árbol binomial), θ(log⁡m)\theta(\log m).

Para las líneas 8–15 en una iteración ii, con c=n−i−1c = n - i - 1:

2. Tiempo secuencial:

T=∑j=i+1n−1(2m+2m) tc=4mc tcT = \sum_{j=i+1}^{n-1}(2m + 2m)\,t_c = 4mc\,t_c

3. Descomposición del dominio, centrada en los datos de salida: las columnas A(:,j)A(:,j), j>ij > i. A cada columna se le aplica la misma operación (producto escalar con Q(:,i)Q(:,i) y actualización) y las columnas son independientes entre sí; solo comparten la lectura de Q(:,i)Q(:,i).

4. Agrupar por columnas (bloques de columnas, p≤cp \le c). Así el producto escalar de cada columna es local (no hace falta ninguna reducción) y la única comunicación es difundir Q(:,i)Q(:,i) (mm 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):

T(m,c,p)=4mcptc+log⁡p (ts+m tw)T(m,c,p) = \frac{4mc}{p}t_c + \log p\,(t_s + m\,t_w)

6.

C=4mc tc+plog⁡p (ts+m tw)T0=plog⁡p (ts+m tw)E=11+plog⁡p (ts+mtw)4mc tcC = 4mc\,t_c + p\log p\,(t_s + m\,t_w) \qquad T_0 = p\log p\,(t_s + m\,t_w) \qquad E = \frac{1}{1 + \dfrac{p\log p\,(t_s + m t_w)}{4mc\,t_c}}

7. Escalabilidad (W=mcW = mc):

  • tst_s: mc∝Kplog⁡pmc \propto Kp\log p → θ(plog⁡p)\theta(p\log p).
  • twt_w: mc∝Kplog⁡p⋅m⇒c∝Kplog⁡pmc \propto Kp\log p\cdot m \Rightarrow c \propto Kp\log p → creciendo el número de columnas, W∈θ(plog⁡p)W \in \theta(p\log p).

Escalabilidad buena (no óptima).

8. Algoritmo completo: sí hay pérdida. Las columnas ≤i\le i 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 j→j \to procesador j mod pj \bmod p. Así todos conservan ≈c/p\approx c/p columnas activas en cada iteración.


Problema 25 (nov. 2022)

A∈Rm×nA \in \mathbb{R}^{m\times n}, m≫nm \gg n. Contar las filas cuya proyección P(fila)=(c12+⋯+cn2)/nP(\text{fila}) = \sqrt{(c_1^2 + \dots + c_n^2)/n} es igual a ScS_c. Cuadrado, división, suma y raíz: 1 FLOP.

TT secuencial. Por fila: nn cuadrados + (n−1)(n-1) sumas + 1 división + 1 raíz + comparar/contar ≈ 2n+22n + 2:

T(m,n)=m(2n+2) tc≈2mn tcT(m,n) = m(2n+2)\,t_c \approx 2mn\,t_c

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 m≫nm \gg n hay concurrencia de sobra a nivel de fila.

2. Comunicaciones: solo al final, una reducción (suma) de los mm resultados 0/1. Global, en árbol.

3. Agrupar en bloques de m/pm/p 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 ScS_c lo conocen todos.

T(m,n,p)=(2n+2)mptc+log⁡p (ts+tw+tc)T(m,n,p) = \frac{(2n+2)m}{p}t_c + \log p\,(t_s + t_w + t_c)
T0=plog⁡p (ts+tw+tc)E=11+plog⁡p (ts+tw+tc)(2n+2)m tcT_0 = p\log p\,(t_s + t_w + t_c) \qquad E = \frac{1}{1 + \dfrac{p\log p\,(t_s + t_w + t_c)}{(2n+2)m\,t_c}}

Isoeficiencia: W=mn∝Kplog⁡pW = mn \propto Kp\log p en los tres términos → θ(plog⁡p)\theta(p\log p): muy buena. Es de coste óptimo mientras plog⁡p∈O(mn)p\log p \in O(mn).


Problema 24 (nov. 2022)

A∈Rm×nA \in \mathbb{R}^{m\times n}, n>mn > m, 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). m,nm, n divisibles por pp, reparto y recogida gratis.

1. T(m,n)=mn tcT(m,n) = mn\,t_c.

2. 1D por bloques de filas consecutivas (BFC): m/pm/p 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 ≈n\approx n datos a cada lado y recibir otra:

TBFC=mnptc+4(ts+n tw)T_{BFC} = \frac{mn}{p}t_c + 4(t_s + n\,t_w)

3. 1D por bloques de columnas consecutivas (BCC): n/pn/p columnas. Igual, pero las fronteras son columnas de mm datos (horizontal y diagonal caen en la misma columna vecina):

TBCC=mnptc+4(ts+m tw)T_{BCC} = \frac{mn}{p}t_c + 4(t_s + m\,t_w)

4. Relación superficie/volumen:

BFC: 4(ts+n tw)mnptcBCC: 4(ts+m tw)mnptc\text{BFC: } \frac{4(t_s + n\,t_w)}{\frac{mn}{p}t_c} \qquad \text{BCC: } \frac{4(t_s + m\,t_w)}{\frac{mn}{p}t_c}

El volumen es el mismo y, como n>mn > m, BCC tiene menos superficie. Se elige BCC. Además admite hasta p=np = n procesadores, frente a p=mp = m de BFC.

5. Escalabilidad de BCC. T0=4p(ts+m tw)T_0 = 4p(t_s + m\,t_w), W=mnW = mn:

  • tst_s: mn∝Kpmn \propto Kp → θ(p)\theta(p), óptimo.
  • twt_w: mn∝Kp m⇒n∝Kpmn \propto Kp\,m \Rightarrow n \propto Kp. Si el problema crece en nn (la dimensión larga) con mm fijo, W=mn∈θ(p)W = mn \in \theta(p): escalabilidad óptima. Si crecen las dos dimensiones a la vez (m∝nm \propto n), W∈θ(p2)W \in \theta(p^2).

Es escalable.


Problema 23 (nov. 2021)

A∈Rn×nA \in \mathbb{R}^{n\times n}. 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 (p=n2p = n^2).

1. T(n)=n2tc+n2tc=2n2tcT(n) = n^2 t_c + n^2 t_c = 2n^2 t_c.

2.1. Centralizado (cota ≥T(n)\ge T(n)). Todas las tareas envían su valor a una; esta calcula el estadístico y lo devuelve una a una; cada tarea se actualiza:

T(n,p)≅n2(ts+tw)+n2tc+n2(ts+tw)+tc≅2n2(ts+tw)+n2tcT(n,p) \cong n^2(t_s + t_w) + n^2 t_c + n^2(t_s + t_w) + t_c \cong 2n^2(t_s + t_w) + n^2 t_c
E=2n2tcn2 T(n,p)=2tc2n2(ts+tw)+n2tcT0=n2 T(n,p)−2n2tcE = \frac{2n^2 t_c}{n^2\,T(n,p)} = \frac{2t_c}{2n^2(t_s + t_w) + n^2 t_c} \qquad T_0 = n^2\,T(n,p) - 2n^2 t_c

Peor que el secuencial: toda la carga recae en una tarea.

2.2. Árbol sobre n2n^2 tareas (divide y vencerás): reducción en log⁡n2\log n^2 niveles y difusión en otros log⁡n2\log n^2:

T(n,p)≅2log⁡(n2)(ts+tw)+log⁡(n2) tc+tc=4log⁡n (ts+tw)+2log⁡n tc+tcT(n,p) \cong 2\log(n^2)(t_s + t_w) + \log(n^2)\,t_c + t_c = 4\log n\,(t_s + t_w) + 2\log n\,t_c + t_c
E=2tcT(n,p)T0=n2 T(n,p)−2n2tcE = \frac{2t_c}{T(n,p)} \qquad T_0 = n^2\,T(n,p) - 2n^2 t_c

2.3. Filas y columnas con réplica (hipercubo en cada dimensión). Primero un allreduce en cada fila (log⁡n\log n pasos, todas las filas a la vez): cada tarea tiene el estadístico de su fila. Luego un allreduce en cada columna (log⁡n\log n pasos): todas tienen el global. No hace falta difusión: se ha transformado el grafo de 2.2 replicando cálculo y comunicaciones.

T(n,p)≅2log⁡n (ts+tw+tc)+tcT(n,p) \cong 2\log n\,(t_s + t_w + t_c) + t_c
E=2tc2log⁡n (ts+tw+tc)+tcT0=2n2log⁡n (ts+tw+tc)−n2tcE = \frac{2t_c}{2\log n\,(t_s + t_w + t_c) + t_c} \qquad T_0 = 2n^2\log n\,(t_s + t_w + t_c) - n^2 t_c

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. A∈Rn×nA \in \mathbb{R}^{n\times n}, pp ordenadores mono-core, comunicación interna gratis. En cada repetición hay que normalizar por filas.

T(n)=2n2tcT(n) = 2n^2 t_c

1. Descomposición y comunicaciones. Descomposición del dominio, una tarea por elemento (p=n2p = n^2). 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):

T(n,p)=log⁡n (ts+tw+tc)+tcT(n,p) = \log n\,(t_s + t_w + t_c) + t_c
E=2n2tcn2 T(n,p)=2tclog⁡n (ts+tw+tc)+tcC=n2 T(n,p)T0=n2log⁡n (ts+tw+tc)−n2tcE = \frac{2n^2 t_c}{n^2\,T(n,p)} = \frac{2t_c}{\log n\,(t_s + t_w + t_c) + t_c} \qquad C = n^2\,T(n,p) \qquad T_0 = n^2\log n\,(t_s + t_w + t_c) - n^2 t_c

2. Agrupación por bloques de filas (n/pn/p filas por ordenador, p≤np \le n). Cada fila queda entera en un ordenador: cero comunicaciones (superficie 0). Por columnas habría que hacer un allreduce de un vector de nn sumas parciales en cada repetición.

T(n,p)=2n2ptcC=2n2tcT0=0E=1T(n,p) = \frac{2n^2}{p}t_c \qquad C = 2n^2 t_c \qquad T_0 = 0 \qquad E = 1

3. Escalabilidad: T0=0T_0 = 0 → la eficiencia es 1 para cualquier p≤np \le n: escalable. Como mucho hay p=np = n ordenadores, así que para usar más hace falta W=n2≥p2W = n^2 \ge p^2 (igual que "columnas" en el problema 2 de aula).


Problema 21 (oct. 2020)

1. Qué hace: normaliza cada columna de MM dividiéndola por la suma de sus elementos.

2. Por columna, nn sumas + nn divisiones: T(n)=2n2tcT(n) = 2n^2 t_c.

3. Por columnas (n/pn/p columnas completas): en a) cada uno tiene ya sus sumas completas, así que pasa directamente a c). Sin comunicaciones:

Tcol(n,p)=2n2ptcT_{col}(n,p) = \frac{2n^2}{p}t_c

4. Por filas (n/pn/p filas):

  • a) sumas parciales de las nn columnas con sus filas: n2ptc\frac{n^2}{p}t_c → vector de nn.
  • b) el gestor recibe los pp vectores uno detrás de otro: p(ts+n tw)p(t_s + n\,t_w); los fusiona sumándolos: ≈p n tc\approx p\,n\,t_c; y los reenvía a los pp: p(ts+n tw)p(t_s + n\,t_w).
  • c) dividir sus elementos: n2ptc\frac{n^2}{p}t_c.
Tfil(n,p)=2n2ptc+2p(ts+n tw)+p n tcT_{fil}(n,p) = \frac{2n^2}{p}t_c + 2p(t_s + n\,t_w) + p\,n\,t_c

5.

Ecol=1Efil=11+2p2(ts+n tw)+p2n tc2n2tcE_{col} = 1 \qquad E_{fil} = \frac{1}{1 + \dfrac{2p^2(t_s + n\,t_w) + p^2 n\,t_c}{2n^2 t_c}}

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. E=1E = 1 siempre, frente a Efil<1E_{fil} < 1, que además decrece con pp.

7. Columnas: T0=0T_0 = 0 → escalable (con el límite p≤np \le n, así que W≥p2W \ge p^2). Para comparar, en filas: T0=2p2(ts+n tw)+p2n tcT_0 = 2p^2(t_s + n\,t_w) + p^2 n\,t_c; con W=n2W = n^2, el término tst_s da θ(p2)\theta(p^2) y los términos twt_w y tct_c dan n2∝p2n⇒n∝p2⇒W∈θ(p4)n^2 \propto p^2 n \Rightarrow n \propto p^2 \Rightarrow W \in \theta(p^4).


Problema 20 (dic. 2019)

Matriz N×MN \times M (N>MN > M), kk iteraciones:

X[i,j]t+1=4X[i,j]t+X[i−1,j−1]t+X[i+1,j+1]t6X[i,j]^{t+1} = \frac{4X[i,j]^t + X[i-1,j-1]^t + X[i+1,j+1]^t}{6}

4 FLOPs por elemento (1 producto, 2 sumas, 1 división): T=4kNM tcT = 4kNM\,t_c.

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.

T(N,M,p=NM)=k(4(ts+tw)+4tc)T(N,M,p{=}NM) = k\big(4(t_s + t_w) + 4t_c\big)

3. Agrupación por diagonales: cero comunicaciones. Las diagonales tienen longitudes distintas, así que hay que juntarlas para equilibrar. Con N=5N = 5, M=4M = 4 hay 8 diagonales de longitudes 1,2,3,4,4,3,2,11, 2, 3, 4, 4, 3, 2, 1 (20 elementos). Con p=4p = 4: {4,1},{4,1},{3,2},{3,2}\{4,1\}, \{4,1\}, \{3,2\}, \{3,2\}, 5 elementos cada uno: perfectamente balanceado.

T(N,M,p)=4kNMptcE=1T0=0T(N,M,p) = \frac{4kNM}{p}t_c \qquad E = 1 \qquad T_0 = 0

Por comparación, por bloques de filas habría que intercambiar una fila de MM datos con cada vecino (4(ts+M tw)4(t_s + M\,t_w) por iteración), y por columnas una de NN (peor, porque N>MN > M). El inconveniente de las diagonales es que el almacenamiento no es contiguo (hay que reorganizar la matriz por diagonales).


Problema 19 (oct. 2019)

Entrada n×nn \times n. Paso 1: WW cúbica (n3n^3) que produce un vector de nn. Paso 2: cuadrático (n2n^2) sobre el vector. Un equipo no puede hacer dos P2P a la vez.

A. T(n)=(n3+n2) tc≈n3tcT(n) = (n^3 + n^2)\,t_c \approx n^3 t_c.

B. Comunicaciones del intercambio (cada uno tiene n/pn/p y todos deben acabar con nn):

  1. Todos a uno y uno a todos: recoger (p−1)(p-1) trozos en serie, (p−1)(ts+nptw)≈p ts+n tw(p-1)(t_s + \frac{n}{p}t_w) \approx p\,t_s + n\,t_w; difundir el vector entero a p−1p-1 en serie, (p−1)(ts+n tw)≈p ts+p n tw(p-1)(t_s + n\,t_w) \approx p\,t_s + p\,n\,t_w. Total ≈2p ts+p n tw\approx 2p\,t_s + p\,n\,t_w.
  2. Todos con todos: se organiza en p−1p-1 rondas de parejas. En cada ronda cada equipo envía y recibe un trozo (2 P2P, no simultáneas): (p−1)⋅2(ts+nptw)≈2p ts+2n tw(p-1)\cdot 2(t_s + \frac{n}{p}t_w) \approx 2p\,t_s + 2n\,t_w.

La mejor es la 2: el mismo término en tst_s, pero en twt_w mueve 2n2n datos por equipo frente a p np\,n.

C. (paso 2 también repartido de forma balanceada)

T(n,p)=n3ptc+2p ts+2n tw+n2ptcT(n,p) = \frac{n^3}{p}t_c + 2p\,t_s + 2n\,t_w + \frac{n^2}{p}t_c

D.

C=(n3+n2)tc+2p2ts+2p n twT0=2p2ts+2p n twE=11+2p2ts+2p n tw(n3+n2)tcC = (n^3 + n^2)t_c + 2p^2 t_s + 2p\,n\,t_w \qquad T_0 = 2p^2 t_s + 2p\,n\,t_w \qquad E = \frac{1}{1 + \dfrac{2p^2 t_s + 2p\,n\,t_w}{(n^3 + n^2)t_c}}

E. Isoeficiencia (W=n3W = n^3):

  • tst_s: n3∝Kp2n^3 \propto Kp^2 → θ(p2)\theta(p^2).
  • twt_w: n3∝Kp n⇒n∝p⇒W∈θ(p3/2)n^3 \propto Kp\,n \Rightarrow n \propto \sqrt{p} \Rightarrow W \in \theta(p^{3/2}).

Escalable, no óptimo: domina θ(p2)\theta(p^2) por las latencias. Con la alternativa 1 el término twt_w sería p2np^2 n y daría θ(p3)\theta(p^3).


Problema 18 (dic. 2019): CUDA, coalescencia

Kernel con 1 bloque de 32 hilos; el hilo xx recorre la fila xx:

<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)">&lt;</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)">&lt;</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 ii, el hilo 0 accede a M[i]M[i], el 1 a M[N+i]M[N + i], el 2 a M[2N+i]M[2N + i]… Hilos consecutivos acceden a posiciones separadas NN elementos (stride NN): cada acceso del warp cae en un segmento de memoria distinto. Con MM 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)">&lt;</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)">&lt;</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 ii los 4 hilos acceden a 4i,4i+1,4i+2,4i+34i, 4i+1, 4i+2, 4i+3: una sola transacción.


Problema 17 (jun. 2019)

A,B,CA, B, C complejas m×nm \times n (m>nm > n); en cada iteración Cij+=Aij⊕BijC_{ij} \mathrel{+}= A_{ij} \oplus B_{ij} y se repite hasta que el error, calculado sobre la parte real de CC, sea <ε< \varepsilon. pp procesadores en línea o en malla 2D. kk iteraciones.

Secuencial. Por elemento: producto complejo (6) + suma compleja (2) = 8 FLOPs. Error: sumar las partes reales (mnmn), cuadrado, raíz y división (≈\approx 3). Por iteración ≈9mn\approx 9mn:

T=9kmn tcT = 9kmn\,t_c

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íaAllreduce de un escalarT(m,n,p)T(m,n,p)
LíneaReducir a lo largo de la línea y difundir: ≈2(p−1)\approx 2(p-1) saltos9kmnptc+2kp(ts+tw)\frac{9kmn}{p}t_c + 2kp(t_s + t_w)
Malla p×p\sqrt{p}\times\sqrt{p}Reducir por filas y luego por columna, y difundir: ≈4p\approx 4\sqrt{p} saltos9kmnptc+4kp(ts+tw)\frac{9kmn}{p}t_c + 4k\sqrt{p}(t_s + t_w)

Se elige la malla 2D con bloques 2D (las dimensiones son divisibles por p\sqrt{p}). Con m=4m = 4, n=3n = 3: 12 tareas en una malla, cada una con su bloque.

Escalabilidad (W=kmnW = kmn):

  • Línea: T0≈2kp2(ts+tw)T_0 \approx 2kp^2(t_s + t_w) → W∈θ(p2)W \in \theta(p^2).
  • Malla: T0≈4kpp(ts+tw)T_0 \approx 4kp\sqrt{p}(t_s + t_w) → W∈θ(p3/2)W \in \theta(p^{3/2}). Mejor.

Problema 16 (may. 2019)

Cij=Aij⊕BijC_{ij} = A_{ij} \oplus B_{ij} (producto complejo, 6 FLOPs), una sola vez: T=6mn tcT = 6mn\,t_c.

No hay ninguna dependencia entre elementos: cualquier particionado 1D o 2D da

T(m,n,p)=6mnptcE=1T0=0T(m,n,p) = \frac{6mn}{p}t_c \qquad E = 1 \qquad T_0 = 0

si no se cuenta la distribución. Escalable hasta p=mnp = mn. La eficiencia escalada es la recta Esca=1E_{sca} = 1.

Lo que decide de verdad es el reparto y la recogida, si se cuentan: desde un nodo, P2P en serie, hay que enviar AA y BB (2mn2mn complejos = 4mn4mn reales) y recoger CC (2mn2mn reales): ≈2p ts+6mn tw\approx 2p\,t_s + 6mn\,t_w. Entonces T0∋6p mn twT_0 \ni 6p\,mn\,t_w → mn∝Kp mn⇒mn \propto Kp\,mn \Rightarrow no escala. En una malla 2D se puede distribuir por filas de la malla y luego por columnas en paralelo (en ≈2p\approx 2\sqrt{p} etapas), lo que reduce el término de latencia.


Problema 15 (dic. 2018)

Filtro del problema 14 en un ordenador: CPU con pp núcleos (tct_c) y una GPU SS veces más potente que la CPU (entera). tst_s, twt_w: comunicaciones CPU/GPU.

1. Reparto proporcional a la potencia: x+Sx=N2⇒x + Sx = N^2 \Rightarrow CPU N2S+1\frac{N^2}{S+1}, GPU S N2S+1\frac{S\,N^2}{S+1}, en bloques de filas. Así las dos terminan a la vez.

2.

  • CPU paralela: TCPU=KN2R2ptcT_{CPU} = \dfrac{K N^2 R^2}{p}t_c.
  • Heterogénea: cargar en la GPU su parte, KK iteraciones de cálculo (equilibradas), intercambiar en cada iteración las R−12\frac{R-1}{2} filas frontera entre CPU y GPU, y descargar:
Thet=2(ts+SN2S+1tw)+KN2R2(S+1)ptc+2K(ts+R−12N tw)T_{het} = 2\left(t_s + \frac{S N^2}{S+1}t_w\right) + \frac{K N^2 R^2}{(S+1)p}t_c + 2K\left(t_s + \frac{R-1}{2}N\,t_w\right)

3. Con K=1K = 1, tc=ts=tw=1t_c = t_s = t_w = 1, p=Rp = R y carga/descarga gratis: con una sola iteración el halo viaja con la carga inicial (gratis), así que

TCPU=N2RThet=N2RS+1T_{CPU} = N^2 R \qquad T_{het} = \frac{N^2 R}{S+1}

Cualquier GPU (S>0S > 0) compensa, con ganancia S+1S + 1. La decisión deja de ser trivial en cuanto la transferencia cuesta: con carga/descarga de coste 2(1+SN2S+1)2(1 + \frac{S N^2}{S+1}) hace falta S(N2(R−2)−2)>2S(N^2(R-2) - 2) > 2, es decir, R>2R > 2 y S>2N2(R−2)−2S > \dfrac{2}{N^2(R-2) - 2}.


Problema 14 (dic. 2018)

Filtro gaussiano KK veces, imagen N×NN\times N, máscara R×RR \times R (sin separabilidad), R2R^2 FLOPs por píxel. Reparto/recogida gratis. Radio r=R−12r = \frac{R-1}{2}.

1. T(N)=KN2R2tcT(N) = K N^2 R^2 t_c.

2. Descomposición del dominio, un píxel por tarea (p=N2p = N^2). Cada tarea necesita los R2−1R^2 - 1 vecinos de su ventana (con R=3R = 3, N=5N = 5: los 8 vecinos de cada píxel; los bordes usan padding). Comunicaciones locales, estáticas, regulares, síncronas.

T(N,p=N2)=K(2(R2−1)(ts+tw)+R2tc)T(N,p{=}N^2) = K\big(2(R^2 - 1)(t_s + t_w) + R^2 t_c\big)

3. Agrupamientos 1D:

  • Bloques de filas: N/pN/p filas. Se intercambian rr filas con el bloque de arriba y con el de abajo: T=K(N2R2ptc+4(ts+rN tw))T = K\left(\frac{N^2R^2}{p}t_c + 4(t_s + rN\,t_w)\right).
  • 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: T0=4Kp(ts+rN tw)T_0 = 4Kp(t_s + rN\,t_w); tst_s da θ(p)\theta(p) y twt_w: N2∝KpN⇒W∈θ(p2)N^2 \propto KpN \Rightarrow W \in \theta(p^2). Además p≤N/rp \le N/r.


Problema 13 (oct. 2018)

T(n)=n2tcT(n) = n^2 t_c. Un ordenador central que no calcula y pp de cálculo: p/2p/2 de tipo A (1 núcleo) y p/2p/2 de tipo B (2 núcleos), todos con tct_c. Solo hay comunicación en el reparto inicial.

1. Reparto óptimo = proporcional a la potencia. Si A recibe xx, B recibe 2x2x:

p2x+p22x=n2  ⇒  xA=2n23p,xB=4n23p\frac{p}{2}x + \frac{p}{2}2x = n^2 \;\Rightarrow\; x_A = \frac{2n^2}{3p}, \qquad x_B = \frac{4n^2}{3p}

Así todos tardan 2n23ptc\frac{2n^2}{3p}t_c.

2. El central envía en serie pp mensajes que suman n2n^2 datos: p ts+n2twp\,t_s + n^2 t_w. El último en recibir empieza a calcular al final:

T(n,p)≅p ts+n2tw+2n23ptcT(n,p) \cong p\,t_s + n^2 t_w + \frac{2n^2}{3p}t_c

Hay p2+p=3p2\frac{p}{2} + p = \frac{3p}{2} núcleos de cálculo; los uso como recursos para coste y eficiencia:

C=3p2T=3p2(p ts+n2tw)+n2tcT0=32p2ts+32p n2twC = \frac{3p}{2}T = \frac{3p}{2}(p\,t_s + n^2 t_w) + n^2 t_c \qquad T_0 = \frac{3}{2}p^2 t_s + \frac{3}{2}p\,n^2 t_w
E=n2tc3p2(p ts+n2tw)+n2tcE = \frac{n^2 t_c}{\frac{3p}{2}(p\,t_s + n^2 t_w) + n^2 t_c}

3. tst_s: n2∝Kp2n^2 \propto Kp^2 → θ(p2)\theta(p^2). twt_w: n2∝Kp n2⇒1∝Kpn^2 \propto Kp\,n^2 \Rightarrow 1 \propto Kp → no escalable.

4. Gustafson–Barsis: la parte secuencial (el reparto, ∝n2tw\propto n^2 t_w) crece igual que el trabajo, así que la fracción secuencial no disminuye al agrandar el problema:

fs≈n2twn2tw+2n23ptc=twtw+2tc3p→p→∞1  ⇒  SG=p−(p−1)fs→1f_s \approx \frac{n^2 t_w}{n^2 t_w + \frac{2n^2}{3p}t_c} = \frac{t_w}{t_w + \frac{2t_c}{3p}} \xrightarrow{p\to\infty} 1 \;\Rightarrow\; S_G = p - (p-1)f_s \to 1

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 twt_w del reparto.


Problema 11

Filtro gaussiano 1D, máscara 3, en el eje vertical de M∈Rm×nM \in \mathbb{R}^{m\times n}, RR veces. Clúster de pp 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. T=R mn tcT = R\,mn\,t_c. Con mnmn ordenadores: T=R(4(ts+tw)+tc)T = R\big(4(t_s + t_w) + t_c\big).

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 n/pn/p columnas, es decir, mnp\frac{mn}{p} elementos.

3. Un ordenador. Secuencial: RmnptcR\frac{mn}{p}t_c. Heterogéneo: CPU 120\frac{1}{20} de sus columnas y GPU 1920\frac{19}{20}. Al ser columnas independientes, no hay halo entre CPU y GPU:

Thet=2(tsGPU+1920mnptwGPU)+R mn20ptcT_{het} = 2\left(t_{s_{GPU}} + \frac{19}{20}\frac{mn}{p}t_{w_{GPU}}\right) + \frac{R\,mn}{20p}t_c

4. Con CPU/GPU gratis y sin comunicación entre ordenadores: T=R mn20ptcT = \dfrac{R\,mn}{20p}t_c. Equivale a 20p20p CPUs sin sobrecarga: escalable (hasta p=np = n). Si se contara el reparto desde un nodo en serie (≈2p ts+2mn tw\approx 2p\,t_s + 2mn\,t_w), el término p mn twp\,mn\,t_w de T0T_0 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 kk núcleos y GPU 9× la CPU:

  • Dependencias horizontales → agrupar por bloques de filas, almacenamiento row-major, mnp\frac{mn}{p} elementos por ordenador, sin comunicaciones.
  • En cada ordenador: CPU 110\frac{1}{10} (repartido entre sus kk núcleos) y GPU 910\frac{9}{10}, sin halo entre ellas.
Thet=2(tsGPU+910mnptwGPU)+mn10 p ktcT_{het} = 2\left(t_{s_{GPU}} + \frac{9}{10}\frac{mn}{p}t_{w_{GPU}}\right) + \frac{mn}{10\,p\,k}t_c
  • Clúster con CPU/GPU gratis: T=mn10pktcT = \dfrac{mn}{10pk}t_c.

Problema 9

A∈Rn×mA \in \mathbb{R}^{n\times m} repartida por bloques de n/pn/p filas; ordenadores con kk núcleos; sincronización interna de coste 1. Calcular la media.

1. Secuencial: espacio θ(nm)\theta(nm) (más O(1)O(1) auxiliar), tiempo T=nm tcT = nm\,t_c (sumas, más 1 división).

2. Dibujo para p=4p = 4:

[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.

T(n,m,p,k)=nmpktc+log⁡k (1+tc)+log⁡p (ts+tw+tc)+tcT(n,m,p,k) = \frac{nm}{pk}t_c + \log k\,(1 + t_c) + \log p\,(t_s + t_w + t_c) + t_c

Espacio: nmp\frac{nm}{p} por ordenador. Con pkpk núcleos:

T0≈pk(log⁡k (1+tc)+log⁡p (ts+tw+tc))E=11+T0/(nm tc)T_0 \approx pk\big(\log k\,(1 + t_c) + \log p\,(t_s + t_w + t_c)\big) \qquad E = \frac{1}{1 + T_0/(nm\,t_c)}

Isoeficiencia: W=nm∝K pklog⁡(pk)W = nm \propto K\,pk\log(pk) → θ(Plog⁡P)\theta(P\log P) con P=pkP = pk: buena.

4. El enunciado dice "tc 10tw=10tst_c\,10t_w = 10t_s"; lo interpreto como tc=10tw=10tst_c = 10t_w = 10t_s. Con un algoritmo asíncrono, T=max⁡(Tar,Tco)+TovT = \max(T_{ar}, T_{co}) + T_{ov}, y con n=mn = m el cálculo (n2pktc\frac{n^2}{pk}t_c) es enorme frente a las comunicaciones (log⁡p (ts+tw)=log⁡p tc5\log p\,(t_s + t_w) = \log p\,\frac{t_c}{5}). Las comunicaciones quedan totalmente ocultas tras el cálculo: T≈n2pktcT \approx \frac{n^2}{pk}t_c y el speedup es prácticamente lineal.


Problema 8

Clúster de pp CPUs mono-core, red de 10 Gigabit ideal, filtro gaussiano 3×3 con imágenes n×nn\times n de tipo char, 1 FLOP por elemento, kk iteraciones. E/S de disco gratis.

1. 10 Gb/s=1.25⋅10910\text{ Gb/s} = 1.25 \cdot 10^9 bytes/s, y un char es 1 byte:

tw=11.25⋅109=8⋅10−10 s=0.8 nst_w = \frac{1}{1.25 \cdot 10^9} = 8 \cdot 10^{-10}\ \text{s} = 0.8\ \text{ns}

2. T=k n2tcT = k\,n^2 t_c. Por bloques de filas (halo de 1 fila con cada vecino):

T(n,p)=k(n2ptc+4(ts+n tw))T(n,p) = k\left(\frac{n^2}{p}t_c + 4(t_s + n\,t_w)\right)

3. Con nn fijo:

E=11+a p,a=4(ts+n tw)n2tcE = \frac{1}{1 + a\,p}, \qquad a = \frac{4(t_s + n\,t_w)}{n^2 t_c}

Para p=1p = 1 no hay comunicaciones y E=1E = 1. Para p≥2p \ge 2 la curva decrece monótonamente, es convexa (forma hiperbólica) y tiende a 0 sin llegar nunca; vale 12\frac{1}{2} en p=1/ap = 1/a. Es el comportamiento de Amdahl con tamaño fijo (strong scaling).

4. Con imágenes h×wh \times w partidas por filas, la comunicación es ∝w\propto w (la anchura) y el cálculo ∝hw/p\propto hw/p. Isoeficiencia en twt_w: hw∝Kp w⇒h∝Kphw \propto Kp\,w \Rightarrow h \propto Kp. Si la imagen crece en altura con la anchura fija, W=hw∈θ(p)W = hw \in \theta(p): escalabilidad óptima. Imágenes altas y estrechas, partidas por filas (con imágenes cuadradas sale θ(p2)\theta(p^2)).


Problema 7

Un equipo: CPU de pp núcleos, GPU 9× la CPU, bus PCIe (tst_s, twt_w). Filtro 3×3, n×nn\times n, 1 FLOP por elemento.

1. Proporcional a la potencia: CPU 110\frac{1}{10} de las filas, GPU 910\frac{9}{10}. Así acaban a la vez.

2.

  • Secuencial: n2tcn^2 t_c.
  • CPU paralela: n2ptc\frac{n^2}{p}t_c.
  • Heterogénea (1 iteración): cargar la parte de la GPU con 1 fila de halo, calcular a la vez y descargar:
Thet=2ts+(9n25+n)tw+n210ptcT_{het} = 2t_s + \left(\frac{9n^2}{5} + n\right)t_w + \frac{n^2}{10p}t_c

3. Carga/descarga gratis, k=1k = 1, p=1p = 1: TCPU=n2tcT_{CPU} = n^2 t_c y Thet=n210tcT_{het} = \frac{n^2}{10}t_c. Ganancia ×10 (el tiempo se reduce un 90 %).


Problema 6

B[i,j]=∑rA[i,r] B[i,r]B[i,j] = \sum_r A[i,r]\,B[i,r], para todo i,ji, j.

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

T(n)=2n2tcT(n) = 2n^2 t_c.

2. Las relevantes son las filas de BB (entrada y salida) y de AA.

3. Para n=3n = 3: 3 tareas por fila (una por rr) calculan A[i,r]B[i,r]A[i,r]B[i,r], 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:

T=tc+log⁡n (ts+tw+tc)E=2tctc+log⁡n (ts+tw+tc)T = t_c + \log n\,(t_s + t_w + t_c) \qquad E = \frac{2t_c}{t_c + \log n\,(t_s + t_w + t_c)}

5. Bloques de filas (n/pn/p por procesador): la reducción de cada fila queda local, sin comunicaciones. T=2n2ptcT = \frac{2n^2}{p}t_c, E=1E = 1.

6. T0=0T_0 = 0 → escalable, con p≤np \le n (así que W≥p2W \ge p^2).

7. B[i,j]=∑rA[r,j] B[i,r]=(B⋅A)[i,j]B[i,j] = \sum_r A[r,j]\,B[i,r] = (B\cdot A)[i,j]: 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 AA (allgather o réplica de AA, como en el problema 28), una copia temporal de sus filas de BB antes de sobrescribirlas, y el coste pasa a 2n3tc2n^3 t_c.


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 bb).

2.

T=∑i=0n−2∑j=i+1n−1((n−i−1)+2)≈∑i(n−i)2≈n33 tcT = \sum_{i=0}^{n-2}\sum_{j=i+1}^{n-1}\big((n-i-1) + 2\big) \approx \sum_{i}(n-i)^2 \approx \frac{n^3}{3}\,t_c

3. Una tarea por elemento. En el paso ii, A[j,k]A[j,k] y b[j]b[j] solo necesitan A[j,i]A[j,i], de su misma fila (así está escrito el código: funcion1 no usa la fila pivote). Grafo para n=5n = 5: en el paso ii, en cada fila j>ij > i, A[j,i]A[j,i] envía su valor a A[j,i+1..4]A[j,i+1..4] y a b[j]b[j], y después se pone a 0. No hay aristas entre filas distintas.

4. Agrupar por filas (con b[j]b[j] en la misma tarea): todas las dependencias quedan dentro de cada procesador, cero comunicaciones.

5. Con bloques consecutivos no está balanceado: la fila jj trabaja en los pasos i<ji < j, así que la fila 0 no hace nada y la n−1n-1 es la que más trabaja (trabajo triangular). Solución: distribución cíclica de filas.

6. Cíclico, sin comunicaciones: T≈n33ptcT \approx \dfrac{n^3}{3p}t_c, E≈1E \approx 1, T0≈0T_0 \approx 0 (salvo un pequeño desequilibrio residual).

7. Es escalable (T0≈0T_0 \approx 0), con el límite p≤np \le n. Sí se podía ver desde la pregunta 3: el grafo ya mostraba que las filas no se comunican entre sí.


Problema 4

Dominio Z×NZ \times N (Z≤NZ \le N), kk iteraciones, 1 FLOP por punto. Según la figura 2, las dependencias son verticales (arriba y abajo).

ParticionadoComunicación por iteración
1D filas (bloques de Z/pZ/p filas)4(ts+N tw)4(t_s + N\,t_w)
1D columnas (bloques de N/pN/p columnas)0: la dependencia vertical queda dentro
2D (p×p\sqrt{p}\times\sqrt{p})4(ts+Nptw)4(t_s + \frac{N}{\sqrt{p}}t_w) (solo los cortes horizontales cuestan)

Se elige 1D por columnas:

T=kZNptcE=1T0=0T = \frac{kZN}{p}t_c \qquad E = 1 \qquad T_0 = 0

Además N≥ZN \ge Z, así que es la dirección que admite más procesadores (p≤Np \le N). Escalable.

Problema 3

Igual con dependencias en el eje X (horizontales):

  • 1D por filas: cero comunicaciones, pero solo admite p≤Zp \le Z (la dimensión pequeña).
  • Si p>Zp > Z hay que cortar también columnas, y cada corte vertical cuesta 4(ts+(alto del bloque) tw)4(t_s + (\text{alto del bloque})\,t_w). Lo mejor es cortar en filas todo lo posible (gratis) y solo lo que falte en columnas: ZZ franjas de filas × p/Zp/Z columnas.

Problema 2

Volumen Z×N×NZ \times N \times N (Z≤NZ \le N), 6 vecinos (estrella 3D), pp nodos.

1D (láminas). Cada lámina intercambia sus 2 caras: 4(ts+cara⋅tw)4(t_s + \text{cara}\cdot t_w).

  • Cortando a lo largo de ZZ: cara N×N=N2N \times N = N^2.
  • Cortando a lo largo de una NN: cara Z×NZ \times N, más pequeña (porque Z≤NZ \le N). Se elige esta.
T=k(ZN2ptc+4(ts+ZN tw))T0=4kp(ts+ZN tw)T = k\left(\frac{ZN^2}{p}t_c + 4(t_s + ZN\,t_w)\right) \qquad T_0 = 4kp(t_s + ZN\,t_w)

Isoeficiencia (W=ZN2W = ZN^2, ZZ fijo): twt_w: ZN2∝Kp ZN⇒N∝p⇒W∈θ(p2)ZN^2 \propto Kp\,ZN \Rightarrow N \propto p \Rightarrow W \in \theta(p^2).

Problema 1

Igual que el 2 pero con pp nodos en malla 2D p×p\sqrt{p}\times\sqrt{p}. Particionado 2D: ¿qué dos ejes se cortan?

  • Las dos NN: bloques Z×Np×NpZ \times \frac{N}{\sqrt{p}} \times \frac{N}{\sqrt{p}}, 4 vecinos, las 4 caras de tamaño ZNp\frac{ZN}{\sqrt{p}} → comunicación 8(ts+ZNptw)8\left(t_s + \frac{ZN}{\sqrt{p}}t_w\right).
  • ZZ y una NN: caras de N2p\frac{N^2}{\sqrt{p}} (2) y ZNp\frac{ZN}{\sqrt{p}} (2) → 4(N2p+ZNp)tw≥8ZNptw4\left(\frac{N^2}{\sqrt{p}} + \frac{ZN}{\sqrt{p}}\right)t_w \ge 8\frac{ZN}{\sqrt{p}}t_w.

Se cortan las dos NN (igual si Z=NZ = N):

T=k(ZN2ptc+8(ts+ZNptw))T0=8k(p ts+p ZN tw)T = k\left(\frac{ZN^2}{p}t_c + 8\left(t_s + \frac{ZN}{\sqrt{p}}t_w\right)\right) \qquad T_0 = 8k\left(p\,t_s + \sqrt{p}\,ZN\,t_w\right)

Isoeficiencia: tst_s da θ(p)\theta(p); twt_w: ZN2∝Kp ZN⇒N∝p⇒W=ZN2∈θ(p)ZN^2 \propto K\sqrt{p}\,ZN \Rightarrow N \propto \sqrt{p} \Rightarrow W = ZN^2 \in \theta(p) → escalabilidad óptima, mejor que el 1D del problema 2 (θ(p2)\theta(p^2)). Es la relación superficie/volumen en acción: el 2D reduce la superficie por bloque.