Paper Mmulfpuna Mpi Cparra Fmancia

417 visualizaciones

Publicado el

0 comentarios
0 recomendaciones
Estadísticas
Notas
  • Sé el primero en comentar

  • Sé el primero en recomendar esto

Sin descargas
Visualizaciones
Visualizaciones totales
417
En SlideShare
0
De insertados
0
Número de insertados
2
Acciones
Compartido
0
Descargas
8
Comentarios
0
Recomendaciones
0
Insertados 0
No insertados

No hay notas en la diapositiva.

Paper Mmulfpuna Mpi Cparra Fmancia

  1. 1. Rendimiento de Algoritmos Paralelos de Multiplicación de Matrices implementados con MPI Cristhian Parra1, Fernando Mancía1 1 Facultad Politécnica de la Universidad Nacional de Asunción Estudiantes del 9no Semestre de Ingeniería Informática {cparra,fmancia}@cnc.una.py Resumen: El problema de la multiplicación de matrices, ha sido ampliamente estudiado en el ámbito académico, y sus soluciones han encontrado aplicaciones en diversos campos del mundo científico. La facilidad para dividir este problema en diferentes partes independientes, ha causado el surgimiento de numerosos algoritmos paralelos que lo resuelven eficientemente. En este trabajo, se presentan los detalles de implementación de 4 algoritmos bien conocidos, aplicados a matrices cuadradas densas. También se presentan los resultados de algunas pruebas llevadas a cabo para analizar el rendimiento de cada algoritmo. Palabras Clave: matrices, algoritmos de multiplicación de matrices, particionamiento de datos, algoritmos paralelos, MPI, Cannon, DNS, particionamiento por bloque cíclico, 2D diagonal.
  2. 2. 1 Introducción Los algoritmos de matrices densas, y entre ellos los de multiplicación de matrices, han encontrado numerosas aplicaciones en la resolución de problemas tanto numéricos, como aquellos que no son numéricos. En ciencias de la computación, la multiplicación de matrices ha encontrado usos importantes en campos como la Criptografía o la Computación y Diseño Gráfico [3]. Todas estas aplicaciones han motivado el estudio y optimización de algoritmos que resuelvan este problema. Además, las matrices y sus operaciones son una parte muy importante del Álgebra Lineal, cuyas aplicaciones en el mundo científico y de la computación son innumerables. La facilidad con que se puede particionar el problema de multiplicar dos matrices cuadradas y densas, lo convierten en un candidato ideal para estudiar los conceptos de computación paralela, y permiten que sea fácil formular algoritmos que aprovechen los recursos de un entorno paralelo. En este trabajo, se describen 4 algoritmos paralelos para multiplicar matrices cuadradas y densas, incluyendo la explicación de los detalles de implementación. Se presentan los resultados de diversas pruebas realizadas, y las conclusiones extraídas a partir del análisis de estas pruebas. El objetivo del trabajo es llevar a la práctica conceptos de computación paralela, analizar algoritmos paralelos para multiplicación de matrices, realizar las implementaciones y ejecutar pruebas que nos permitan extender el análisis preliminar. 2 Descripción general. En primer lugar, es importante definir el problema que queremos resolver y sus parámetros correspondientes. Dada dos matrices densas de tamaño NxN, se necesita obtener el resultado de la multiplicación de estas dos matrices, que es otra matriz NxN igualmente densa, en un tiempo razonable. Una Matriz es Densa cuando existe un bajo porcentaje de ceros en la misma. El parámetro fundamental del problema es el tamaño de la matriz, cuantificada por N. En un entorno de ejecución paralelo, existen dos parámetros más que se tienen en cuenta para nuestros análisis: la cantidad de procesos y la cantidad de procesadores físicos. Para resolver el problema, se implementan los siguientes 4 algoritmos: ✔ Algoritmo 2D con particionamiento por bloques y distribución cíclica, basada en el particionamiento propuesto en el capítulo 3 de [1], que se llamará en adelante 2D cíclico. ✔ Algoritmo DNS, explicado en [1] y extendido en [4] y [5]. ✔ Algoritmo de Cannon, explicado en [1] y generalizado en [4]. ✔ Algoritmo 2D-Diagonal, presentado en [5]. El trabajo está organizado de la siguiente manera: 1. Las secciones 1 y 2 proporcionan una visión global del trabajo realizado. 2/27
  3. 3. 2. La sección 3 proporciona una explicación detallada de cada algoritmo y su implementación. 3. En la sección 4 se describen las pruebas realizadas. 4. La sección 5 alberga la descripción de las métricas obtenidas, los resultados en forma de gráficos, acompañados de algunos comentarios que forman parte del análisis de dichos resultados. 5. La sección 6 proporciona algunas reflexiones de conclusión a las que llegamos al finalizar el trabajo. 6. La sección 7 propone algunas problemáticas y trabajos futuros. 7. La sección 8 presenta nuestras fuentes de información. 8. La sección 9, presenta en forma de anexo, las tablas resumidas de resultados. 2.1 Preliminares 2.1.1 Detalles de implementación comunes a todos los algoritmos: La implementación fue hecha usando el lenguaje de programación C (Ansi C) y la implementación del estándar MPI conocido como OpenMPI. Todo el desarrollo y las pruebas realizadas, fueron llevadas a cabo en el Sistema Operativo Linux Fedora 9, con distintas versiones menores del kernel 2.6.25. Las matrices se generan en tiempo de ejecución, ya que esto era más práctico para realizar las pruebas de rendimiento, aunque está disponible la posibilidad de leer las matrices de archivos en formatos de MatrixMarket, como se hacen [6]. Las matrices son representadas en forma lineal, por lo que para saber el elemento Mat[i,j] se debe acceder de la siguiente manera: Mat[(i*cant_columas) + j] La implementación MPI utilizada, OpenMPI, está disponible para su descarga gratuita en [7] y es Open Source. Para las pruebas realizadas, establecimos cantidades de procesos fijos: 4, 9 y 16 procesos. Solo en el caso de DNS utilizamos cantidades de proceso de 8 y 27, ya que en este algoritmo se requería una cantidad cúbica. Ejecutamos los algoritmos en un entorno de cuatro computadoras conectadas mediante una red ethernet de 10/100 Mbps. De las cuatro máquinas que utilizamos para pruebas, tres contaban con procesadores doble núcleo. Detalles más precisos sobre el ambiente de prueba se presentan en la sección 4. 2.1.2 Disponibilidad del código fuente: Todo el proyecto está disponible a través de Subversion en el repositorio de Google Code, http://mmulfpuna.googlecode.com/svn/trunk/srcmpi. Personas que no son miembros del proyecto pueden acceder al mismo y descargar los fuentes de la siguiente manera: svn checkout http://mmulfpuna.googlecode.com/svn/trunk/srcmpi mmulfpuna- read-only 3/27
  4. 4. 3 Algoritmos Implementados 3.1 Algoritmo 2D Cíclico En realidad, no existe un algoritmo 2D con particionamiento por Bloque Cíclico. Solo existe el concepto sobre el particionamiento por bloque y la distribución cíclica de los bloques, que se presenta y explica en [1]. 3.1.1 Particionamiento por Bloques cíclico: Cuando se debe particionar un determinado problema, para distribuirlo entre distintos procesos, dos objetivos igualmente importante, suelen entrar en conflicto: ✔ Balancear correctamente la carga. ✔ Minimizar el tiempo de ociosidad (Idling) de los procesos. En términos de la multiplicación de matrices, el primer objetivo implica distribuir las tareas de multiplicación en los procesos, de manera que todos los procesos computen matrices igualmente densas. El segundo objetivo implica solapamiento de comunicaciones con cómputo. Además de estos objetivos, existe la realidad de que normalmente existen menos procesadores físicos disponibles, que la cantidad de procesos en los que queremos dividir nuestro problema. Para dar solución a este último problema, y al mismo tiempo encontrar una optimización equilibrada de los dos objetivos presentados, el particionamiento por Bloques cíclicos establece lo siguiente: ✔ Dividir el problema en más tareas que procesos. ✔ Asignar las tareas a los procesos de manera cíclica, de manera tal a que se solapen adecuadamente las comunicaciones con las tareas de cómputo. ✔ Agrupar los procesos en bloque, de manera tal a que cada proceso, se encargue de zonas distintas de la matriz y no asuma el cómputo de largos bloques consecutivos que pueden tener un patrón de carga mayor que otras partes de la matriz. Figura 1. Particionamiento por Bloque con distribución o mapeamiento Cíclico. La imagen (a) muestra la versión 1D y la imagen (b) la versión 2D. La Figura 1 muestra un ejemplo de este tipo de particionamiento y mapeamiento1. 1 Las figuras fueron extraídas directamente de [1] para extender y clarificar las explicaciones. 4/27
  5. 5. Nótese que si la matriz es densa, el agrupamiento de bloques de las matrices hace que al final, a cada proceso le haya tocado una bloque de tarea de cada zona distinta de la matriz, haciendo que el balance general de la carga sea equilibrado. 3.1.2 Algoritmo implementado: El algoritmo implementado, utiliza el particionamiento descrito en la sección anterior, y lo aplica a la matriz de salida. De esta manera, cada proceso calcula un bloque de la salida. Se sigue exactamente la idea del particionamiento, y se utiliza un esquema maestro-esclavo para paralelizar el trabajo. Lo primero que realiza el algoritmo es dividir la matriz de salida, de tamaño N, en N tareas diferentes, lo que implica hacer una división en  N ×  N bloques. La elección de la cantidad de tareas, corresponde a que es el número óptimo que permite tener tamaños de bloque balanceados. Luego, se recorren los índices de la matriz resultado en bloques  P × P tareas, y se asignan estas a los procesos P i de manera cíclica, según se muestra en la siguiente ecuación. ∀ i=0 k ⇒ P i=k mod P Elegido el proceso al cual asignar una tarea, se empaquetan los datos que requerirá este (filas y columnas de las matrices de entrada), y se le envía este paquete en un solo Mensaje a través de MPI_Send. En cuánto al esquema maestro-esclavo, existen dos posibilidades: ✔ La existencia de un proceso dedicado exclusivamente a la asignación y distribución de tareas. ✔ El proceso 0 es el maestro, y además procesa localmente algunos bloques. La segunda opción es atractiva por su simpleza, y sería recomendable en redes de mucha latencia, sin embargo, cuando estamos en redes de alto rendimiento, puede ocurrir que el proceso maestro se convierta en un cuello de botella. La implementación probada en este trabajo corresponde a la segunda opción, por su simplicidad. La estructura principal del algoritmo es la siguiente: int SP = sqrt(P); int ST = sqrt(N); int k = 0; for (i=0; i < N; i += SP) { for (j=0; j < N; j += SP) { for (x=i; x < (i+SP); x += ST) { for (y=j; y < (j+SP); y += ST) { ... int Pdest = P%k; ... if (Pdestino != 0) { Bloque <-- Empaquetar Bloque (x,y) /* Implica empaquetar desde x, ST 5/27
  6. 6. * filas de A, y desde y, ST * columnas de B */ MPI_Send( Bloque, ST * ST *N, MPI_FLOAT, Pdest, Tag, MPI_COMM_WORLD); } else { Bloque Local <-- Empaquetar Bloque (x,y) Local } ... k++; } } ... Multiplicar Bloque Local. Cargar resultado en C. ... for(proc = 0; proc < P, proc++) { if (proc != 0) { MPI_Status status; MPI_Recv( BloqueRec, ST * ST * N, MPI_FLOAT, 0, Tag, MPI_COMM_WORLD, &status); Cargar BloqueRec a matriz C } } } } ... El código ejecutado por los esclavos es sencillamente el siguiente: while (done == 0) { ... MPI_Recv( Bloques, ST * ST * N, MPI_FLOAT,0, Tag, MPI_COMM_WORLD, &status); ... Multiplicar los bloques recibidos. ... MPI_Send( Salida, ST * ST, MPI_FLOAT, 0, Tag, MPI_COMM_WORLD); } ... El algoritmo presentará una sobrecarga y un cuello de botella importante en redes de alto rendimiento, en las cuales el tiempo de envío de mensajes será mucho menor que el tiempo que dura la computación, y muy pronto los procesadores esclavos se quedarán ociosos. 3.1.3 Costo Asintótico. Para analizar el costo asintótico, debemos basarnos en la longitud del ciclo principal del algoritmo. Los principales costos de comunicación se encuentran en el Send más interno. Por cada tarea, se realiza un envío desde el maestro a un esclavo, a lo que este responde con otro Send de la respuesta. 6/27
  7. 7. Además, aunque no figura en nuestro algoritmo simplificado mostrado en la página anterior, luego de cada envío, a los procesos se les envía una pequeño mensaje de 1 palabra para confirmar la continuidad del ciclo. Podríamos considerar, para simplificar el análisis, que se envía un solo mensaje, pero con una palabra más de longitud. En síntesis, teniendo en cuenta que se realizan N tareas, se realizarían N Sends, menos la cantidad de veces en la que el proceso asignado es el 0. Para simplificar el análisis, diremos que en realidad el Proceso 0, se envía su tarea a sí mismo, de manera a tener tantos envíos como tareas. En cada uno de estos envíos, se envían palabras de tipo float, igual a la cantidad de elementos de los bloques de matriz enviados. Por cada tarea, se envía un bloque de  N × N (filas de A) más N × N (columnas de B). A esto hay que sumarle la cantidad de envíos realizados desde los procesos para enviar sus resultados. En estos envíos la cantidad de palabras es igual a  N ×  N =N , ya que corresponde al bloque resultado de la multiplicación de los bloques citados más arriba. A cada proceso, se le asignan N/P tareas, por lo cual, esta es la cantidad de envíos de respuesta. Además, por cada envío realizado desde el proceso maestro, se realiza 1 envío más para confirmar la continuación o no del algoritmo. En síntesis, se realiza la siguiente cantidad de envíos. De esta manera, tenemos la siguiente función sobre el tiempo de comunicaciones. 1 t s× N ×2 t w ×N 2× N × N N 1 P En cuánto al cómputo, cada proceso realiza un cómputo similar de orden N 3 , sin P embargo, hay una importante sobrecarga para el proceso maestro que además de realizar el cómputo de computación, realiza un cómputo lineal por cada envío (cargar el buffer de envío) y otro cómputo extra de cargar las matrices en la estructura final a medida que llegan las respuestas. Aún así, esta sobrecarga no afecta el orden asintótico del cómputo realizado en cada proceso. 3.2 Algoritmo 2D Diagonal El algoritmo 2D diagonal es un algoritmo que sirve como base a otro, de tipo 3D, que optimiza las comunicaciones realizadas. Se organizan los P procesos en una malla 2D de  P× P procesos. La matriz A es particionada en  P grupos de columnas y lo propio se hace con la matriz B, pero en grupos de filas. En el primer paso del algoritmo, se realizan  P envíos para armar la distribución inicial del algoritmo. Luego de esta distribución los procesadores pjj en la diagonal principal tienen cada uno el jth grupo de filas de B y el jth grupo de columnas de A. A partir de aquí, se crean dos sub-topologías: una que conecta a los procesos con sus pares de la misma fila, y otra que conecta a los procesos con sus pares de la misma columna. Entonces se inicia la lógica principal del algoritmo, que consiste en hacer 3 pasos muy sencillos: 7/27
  8. 8. 1. Broadcasting (MPI_Bcast) desde Pjj a los procesos de la misma fila Pj* para enviar a todos el jth grupo de columnas de A, como se muestra en la Figura 22. 2. One to all personalized Broadcast (MPI_Scatter) de los subbloques de tamaño N N del jth grupo de filas de B, desde Pjj . De esta manera, cada proceso  ×  P P Pj* se encarga de computar el producto externo de las columnas de A y las filas de B inicialmente almacenadas en pjj. 3. All to One Reduce (MPI_Reduce) desde cada proceso al proceso diagonal correspondiente a su columna, a través de la operación de suma. El término personalizado del paso 2, se refiere a que solo la porción del grupo de filas de B (Bik) que es necesario para computar una columna i del producto externo de la jth columna de A por la jth fila de B, se le pasa al procesador Pkj La ultima etapa reduce el resultado por adición a lo largo de la dirección y. La matriz final C se obtiene a lo largo de los procesadores diagonales. Cada procesador, envía su producto externo a la diagonal principal que se encarga de procesar C*,i Figura 2. Esquema general del algoritmo 2D Diagonal. De esta manera, a lo largo de la diagonal principal se encuentra el resultado final de la multiplicación. A continuación se muestra el bloque de código principal del algoritmo. Es notable la simplicidad del mismo. ... remains[0] = 0; remains[1] = 1; 2 Las figuras fueron extraídas directamente de [5] para extender y clarificar las explicaciones. 8/27
  9. 9. MPI_Cart_sub(Comm2d,remains,&commF); remains[0] = 1; remains[1] = 0; MPI_Cart_sub(Comm2d,remains,&commC); ... int rrank = mycoords[0]; MPI_Bcast(IthGrupoA,GrupSize*N,MPI_FLOAT,rrank,commF); if (mycoords[0] == mycoords[1] && mycoords[0] ) { cargar_blocks(BloquesB,IthGrupoB,N,GrupSize,GrupSize); } MPI_Scatter( BloquesB, GrupSize*GrupSize, MPI_FLOAT, BloqueB,GrupSize*GrupSize, MPI_FLOAT, mycoords[0], commF); calcular_producto_externo(); MPI_Reduce( IProduct,IProductReduced,GrupSize*N, MPI_FLOAT,MPI_SUM,mycoords[1],commC); ... 3.2.1 Costo Asintótico. El gasto de comunicación realizado en este algoritmo, se resume en la suma de los gastos en las tres operaciones fundamentales que realiza el algoritmo: 1.  P operaciones de Broadcasting, una en cada proceso diagonal, enviando N palabras correspondientes a las filas de A.  × N P 2. Una operación de Scatter en la que se distribuyen N N palabras a a cada  ×  P P proceso de una fila de la malla. 3. Una operación de Reduce a los largo de las columnas que reduce bloques de N palabras.  × N P Teniendo en cuenta estos parámetros, y en base a los costos de las primitivas de comunicación presentadas y analizadas con profundidad en el capítulo 4 de [1], nuestro tiempo de ejecución estaría definido por los siguientes componentes: Broadcasting entre P procesos: N t st w × ×N ×log  P  P Scatter entre P procesos: N N t s×log  P t w × × ×  P−1 P P Reduce entre P procesos (mismo costo que el Broadcast): 9/27
  10. 10. N t st w × ×N ×log   P  P La suma final de estos tres componentes es la siguientes: N2 3×t s×log   P t w × × 2×log   P P−1 P 3.3 Algoritmo de Cannon El algoritmo es una versión de uso eficiente de memoria, comparada con el algoritmo simple. Utiliza una malla de procesadores virtuales y la partición de los datos son de entrada inicialmente para las matrices A y B, aunque a nivel de mapeamiento de tareas, tenemos un mapeamiento de salidas, ya que cada proceso computa un bloque de la matriz de salida. Para comenzar se crea la topología cartesiana de 2 dimensiones, y en cada dimensión se tienen  P procesos. Entonces quedan identificados los procesos desde P 0,0 hasta P  P −1 ,  P −1 . N Las matrices A y B se particionan en bloques de tamaño Tam= quedando en cada P procesador en cada momento un bloque de tamaño Tam de A y otro de B. Luego el Proceso P0,0 es el encargado de pasar al resto de los procesos el bloque que le corresponde. El algoritmo propone una alineación inicial de los bloques de A y B en cada proceso. En la implementación presentada el proceso P0,0 es el encargado de enviar al Proceso P(i,j) el bloque de A y B ya alineado quedando en el proceso P(i, j) el bloque A(i, j+i) y el bloque B(i+j , j). En el caso de que i j  P  , hacemos i j−  P  para simular una estructura toroidal (circular). Una vez que cada procesador ya tiene los bloques A y B que le corresponden, los multiplica y almacena su resultado en un bloque para C con las coordenadas del mismo proceso. Luego se hacen   P −1 corrimientos circulares en cada procesador, de las filas de A por la izquierda y las columnas B por arriba. Las figuras siguientes, ilustran el proceso de manera gráfica. Luego se muestra el código principal del algoritmo implementado3. 3 Las imágenes presentadas en esta sección fueron extraídas de [1] para aclarar mejor el algoritmo implementado. 10/27
  11. 11. Figure 3. Alineación inicial de A y B. Figure 4. A y B luego de la alineación inicial y luego del primer corrimiento. Figure 5. Alineación de las submatrices luego del tercer y cuarto corrimiento. for(pasos=1; pasos< raiz_p; pasos++){ // Desplazamiento Para Matriz A MPI_Cart_shift(malla, 1, -1, &origen, &destino); MPI_Sendrecv_replace(buffA, tam_bloq * tam_bloq, MPI_FLOAT, destino, 0 , origen, 0, malla, &estado_msg ); // Desplazamiento Para Matriz B MPI_Cart_shift(malla, 0, -1, &origen, &destino); MPI_Sendrecv_replace(buffB, tam_bloq * tam_bloq, MPI_FLOAT, destino, 0 , origen, 0, malla, &estado_msg ); //Pasamos del buffer al bloque local buff2Bloque(bloqueA, tamanho_bloque, buffA); buff2Bloque(bloqueB, tamanho_bloque, buffB); //Multiplicación acumular_multiplicar(bloqueA, bloqueB, bloqueC); } 11/27
  12. 12. En cada paso, cada procesador multiplica su bloque de A por su bloque de B, y le suma al bloque de C que tiene. Finaliza el algoritmo y cada procesador P(i, j) tiene el bloque C(i, j) de la matriz resultado. 3.3.1 Diferencias, Mejoras El alineamiento inicial que se propone en el artículo [4], el algoritmo asume que en cada procesador P(i,j) se encuentra el bloque A(i,j) y B(i,j) y luego realiza i corrimientos a la izquierda para A y j corrimientos arriba para B. En nuestra implementación, sin embargo, el proceso P(0,0) calcula que bloque de A y de B es el que corresponde a los demás procesos después de la alineación inicial. Pensamos que esto podría mejorar el tiempo de ejecución en cada proceso evitando los corrimientos iniciales, pero esta mejora solo se dará en el caso de que efectivamente el proceso P(0,0) sea el encargado de distribuir los bloques, caso contrario no sería una mejora. 3.3.2 Limitaciones Las limitaciones y verificaciones de la implementación presentada son las siguientes: • La cantidad de procesos debe ser un cuadrado perfecto. Para la distribución equitativa de los bloques a los procesos en cada dimensión. • La cantidad de elementos (N) debe ser divisible entre la raíz cuadrada de la cantidad de procesos (P). Para que todos los bloques tengan el mismo tamaño, haciendo mas sencilla la implementación. 3.3.3 Análisis Asintótico Considerando solamente los corrimientos de a uno y la multiplicación de cada bloque en los procesos, tenemos que en cada corrimiento se pasan tam_bloque * tam_bloque elementos, N N N2 que es  × = , que seria para cada proceso, tanto para A y B, por lo cual P P P multiplicamos este término por 2. Si consideramos la comunicación y el tiempo de inicialización, tenemos que el gasto de comunicación es el siguientes: N2 2×t st w ×  P Por su parte, el procesamiento en sí de la multiplicación, asumiendo que esta y la suma toman una unidad de tiempo, entonces tendríamos un costo de tam_bloq * tam_bloq *tam_bloq es decir: N N N N3  × × =  P  P  P  P ×  P  Entonces el tiempo total de ejecución paralelo sería: N3 N T p= 2×t s t w ×  P P 12/27
  13. 13. 3.4 Algoritmo DNS (Dekel, Nassimi, Sahni) El DNS es un algoritmo para la multiplicación de matrices densas, que lleva las siglas de sus creadores (Dekel, Nassimi, Sahni). Está basado en particionamiento de datos intermedios y está diseñado para usar una topología de procesadores en tres dimensiones. El algoritmo asume que en cada procesador se realiza una simple multiplicación escalar o de matrices en bloques más pequeños. Para la descripción se tienen P procesadores y el tamaño de bloques entonces es q= P . Generalizamos para que el algoritmo resuelva por bloques. 3 En la implementación realizada, se crea la topología en 3 dimensiones, luego el proceso P(0,0,0) realiza la distribución de los bloques de A y B en los procesos del plano 0. Después de este paso, todos los procesos P(i,j0) tienen el bloque A(i,j) y el bloque B(i,j). Este paso de comunicación inicial en el plano cero no se especifica como parte del algoritmo en su definición, sin embargo, para la implementación de este trabajo se ha decidido hacerlo. Luego cada proceso del plano 0 (Pi,j,0), envía las columnas j de A(*,j) y las filas i de B(i,*) a los pla0nos k=i para el bloque de A y k=j para el bloque de B. En los demás planos cada proceso recibe su parte de A y B. A continuación, cada proceso que está en el plano k=j y tiene la columna j debe replicar su bloque de A a los demás procesos que comparten su plano, para lo cual se utiliza la primitiva MPI_Bcast. Lo mismo para los procesos de k=i y que tienen la fila i, caso en el que se replica el bloque de B a los de su mismo plano y fila. A continuación se muestra la parte del código que realiza dicha acción. int remainA[3]={0,1,0}; MPI_Comm comm_1d_A; MPI_Cart_sub(comm_3d, remainA, &comm_1d_A ); MPI_Bcast(buffA, tamanho_bloque * tamanho_bloque, MPI_FLOAT, micoord[2], comm_1d_A); int remainB[3]={1,0,0}; MPI_Comm comm_1d_B; MPI_Cart_sub(comm_3d, remainB, &comm_1d_B ); MPI_Bcast(buffB, tamanho_bloque * tamanho_bloque, MPI_FLOAT, micoord[2], comm_1d_B); Luego cada uno de los P procesadores ya tiene el bloque de A y de B que le corresponde, es decir, el procesador P(i,j,k) tiene el bloque A(i,k) y el bloque B(k,j). Después de eso cada proceso realiza una simple multiplicación entre el bloque de A y B que tiene, y utilizando la función MPI_Reduce se realiza un all_to_one_reduce entre los elementos P(i,j,*) a través de la operación de suma. A continuación se resume el código para el paso final. acumular_multiplicar(bloqueA, bloqueB, bloqueC); bloque2Buff(bloqueC, tamanho_bloque, buffC); //Creamos un comunicador vertical MPI_Comm comm_vertical; int comm_vertical_id; int remain[3]={0,0,1}; MPI_Cart_sub(comm_3d, remain, 13/27
  14. 14. &comm_vertical); MPI_Comm_rank(comm_vertical, &comm_vertical_id); MPI_Reduce( buffC , bufferFinal, tamanho_bloque * tamanho_bloque, MPI_FLOAT, MPI_SUM, 0, comm_vertical); buff2Bloque(bloqueFinal, tamanho_bloque, bufferFinal); Las siguientes dos imágenes, ilustran el algoritmo visualmente. Figure 6. La distribución inicial de A y B, y luego de realizar el envío de Aij desde Pij0 a Pijj 14/27
  15. 15. Figure 7. Distribución luego del los Broadcastings de A y B 3.4.1 Diferencias con la propuesta original. En la implementación presentada el proceso (0,0,0) es el que lee toda la matriz A y B y empieza a distribuir a los procesos del plano cero. Esto hace que este proceso se pueda constituir en un cuello de botella al inicio del algoritmo, sobre todo para el caso de matrices grandes, en el que se puede producir la saturación del socket de envío de este proceso. Por otro lado, los elementos se tratan por bloques, en lugar de elementos como se muestra en [1]. Además, para el paso final del Reduce, todos los procesos P(i,j,k) suman su bloque de C entre los P(i,j,*) y estos se almacenan en los procesos del plano cero. Entonces al finalizar el algoritmo, la matriz C está distribuida entre los procesos P(0,0,0) a P(q,q,0), siendo q= P 3 3.4.2 Limitaciones: Las limitaciones y verificaciones de la implementación presentada son las siguientes: • La cantidad de procesos debe ser un cubo perfecto. Para la distribución equitativa de los bloques a los procesos en cada una de las 3 dimensiones. • La cantidad de elementos (N) debe ser divisible entre la raíz cúbica de la cantidad de procesos (P). Para que todos los bloques tengan el mismo tamaño, haciendo mas sencilla la implementación. 3.4.3 Tiempo de ejecución Toma un paso para multiplicar y q pasos para sumar, con costo Θ (log q), siempre teniendo q= P . 3 En cuanto a la comunicación, el primer paso de comunicación uno a uno se ejecuta en A y en n 2 B y toma un tiempo para cada matriz de tstw×  tw×log q  . El segundo q 15/27
  16. 16. paso es un broadcast de uno a todos que se ejecuta en A y en B y toma un tiempo para cada n 2 matriz de ts×logqtw×  ×log q q La acumulación de nodo simple final se ejecuta una sola vez (para la matriz C) y toma un 2 n tiempo tslogqtw×  ×log q q 4 Plan de Pruebas. A continuación se describen los detalles de las pruebas, desde las configuraciones y entorno de ejecución paralelo hasta los cálculos de los datos de prueba y como éstos fueron ejecutados. 4.1 Datos de prueba Los parámetros de ejecución de los algoritmos paralelos son los siguientes: • N = Tamaño de la matrices de entrada y resultado • P = Cantidad de procesos. • C = Cantidad de máquinas. Atendiendo las limitaciones de N y P descritas en cada algoritmo en la sección 3, a continuación se presenta una tabla que resume el plan de ejecución de las pruebas. La cantidad de máquinas (C ) fue constante e igual 4 (7 procesadores= 3 dual core + 1 single core), luego se detallan totalmente las especificaciones de las mismas. Para que la comparación pueda ser hecha, agrupamos los valores en 3 tamaños de N y 3 de P, en cada caso, cada algoritmo puede tener una pequeña . La idea de las pruebas era lo siguiente: como se tenían 4 máquinas y 7 procesadores, entonces probar con P menor a 4 Resumen agrupado de las pruebas. N= 400 ~500 N= 900 N = 1200~1800 P=4 Cannon Cannon Cannon 2D 2D 2D 2DD 2DD 2DD(N=1800) P= 8 ~9 Cannon Cannon Cannon DNS (P=8) DNS (P=8) DNS (P=8) 2D (N= 441) 2D 2D (N =1764) 2DD 2DD 2DD P=16~27 Cannon Cannon Cannon DNS (P=27) DNS (P=27) DNS (P=27) 2D 2D 2D 2DD 2DD 2DD 16/27
  17. 17. 4.2 Entorno Para la ejecución se usaron 4 máquinas con sistema operativo Linux Fedora Core 9, kernel 2.6.25.x. Se instaló y configuró la librería OPEN-MPI 1.2.2 y se uso el compilador mpicc, para las ejecuciones se usó mpirun. También se configuró la red y los servidores SSH de cada host para que se pueda ejecutar. Además se configuró la autentificación DSA, para eso en el host maestro donde se lanza la ejecución se generó la clave pública con ssh-keygen y se le pasó a cada una de las demás máquinas para que pueda interactuar vía ssh con esa autentificación. El usuario común usado en cada host uno fue mpiuser. Máquina Procesador Memoria RAM S.O cparra-portatil Intel Core Duo1,6 1,5 GB Linux Fedora Core 9. Ghz. Cache L2 4MB Kernel 2.6.25.xx liz Intel Core Duo. 1,46 1,0 GB Linux Fedora Core 9. Ghz. Cache L2 2MB. Kernel 2.6.25.xx fmanciahome Intel Core Duo. 3,00 1,0 GB Linux Fedora Core 9. Ghz. Cache L2 4MB Kernel 2.6.25.xx familia Intel Pentium IV 1,8 512 MB Linux Fedora Core 9. Ghz (single core) Kernel 2.6.25.xx 5 Resultados Obtenidos Se presentan en esta sección, los gráficos más importantes que resumen los principales resultados obtenidos en las pruebas. 5.1 Métricas Por cada prueba, se extrajeron los valores de las siguientes métricas: ✔ Tiempo de Ejecución en paralelo, obtenido como el tiempo de ejecución de un proceso paralelo en particular. Los procesos de nuestras implementaciones medían sus tiempos y estas medidas eran enviadas al proceso cero que se encargaba de guardarlas para su posterior análisis. De los tiempos de ejecución de los P procesos, se marca al mayor como el tiempo de ejecución paralelo de todo el algoritmo. ✔ Aceleración, que es el ratio entre el mejor tiempo secuencial y el tiempo de ejecución paralelo. Para este propósito, ejecutamos un algoritmo secuencial en una sola máquina para cada tamaño de N utilizado en las pruebas. ✔ Sobre Costo, definido como la diferencia entre el producto del tiempo paralelo por P y el tiempo secuencial. ✔ Eficiencia: definido como la relación entre la aceleración y la cantidad de procesos lanzados. De estas métricas, presentamos los resultados obtenidos para las dos primeras y la eficiencia. 17/27
  18. 18. 5.2 Tiempos de ejecución: P=4;N=600 P=4;N=900 6.0000 18.0000 16.0000 5.0000 14.0000 4.0000 12.0000 10.0000 3.0000 8.0000 2.0000 6.0000 1.0000 4.0000 2.0000 0.0000 0.0000 2D CANNON 2DDIAGONAL 2D CANNON 2DDIAGONAL P=4;N=1200 45.0000 40.0000 35.0000 30.0000 25.0000 20.0000 15.0000 10.0000 5.0000 0.0000 2D CANNON 2DDIAGONAL Figure 8. Tiempos de ejecución de los algoritmos para la ejecución con 4 procesos La Figure 8 muestra los tiempos de ejecución paralela de cada algoritmo cuando se ejecutan en cuatro procesos y distintos tamaños de N. En general, se nota el bajo rendimiento del algoritmo 2D cíclico debido a que el proceso cero distribuye y realiza mucho cómputo. Esto no variará en ninguna de los resultados que se presentarán en esta sección. Además, se puede notar que el aumento en los tamaños de la entrada no afectan el rendimiento relativo de los algoritmos. Esta tendencia se mantuvo en casi todas las pruebas salvo algunas excepciones. Cannon se muestra más rápido que el algoritmo 2D diagonal, aunque el algoritmo 2D diagonal presenta tiempos de ejecución mínimos en los procesos no diagonales, lo cual es un dato muy interesante. Esto no se muestra en los gráficos, pero forma parte de los resultados obtenidos. El cuello de botella precisamente del 2D diagonal está en los procesos diagonales, que realizan un trabajo de cómputo adicional de empaquetamiento de mensajes y recepción de resultados. Dicho de otro modo, el 2D diagonal presenta una mayor eficiencia de comunicación, pero tiene problemas en el desbalanceo del trabajo existente, poniendo mayor carga de trabajo en los procesos diagonales. Con 4 procesos no se realizaron pruebas de DNS, que requiere un número cúbico de procesos. 18/27
  19. 19. P=9;N=600 P=9;N=1200 5.0000 4 0.00 00 4.5000 3 5.00 00 4.0000 3.5000 3 0.00 00 3.0000 2 5.00 00 2.5000 2 0.00 00 2.0000 1 5.00 00 1.5000 1 0.00 00 1.0000 0.5000 5 .0 00 0 0.0000 0 .0 00 0 2D DNS CANNON 2DDIAGONAL 2D DNS C AN N ON 2 D D IAGON AL P=9;N=900 16.0000 14.0000 12.0000 10.0000 8.0000 6.0000 4.0000 2.0000 0.0000 2D DNS CANNON 2DDIAGONAL Figure 9. Tiempos de ejecución de los algoritmos para la ejecución con 9 procesos Con nueve procesos, ya se incluye en el análisis al algoritmo DNS, que muestra tiempos de ejecución muy similares al de Cannon. Sin embargo, las tendencias que se manifestaron en los primeros resultados con 4 procesos, se mantienen y sigue siendo Cannon el algoritmo con menor tiempo de ejecución, seguido del DNS y del 2D Diagonal. El 2D cíclico, como siempre, se mantiene en la última posición por los problemas que ya explicábamos más arriba. P=16;N=600 P=16;N=900 5.0000 25.0000 4.5000 4.0000 20.0000 3.5000 3.0000 15.0000 2.5000 10.0000 2.0000 1.5000 5.0000 1.0000 0.5000 0.0000 0.0000 2D DNS CANNON 2DDIAGONAL 2D DNS CANNON 2DDIAGONAL 19/27
  20. 20. P=16;N=1200 45.0000 40.0000 35.0000 30.0000 25.0000 20.0000 15.0000 10.0000 5.0000 0.0000 2D DNS CANNON 2DDIAG- ONAL Figure 10. Tiempos de ejecución de los algoritmos para la ejecución con 16 procesos Para la ejecución de los algoritmos en 16 procesos, como se muestra en la Figure 10 se presentan excepciones. Para matrices pequeñas, la tendencia se mantiene, pero a medida que aumentamos el número de matrices llega el caso en el que DNS llega a un tiempo de ejecución inferior al de Cannon. En realidad, mirando los tres conjuntos de gráficos, podemos ver que DNS presenta mejoras en su tiempo de ejecución a medida que aumenta el tamaño de las matrices, fenómeno que no es tan notable en los demás algoritmos. 5.3 Aceleración y Eficiencia. En esta sección, se presentan los resultados de la aceleración y la eficiencia de los algoritmos implementados. La aceleración y la eficiencia de los algoritmos siguen en el mismo patrón relativo que los gráficos de tiempo de ejecución, lo cual era de esperarse. Lo notable en estos trabajos es que en ciertos casos, el 2D Cíclico tiene peor rendimiento que el algoritmo secuencial óptimo, lo que permite reafirmar la necesidad de separar a un proceso dedicado para que se comporte como maestro. P=4;N=400 P=4;N=600 3.5000 3.5000 3.0000 3.0000 2.5000 2.0000 2.5000 1.5000 2.0000 1.0000 1.5000 0.5000 1.0000 0.0000 0.5000 2D CANNON 2DDIAG- ONAL 0.0000 2D CANNON 2DDIAGONAL 20/27
  21. 21. P=4;N=1200 3.5000 3.0000 2.5000 2.0000 1.5000 1.0000 0.5000 0.0000 2D CANNON 2DDIAGONAL Figure 11. Aceleración y Eficiencia (en menor escala) de los algoritmos para la ejecución con 4 procesos Además, otra cosa notable es que ninguno de los algoritmos tiene una eficiencia mayor al 70%, lo que habla de que las sobrecargas por comunicación y otros cómputos están aproximadamente entre el 50% y el 70%. P=9;N=600 P=9;N=900 1.6000 4.0000 1.4000 3.5000 1.2000 3.0000 1.0000 2.5000 0.8000 2.0000 0.6000 1.5000 0.4000 1.0000 0.2000 0.5000 0.0000 0.0000 2D DNS CAN 2DDI- 2D DNS CANNON 2DDIAG- NON AGONAL ONAL P=9; N=1200 4.5000 4.0000 3.5000 3.0000 2.5000 2.0000 1.5000 1.0000 0.5000 0.0000 2D DNS CANNON 2DDIAGONAL Figure 12. Aceleración y Eficiencia (en menor escala) de los algoritmos para la ejecución con 8 y 9 procesos 21/27
  22. 22. Al aumentar el número de procesos a 9, podemos incluir a DNS en el análisis y se confirma lo que se puede notar con los tiempos de ejecución: DNS aumenta notablemente a medida que se procesan tamaños mayores de la entrada. Para N=600, DNS tiene un rendimiento inferior al algoritmo secuencial, mientras que al llegar a tamaños superiores al 1000, llega a alcanzar una eficiencia cercana al 70%. Otra cosa notable de estos resultados es el rendimiento superescalar de Cannon cuando la N llega a 1200. Por una lado, el entorno de ejecución paralelo de las pruebas no era totalmente homogéneo, y las pruebas secuenciales se ejecutaron en uno de los nodos que no necesariamente podría obtener los mejores resultados. Por otra parte, algunos de los nodos del entorno estaban equipados con procesadores de dos núcleos, lo cual pudo haber incrementado el nivel de paralelismo. Cualquiera de estos motivos puede explicar la superescalabilidad. O tal vez esta superescalabilidad se debe a alguna de las razones tradicionales explicadas en [1]: los algoritmos paralelos efectivamente realizan menos trabajo en proporción que el algoritmo secuencial, o la división de los datos hace que para los bloques más pequeños de datos, se aproveche mejor las capacidades de la cache de cada nodo. P=16;N=600 P=16;N=900 5.0000 1.4000 4.5000 1.2000 4.0000 1.0000 3.5000 3.0000 0.8000 2.5000 0.6000 2.0000 0.4000 1.5000 1.0000 0.2000 0.5000 0.0000 0.0000 2D DNS CANNON 2DDIAG- 2D DNS CANNO N 2DDIAGO NAL ONAL P=16;N=1200 4.0000 3.5000 3.0000 2.5000 2.0000 1.5000 1.0000 0.5000 0.0000 2D DNS CANNON 2DDIAGONAL Figure 13. Aceleración y Eficiencia (en menor escala) de los algoritmos para la ejecución con 16 y 27 procesos 22/27
  23. 23. Por otro lado, debido a la presencia de nodos con procesadores doble núcleo, teóricamente la aceleración debería dispararse entre los resultados con 4 procesos y aquellos obtenidos con 8 o 9. Sin embargo esto no sucede lo que nos lleva a la conclusión de que la implementación MPI utilizada no aprovecha al máximo las posibilidades de multiprocesamiento en los nodos. Los tres últimos gráficos de aceleración y eficiencia sencillamente confirman las tendencias presentadas en los demás resultados. Para tamaños grandes de matrices, la aceleración de DNS es superior a la de Cannon, probablemente por su menos sobrecarga de comunicación. Por otro lado, aunque esto no se nota en los gráficos, de los resultados extraídos en las pruebas, también pudimos notar al revisar los tiempos de ejecución de cada proceso ejecutado por un algoritmo, que el DNS es el que presenta mayor estabilidad. Por su parte, Cannon es el que presenta mayores variaciones entre los tiempos de ejecución de los distintos procesos. 2D Diagonal por su parte, presenta la misma inestabilidad que Cannon y bajos tiempos de ejecución en los procesos no diagonales, mientras que el tiempo de ejecución se dispara en la diagonal de la topología. 6 Conclusiones ✔ Se alcanzaron los objetivos de profundizar los conceptos de programación paralela a través de la implementación de algoritmos paralelos. ✔ Se pudo comprobar que la paralización de la solución efectivamente disminuye el tiempo de solución del mismo. ✔ En el caso particular de la multiplicación de matrices, casi todos los algoritmos aceleraron la solución, con excepción del algoritmo 2D cíclico. ✔ A partir de tamaños de matrices superiores a 1000, ninguno de los algoritmos paralelos tiene aceleración inferior a 1, y a medida que aumenta el tamaño, algunos algoritmos mejoran su rendimiento. ✔ Cannon ofrece, entre todos los algoritmos paralelos implementados, las mejores prestaciones en cuánto a tiempo de ejecución se refiere. ✔ DNS sin embargo, aumenta su rendimiento a medida que aumenta el tamaño de las matrices de entrada. ✔ En cuánto a la variabilidad de los algoritmos, entendiendo variabilidad como variación entre los tiempos de ejecución de cada proceso, DNS ha probado ser el algoritmo más estable. ✔ Cannon, sin embargo, es el más inestable en este sentido. ✔ 2D diagonal tiene una variabilidad similar a la de Cannon, con el agregado de que los procesos diagonales presentan tiempos de ejecución muy superiores que los que no son diagonales. 7 Trabajos Futuros • Mejorar el modelo maestro-esclavo del algoritmo 2D cíclico: el problema fundamental en la implementación del algoritmo 2D cíclico, es que el proceso maestro se convierte en un cuello de botella cuando se procesan matrices grandes. Una solución interesante que puede aumentar dramáticamente el rendimiento de este algoritmo es dedicar uno de los procesos a la tarea exclusiva de ser maestro y distribuidor de tareas. 23/27
  24. 24. Esto podría permitir que efectivamente se optimice el solapamiento del cómputo con las comunicaciones, y se reduciría al mínimo el Idling time gracias a que tan pronto un proceso quede libre, seriá asignado a una nueva tareas. Otra mejora en este modelo consiste en reducir la restricción de asignación cíclica, y permitir que la asignación de tareas se realice directamente al procesador que quedó libre primero. Esto se puede implementar fácilmente reemplazando la asignación circular, por un mecanismo de cola de procesos a la que se irán agregando los procesos por orden de llegada a medida que quedan libres. Finalmente, una mejora más que se le podría realizar al algoritmo 2D consiste en implementar un mecanismo de pre-empaquetamiento de datos a enviar. Es decir, una vez que el proceso maestro termino de asignar una ráfaga de tareas, que comience inmediatamente a empaquetar los próximos mensajes para que cuando llegue el momento, estos estén listos y se minimice el tiempo de ociosidad del proceso maestro. • Aprovechamiento de las capacidades multi-hilos de los procesadores con múltiples núcleos: MPI no aprovecha las capacidades de los procesadores de manejar hilos. El resultado es que cada proceso es lanzado en cada procesador como un proceso pesado. Un trabajo interesante seriá realizar una implementación de estos algoritmos sobre una implementación de MPI que aproveche la capacidad de multihilado de los procesadores, como la que se propone en [8], y lance las tareas como hilos en cada nodo y no como procesos pesados. En los resultados obtenidos por este trabajo se notó que la utilización de procesadores doble-núcleo en la ejecución de los procesos por medio de MPI, no generaba mejoras muy notables en la aceleración. • Otro trabajo interesante, sería analizar el rendimiento de estos algoritmos para matrices no densas, en busca de posibles optimizaciones particulares a los mismos aplicables a distintos tipos de matrices especiales. 8 Referencias [1] Ananth Grama, Anshul Gupta, George Karypis, Vipin Kumar: Introduction to Parallel Computing, Second Edition. Addison Wesley. 2003. [2] MatrixMarket - Mathematical and Computational Sciences Division NIST (National Institute Standars and Technology) - http://math.nist.gov/MatrixMarket/ [3] http://en.wikipedia.org/wiki/Matrix_(mathematics) [4] Generalized Cannon's algorithm for parallel matrix multiplication. Hyuk-Jae Lee, James P. Robertson, José A. B. Fortes. [5] Communication Efficient Matrix Multiplication on Hypercubes. Himanshu Gupta, P. Sadayappan. [6] Rendimiento de Algoritmos Paralelos de Multiplicación de Matrices implementados con Hilos. Cristhian Parra, Fernando Mancía. Facultad Politécnica de la Universidad de Asunción, 2008. [7] www.open-mpi.org [8] F. García, A. Calderón, J. Carretero, MiMPI: a multithread-safe implementation of MPI, in: Recent Advances in Parallel Virtual Machine and Message-Passing Interface, Proceedings of the Sixth European PVM/MPI Users' Group Meeting, Lecture Notes in Computer Science 1697, Springer, September 1999, pp. 207-214. 24/27
  25. 25. 9 Anexo: Tablas de Resultados. Re sultados obte nidos con 4 proc e sos Tie mpo Tie mpo Ac ele rac Sobre Efic ie nci Eficie n Efic ie nc Alg oritmo Tamaño Costo Parale lo Se c ue nc ial ión C osto a c ia* ia** 2D 40 0 2,08 4 6 1,0727 0,514 6 7,2657 0,128 6 8 ,338 4 0,128 6 0,0735 CANNON 40 0 0,4 4 8 4 1,0727 2,3924 0,7208 0,598 1 1,7934 0,598 1 0,34 18 2DDIAG ONAL 40 0 0,7767 1,0727 1,38 10 2,034 2 0,34 53 3,1069 0,34 53 0,1973 2D 576 5,29 80 1,4811 0 ,279 6 19 ,710 9 0 ,0 6 9 9 -- -- -- CANNON 600 1,7351 1,4 8 11 0,8 536 5,4 591 0,2134 6,94 03 0,2134 0,1219 2DDIAG ONAL 600 3,0172 1,4 8 11 0,4 909 10,58 76 0,1227 12,068 7 0,1227 0,0701 2D 900 15,7728 16,8 34 0 1,0673 4 6,2572 0,2668 63,0913 0,2668 0,1525 CANNON 900 5,6179 16,8 34 0 2,9965 5,6375 0,74 91 22,4 715 0,74 91 0,4 28 1 2DDIAG ONAL 900 8 ,5221 16,8 34 0 1,9753 17,254 2 0,4 938 34 ,08 8 2 0,4 938 0,28 22 2D 129 6 40 ,2125 -- -- -- -- -- -- -- CANNON 120 0 13,3522 4 0,568 7 3,038 3 12,8 4 02 0,7596 53,4 08 8 0,7596 0,4 34 0 2DDIAG ONAL 120 0 20,8 328 4 0,568 7 1,94 73 4 2,7626 0,4 8 68 8 3,3313 0,4 8 68 0,278 2 2D 16 0 0 69,5011 8 7,1772 1,254 3 190,8 274 0,3136 278 ,004 5 0,3136 0,1792 CANNON 150 0 30,7217 -- -- -- -- -- -- -- 2DDIAG ONAL 150 0 4 1,9274 79,5236 1,8 967 8 8 ,18 61 0,4 74 2 167,7097 0,4 74 2 0,2710 2D 180 0 -- -- -- -- -- -- -- -- CANNON 180 0 -- -- -- -- -- -- -- -- 2DDIAG ONAL 180 0 73,3709 137,5139 1,8 74 2 155,9695 0,4 68 6 293,4 8 34 0,4 68 6 0,2677 2D 250 0 223,4 08 8 377,6175 1,6903 516,0178 0,4 226 8 93,6353 0,4 226 0,24 15 CANNON 250 0 -- -- -- -- -- -- -- -- Tabla 1. Resultados en la ejecución de los algoritmos con 4 procesos. 25/27
  26. 26. Re sultados obte nidos c on 9 proc e sos (8 para DNS) Tie mpo Tam Tie mpo Ac e le ra Sobre Efic ie n Efic ie n Efic ie n Alg oritmo Se c ue nc ia C osto año Parale lo c ión C osto c ia c ia* c ia** l 2D 441 2,3232 1,4 8 11 0,6375 19,4 273 0,0708 20,908 4 0,1594 0,0911 DNS 40 0 0,4 220 1,0727 2,54 16 2,3037 0,3177 3,3764 0,6354 0,3631 CANNO N 40 0 -- -- -- -- -- -- -- -- 2DDIAG O NAL 40 0 -- -- -- -- -- -- -- -- 2D 576 4,4131 1,4811 -- -- -- -- -- -- DNS 600 1,8514 1,4811 0 ,80 0 0 13,330 3 0 ,10 0 0 14,8114 0 ,20 0 0 0 ,1143 CANNO N 600 1,04 11 1,4 8 11 1,4 227 7,8 8 8 4 0,158 1 9,3695 0,3557 0,2032 2DDIAG O NAL 6 0 0 2,138 7 1,4 8 11 0,6925 17,7671 0,0769 19,24 8 2 0,1731 0,098 9 2D 900 13,6772 16,8 34 0 1,2308 106,2608 0,1368 123,094 9 0,3077 0,1758 DNS 900 5,7700 16,8 34 0 2,9175 29,3256 0,364 7 4 6,1596 0,7294 0,4 168 CANNO N 900 5,0051 16,8 34 0 3,3634 28 ,2121 0,3737 4 5,04 61 0,8 4 08 0,4 8 05 2DDIAG O NAL 9 0 0 7,5669 16,8 34 0 2,224 7 51,268 2 0,24 72 68 ,1023 0,5562 0,3178 2D 129 6 35,2415 -- -- -- -- -- -- -- DNS 120 0 14,176 2 40 ,56 87 2,86 17 72,840 7 0 ,3577 113,40 9 4 0 ,7154 0 ,40 88 CANNO N 120 0 10,0516 4 0,568 7 4 ,0361 4 9,8 953 0,4 4 8 5 90,4 64 0 1,0090 0,5766 2DDIAG O NAL 120 0 17,3277 4 0,568 7 2,34 13 115,38 07 0,2601 155,94 94 0,58 53 0,334 5 2D 176 4 79,54 51 129,7509 1,6312 58 6,154 9 0,18 12 715,9058 0,4 078 0,2330 DNS 16 0 0 -- -- -- -- -- -- -- -- CANNO N 150 0 22,6051 79,5236 3,5179 123,9226 0,3909 203,4 4 63 0,8 795 0,5026 2DDIAG O NAL 150 0 34 ,8 575 79,5236 2,28 14 234 ,1936 0,2535 313,7172 0,5703 0,3259 2D 176 4 79 ,5451 129 ,750 9 1,6 312 586 ,1549 0 ,1812 715,9 0 58 0 ,40 78 0 ,2330 DNS 180 0 -- -- -- -- -- -- -- -- CANNO N 180 0 -- -- -- -- -- -- -- -- 2DDIAG O NAL 180 0 62,4 08 9 137,5139 2,2034 4 24 ,1659 0,24 4 8 561,6798 0,5509 0,314 8 2D 250 0 -- -- -- -- -- -- -- -- DNS 250 0 -- -- -- -- -- -- -- -- CANNO N 250 0 -- -- -- -- -- -- -- -- 2DDIAG O NAL 270 0 231,6767 4 93,714 2 2,1310 1591,3765 0,2368 208 5,0907 0,5328 0,304 4 Tabla 2. Resultados en la ejecución de los algoritmos con 9 procesos. 26/27
  27. 27. Re sultados obte nidos c on 16 proc e sos (27 proc e sos para DNS) Tie mpo Tam Tie mpo Ac e le ra Sobre Efic ie n Efic ie n Efic ie n Alg oritmo Se c ue nc ia C osto año Parale lo c ión C osto c ia c ia* c ia** l 2D 40 0 1,8 58 8 1,0727 0,5771 28 ,668 1 0,0361 29,74 07 0,14 4 3 0,08 24 DNS 40 0 -- -- -- -- -- -- -- -- CANNO N 40 0 0,398 7 1,0727 2,6905 5,3062 0,168 2 6,378 9 0,6726 0,38 4 4 2DDIAG O NAL 40 0 0,718 8 1,0727 1,4 924 10,4 277 0,0933 11,5004 0,3731 0,2132 2D 576 4,6 245 1,4811 -- -- -- -- -- -- DNS 600 1,49 31 1,4811 0 ,9 9 20 38,8323 0 ,0 36 7 40 ,3134 0 ,2480 0 ,1417 CANNO N 600 1,238 7 1,4 8 11 1,1958 18 ,3373 0,074 7 19,8 18 4 0,298 9 0,1708 2DDIAG O NAL 6 0 0 2,3018 1,4 8 11 0,64 35 35,34 8 3 0,04 02 36,8 294 0,1609 0,0919 2D 10 24 71,1670 78 ,6965 1,1058 1059,9755 0,0691 1138 ,6720 0,2765 0,158 0 DNS 900 8 ,64 99 16,8 34 0 1,94 61 216,714 0 0,0721 233,54 8 1 0,4 8 65 0,278 0 CANNO N 900 3,7332 16,8 34 0 4 ,5093 4 2,8 971 0,28 18 59,7312 1,1273 0,64 4 2 2DDIAG O NAL 80 0 5,5556 10,9094 1,9637 77,98 03 0,1227 8 8 ,8 8 96 0,4 909 0,28 05 2D 129 6 39 ,6 770 -- -- -- -- -- -- -- DNS 120 0 11,4744 40 ,56 87 3,5356 26 9 ,239 9 0 ,130 9 30 9 ,80 85 0 ,8839 0 ,50 51 CANNO N 120 0 12,7215 4 0,568 7 3,18 90 162,9753 0,1993 203,54 4 0 0,7972 0,4 556 2DDIAG O NAL 120 0 21,0735 4 0,568 7 1,9251 296,6076 0,1203 337,1763 0,4 8 13 0,2750 2D 16 0 0 69,0511 8 7,1772 1,2625 1017,6399 0,078 9 ### 0,3156 0,18 04 DNS 150 0 25,1913 79,5236 3,1568 600,64 25 0,1169 68 0,1661 0,78 92 0,4 510 CANNO N 150 0 24 ,5998 79,5236 3,2327 314 ,0730 0,2020 393,5967 0,8 08 2 0,4 618 2DDIAG O NAL 150 0 39,2616 79,5236 2,0255 54 8 ,6622 0,1266 628 ,18 58 0,5064 0,28 94 2D 180 0 -- -- -- -- -- -- -- -- DNS 180 0 43,30 0 1 137,5139 3,1758 10 31,5883 0 ,1176 116 9 ,10 22 0 ,79 40 0 ,4537 CANNO N 180 0 -- -- -- -- -- -- -- -- 2DDIAG O NAL 180 0 73,7756 137,5139 1,8 639 104 2,8 94 9 0,1165 ### 0,4 660 0,2663 2D 270 0 -- -- -- -- -- -- -- -- DNS 270 0 -- -- -- -- -- -- -- -- CANNO N 270 0 -- -- -- -- -- -- -- -- 2DDIAG O NAL 270 0 236,38 50 4 93,714 2 2,08 8 6 328 8 ,4 4 57 0,1305 378 2,1599 0,5222 0,298 4 Tabla 3. Resultados en la ejecución de los algoritmos con 16 y 27 procesos. 27/27

×