proParCurso 14/15 1Computadores Paralelos 2Programación basada en paso de mensajes 3Técnicas básicas de programación paralela Compulsiva, Divide y Vencerás, Pipeline, Síncrona, Equilibrado de carga y Terminación 4Programación basada en memoria común 5Algoritmos y aplicaciones Ordenación, … 5 5 2, 2, 2 2, 2 5 4
proPar TemariodiviVence-2 4Particionamiento y Divide y Vencerás 1Particionamiento 2Divide y Vencerás 3Ordenación mediante cubetas 4Integración numérica 5Problema de los N-cuerpos
proParParticionamientodiviVence-3 Particionar: Dividir el problema en partes (Ej: Fractal Mandelbrot) datos cuentaM cuentaE1cuentaEn Componer resultado final: scatter + reduce funcional f1 f2 fn Mucho menos frecuente Huffman Quantizar IDCT P B YCbCr RGB MPEG
proParDivide y VencerásdiviVence-4 Divide y Vencerás: Particionar de forma recursiva, combinando resultados (Ej: QuickSort, SumarNúmeros) int sumar(int *s) { if (card(s) <= 2) return (n1 + n2); else { dividir (s, s1, s2); suma1 = sumar(s1); suma2 = sumar(s2); return (suma1 + suma2); } ¿ Paralelización ?
proParDivide y Vencerás (paralelización)diviVence-5 P7P7 P8P8 P9P9 P 10 P 11 P 12 P 13 P 14 P3P3 P4P4 P5P5 P6P6 P1P1 P2P2 P0P0 Dividir Recolectar sumas No trabajan todos los procesos todo el tiempo
proParDivide y Vencerás (paralelización)diviVence-6 P0P0 P1P1 P2P2 P3P3 P4P4 P5P5 P6P6 P7P7 P0P0 P2P2 P4P4 P6P6 P0P0 P4P4 P0P0 ¿ Cómo programarlo ? Recibir_Mi_Trozo Distribuir_A_Descendientes Procesar_Mi_Trozo Recolectar_Sumas_De_Mis_Descendientes Enviar_Mi_Suma_Total_A_Mi_Ancestro ¿Cuáles? P 0 => 4, 2, 1 P 4 => 6, 5 P 2 => 3 P 6 => 7 Nivel
proParDivide y Vencerás (paralelización)diviVence-7 //Recibir_Mi_Trozo if (yo == 0) nivel = numProcesos / 2; else recibir (-1, {&padre, &nivel, &miTrozo}); //Distribuir_A_Descendientes for (i=nivel; i>0; i=i/2) enviar(yo+i, {yo, i/2, trozo(i)}); //Procesar_Mi_Trozo //Recolectar_Sumas_De_Mis_Descendientes //Enviar_Mi_Suma_Total_A_Mi_Ancestro if (yo == 0) printf(“Total = %d\n”, Total); else enviar(padre, Total);
proParDivide y Vencerás (paralelización)diviVence-8 //Recibir_Mi_Trozo if (yo == 0) { for (i=0; i<N; i++) v[i] = random(); nivel = numProcesos / 2; longRodaja = N; } else { MPI_Recv (v, N/2, MPI_INT, MPI_ANY_SOURCE, MPI_ANY_TAG, MPI_COMM_WORLD, &estado); padre = estado.MPI_SOURCE; nivel = estado.MPI_TAG; MPI_Get_count (&estado, MPI_INT, longRodaja); } //Distribuir_A_Descendientes for (i=nivel; i>0; i=i/2) { longRodaja = longRodaja / 2; MPI_Send (&v[longRodaja], longRodaja, MPI_INT, yo+i, i/2, MPI_COMM_WORLD); }
proParDivide y Vencerás (paralelización)diviVence-9 //Procesar_Mi_Trozo suma = 0; for (i=0; i<longRodaja; i++) suma += v[i]; //Recolectar_Sumas_De_Mis_Descendientes for (i=nivel; i>0; i=i/2) { MPI_Recv (&sumaBis, 1, MPI_INT, MPI_ANY_SOURCE, 1, MPI_COMM_WORLD, &estado); suma += sumaBis } // Enviar_Mi_Suma_Total_A_Mi_Ancestro if (yo>0) MPI_Send (&sumaBis, 1, MPI_INT, padre, 1, …); else printf (“Suma = %d\n”, suma);
proParDivide y Vencerás (paralelización)diviVence números int sumaSec => 0,033 ProcesossumaPar 20,152 40,222 80, , ,626 sumaParBis 0,161 0,234 1,006 1,385 1,632 Scatter + Reduce ? *500 17,021 0,75 0,41 0,34 0,27 0,20 211, ,279 86, , ,675
proParOrdenación mediante cubetasdiviVence-11 ¿Ordenar exámenes? ¿Cuánto tardará en ordenar 600 exámenes? De uno en uno ¡ Lentísimo ! A BZ Un montón por letra juntar ¡ Más rápido ! ¿ Paralelo ? ordenar
proParOrdenación mediante cubetasdiviVence-12 M números ( ) en un rango (sea: ) Uniformemente distribuidos en N intervalos regulares (sean: 10) [ ], [ ], M-1 Distribuir en cubetas Ordenar cubetas ¿Programa paralelo? Juntar ¡ No todas iguales !
M-1 P0P0 P1P1 P9P9 proParOrdenación mediante cubetasdiviVence-13 Versión 1: Asociar una cubeta por proceso Ordenar mi cubeta Recolectar en mi cubeta Para todo Pi Todos los Pi necesitan el array completo
proPar Ordenación mediante cubetas diviVence-14 static int *vector, elemCubeta; vector = malloc (CARDINALIDAD * 4); enterosCubeta = MAX_ENTERO / numCubetas; if (yo == 0) // Inicializar vector MPI_Bcast (vector, CARDINALIDAD, MPI_INT, 0, MPI_COMM_WORLD); // Coger los de mi cubeta elemCubeta = 0; for (i=0; i<CARDINALIDAD; i++) if ((vector[i] / enterosCubeta) == yo) vector[elemCubeta++] = vector[i]; ordenarCubeta ( ); // Enviar y Recoger cubetas if (yo == 0) { j = elemCubeta; for (i=1; i<numCubetas; i++) { MPI_Recv (&vector[j], CARDINALIDAD, MPI_INT, i, 1, MPI_COMM_WORLD, &estado); MPI_Get_count (&estado, MPI_INT, &elemCubeta); j+=elemCubeta; } } else MPI_Send (vector, elemCubeta, MPI_INT, 0, 1, MPI_COMM_WORLD);
proParOrdenación mediante cubetasdiviVence-15 Versión 2: Asignar un trozo del array original a cada proceso 0 M-1 P0P0 P1P1 P9P9 Recolectar en miniCubetas Para todo Pi Env/Rec miniCubetas Ordenar mi cubeta MPI_Alltoall
proParOrdenación mediante cubetasdiviVence-16 MPI_Alltoall (BufferEnvio, BufferRecepcion, MPI_Comm) buffer enviar buffer recibir P0P0 buffer enviar 0n-1 P1P1 0 P n-1 0n-1 P0P0 0 P n-2 ¿Todas las minicubetas iguales? gather (receptorP 0 ) gather (receptorP n-1 )
proParTiempos ordenar CubetasdiviVence números, cubetas/Pi y Selección Directa 97,208 puro
Precisión I = F(x) x=a b- proParIntegración numéricadiviVence-18 a F(x) dx b a b = (b – a) / N a b ¿Cómo puede ser el programa paralelo? ¿Mejor trapecios?
proParIntegración numéricadiviVence-19 a b = (b – a) / N (N= ) a)Particionamiento y Asignación Estática (4Pi) a b Cada Pi calculará su trozo: rectángulos ( ) ¿Esbozo del programa paralelo? = (b – a) / P (P=4) P0P0 P1P1 P2P2 P3P3 Conocimiento previo del N razonable
proParIntegración numéricadiviVence-20 a b = (b – a) / N (N=¿?) nAnAn 2nA 2n |A 2n -A n | < cotaError ¿Esbozo del programa paralelo? NArea 2A2A2 4A4A4 b)Divide y Vencerás y Asignación Estática (4Pi) a b P0P0 P1P1 P2P2 P3P3 Reparto no equilibrado de trabajo
proParProblema de los N-cuerposdiviVence-21 “N-body” Interacción de astros en el espacio (galaxia) m1 m2 m3 m4 t m2 (x i,y i ) V i i titi ii G m p m q F = F = m a r 2 q p (x i+1,y i+1 ) V i+1 i+1 t i+1 tt composición i+1 m (V i+1 -V i ) F = t V i+1 = V i + (F t / m) dist = V i t (x i+1,y i+1 ) = f (dist, i, X i, Y i )
proParProblema de los N-cuerposdiviVence-22 for (t=0; t<tMax; t++) /* Para cada periodo */ for (i=0; i<N; i++) { /* Para cada cuerpo */ FNew[i] = NuevaFuerza(i); VNew[i] = NuevaVelocidad(i); PNew[i] = NuevaPosicion(i); } for (i=0; i<N; i++){ /*Actualizar estado global*/ F[i] = Fnew[i] V[i] = VNew[i]; P[i] = PNew[i]; } ¿Esbozo del programa paralelo? No computable eficientemente O(N 2 )
proParAlgoritmo de Burnes-HutdiviVence-23 Idea: Disminuir N agrupando cuerpos próximos r Centro de masa ¿ Cómo determinar las agrupaciones ?
proParAlgoritmo de Burnes-HutdiviVence-24 Idea: Dividir el espacio en (8 ó 4) regiones (cubos o cuadrados) que contengan un único cuerpo y luego QuadTree: Árbol cuaternario En cada nodo: Masa total Centro de masa
proParAlgoritmo de Burnes-HutdiviVence-25 Idea:..... y luego calcular (F,V,X,Y) recorriendo árbol Para Sigue ¿ Parar ? r >= d / tal que = 1.0 o menor r d
proParAlgoritmo de Burnes-HutdiviVence-26
for (t=0; t<tMax; t++) { /* Para cada periodo */ formarArbol; CalcularMasasYCentros; CalcularNuevasFuerzas; Actualizar; } Complejidad = O(NlogN) proPar Algoritmo de Burnes-Hut diviVence-27 ¿Programa paralelo? ??? Asignar nodos a procesos Árbol muy poco equilibrado Bisección recursiva ortogonal
proPar Algoritmo de Burnes-Hut diviVence Media = 10,25
proParAlgoritmo de Burnes-HutdiviVence-29 Bisección recursiva ortogonal FIN