Pgina 1 1.- Introducc|n El presente trabajo constituye la entrega del trabajo de curso de la asignatura Introduccin a la Computacin Paralela, del Mster Universitario en Sistemas Inteligentes y Aplicaciones Numricas en la Ingeniera. Consiste en la implementacin del Juego de la Vida de Conway de forma secuencial y paralela. Esta ltima se realiza haciendo uso de las libreras OpenMP y MPI. Dado que se trata de un problema clsico, en esta memoria se obviar la explicacin del mismo. Tambin, ya que los conceptos fundamentales de las citadas libreras se han visto en clase, no se har descripcin alguna de las mismas, salvo que fuera necesario en alguna parte de la solucin desarrollada. De esta forma, en las siguientes secciones se ver: en la parte de desarrollo se describir la solucin adoptada, tanto para la versin secuencial como para las versiones paralelas. En el apartado de resultados se vern cules son las salidas que se han obtenido, considerando que se han hecho simulaciones para diferentes tamaos del grid y diferentes configuraciones en las versiones paralelas. Por ltimo, en la seccin de conclusiones, se har un anlisis de los resultados que se han obtenido. 2.- Desarro||o En esta seccin se describir la solucin implementada, tanto su versin secuencial como las versiones paralelas. Inicialmente se describir la primera, para posteriormente indicar cules han sido las modificaciones necesarias para la obtencin de las otras dos. En principio, este proceso es casi trivial en el caso de OpenMP, mientras que en el caso de MPI es preciso realizar un mayor nmero de cambios. 2.1.- rograma Secuenc|a| Una de las consideraciones previas que se deben tener al comenzar es decidir si se va a hacer o no un tratamiento toroidal del grid. Aunque en las primeras fases, por simplicidad, se tom la decisin de no hacerlo, finalmente se ha incorporado esta funcionalidad. El tratamiento que se realiza en este sentido es igual tanto en este caso, el secuencial, como en la implementacin en OpenMP. Sin embargo, en el caso del desarrollo en MPI, se han hecho ligeros cambios, los cules sern descritos en el apartado 2.3. La implementacin est compuesta por un conjunto de funciones. Es por ello que en los siguientes apartados se va a tratar de describir todas y cada una de ellas, en el mismo orden en el que se pueden encontrar en el fichero correspondiente. Dicho archivo es el denominado como sequential.cpp. 2.1.1.- r|ntMat Este procedimiento es el encargado de imprimir en un fichero el resultado de las sucesivas iteraciones. Para ello se hace uso de la librera fstream de C++ para el tratamiento de ficheros. Recibe como parmetros un puntero al vector que representa el grid y la dimensin del mismo. Dependiendo de la dimensin del grid, se crea un fichero que comienza por output_grid_X donde X es el tamao de la matriz. El fichero siempre se abre en modo adicin, de forma que si este ya existe, el resultado se aadir al final del mismo. Dentro del fichero se representa la matriz donde cada posicin est asociada a un smbolo. Si el elemento est vivo, es decir, su valor es 1, se escribir en el fichero un asterisco *. Sin embargo, si est muerta, se escribir un punto . Al terminar, se deja un espacio doble. De esta manera es sencillo diferenciar entre una iteracin y la siguiente. 2.1.2.- CopyMat Este procedimiento se ocupa de copiar una matriz en otra. Debido a que el proceso va escribiendo en la posicin asociada de la matriz, siempre se tiene una copia del resultado de la iteracin anterior para realizar la actualizacin. Recibe como parmetros dos punteros uno al vector de la matriz y otro a la copia, adems del tamao. Introduccin a la Computacin Paralela Mster SIANI
Pgina 2 Una vez terminada la iteracin se invoca a CopyMat para que actualice la matriz y as pasar a la siguiente iteracin. 2.1.3.- CountNe|ghbours Esta funcin es la encargada del conteo de los vecinos de una posicin dada, usando la vecindad de Moore, es decir, sus ocho vecinos. Para ello se actualizan en cada caso los valores de ocho variables que representan las ocho diferentes posiciones: esquina superior izquierda, borde superior, esquina superior derecha, borde izquierdo, borde derecho, esquina inferior izquierda, borde inferior y esquina inferior derecha. Recibe como parmetros el puntero al vector, la posicin (i,j) y el tamao. En primer lugar se tiene en cuenta el caso general, es decir, que se est dentro de la matriz. Para ello los ndices (i,j) de la posicin indicada han de ser ambos mayores que cero e inferiores a la dimensin de la matriz. Si no es as, se comienza a chequear si estamos en alguno de los casos descritos anteriormente: las cuatro esquinas o los cuatro bordes. En cada uno de dichos casos se actualiza adecuadamente el valor que se ha de coger del vecino adecuado, accediendo a la posicin correcta del vector que representa el grid. Un ejemplo de este procedimiento puede verse en la ilustracin 1. (n-1,n-1) (n-1,0) (n-1,1)
(0,n-1) (0,0) (0,1) (1,n-1) (1,0) (1,1)
Ilustracin 1: ejemplo de tratamiento toroidal para la esquina superior izquierda. En azul el elemento a tratar, en verde sus vecinos adyacentes y en rojo sus vecinos toroidales Finalmente se devuelve el valor correspondiente a la suma de los valores de esos ocho vecinos ya que cada uno de ellos slo puede valor cero o uno, en funcin de si est muerto o vivo respectivamente. 2.1.4.- GameCfL|ve Este procedimiento es el encargado de realizar el Juego de la Vida de Conway. Para ello recibe por parmetro el puntero a la matriz, la copia de la misma que es la que se actualiza y el tamao. Simplemente se recorre toda la matriz y va invocando a la funcin anterior, CountNeighbours, para saber cundo actualizar o no el valor asociado a cada posicin. La actualizacin se realiza mediante una operacin a nivel de bits: copy[i*size+j] = (NumNeighbours == 3) | (NumNeighbours==2 & m[i*size+j]); Esta operacin permite hacer la actualizacin de una manera correcta y simple. 2.1.S.- In|tMatr|x Este procedimiento se encarga de la inicializacin de la matriz. Recibe un puntero al vector y el tamao. Es dependiente de una macro denominada RAND_INIT. En funcin de si esta macro vale uno o cero, se proceder a hacer una inicializacin aleatoria o una conocida para hacer pruebas. Esta configuracin predeterminada es la que se muestra en el enunciado de la prctica y es la usada siempre para saber si todo est funcionando correctamente. Introduccin a la Computacin Paralela Mster SIANI
Pgina 3 Los valores a cero representan que la clula o elemento en cuestin est muerto y el valor uno que est vivo. En el caso de la inicializacin aleatoria se hace uso de la funcin rand() de C++ a la que se le aplica un mdulo dos, para as siempre obtener como resultado valores cero o uno. 2.1.6.- Ma|n Es el programa principal. En este caso no recibe ningn tipo de parmetro. Inicialmente se tienen dos punteros, uno para la matriz y la copia de la misma. Luego se tienen dos vectores para las pruebas. El primero de ellos es para diferentes tamaos del grid: diez, cincuenta, cien, doscientos cincuenta, quinientos y mil. Este tamao se refiere a la longitud del lado, es decir, las matrices sern de diez por diez y as sucesivamente. El otro vector es para el nmero de iteraciones: quinientas y mil. Adems de estas variables se tienen dos ms para el cmputo del tiempo al principio y al final, para saber cunto tarda la ejecucin. Terminada la declaracin de variables, se tienen dos bucles anidados, uno para iterar en el vector de tamaos de grid y el otro sobre el nmero de iteraciones. De esa forma se pueden hacer de manera automtica las distintas pruebas. A continuacin se reserva la memoria tanto para el grid principal como para la copia del mismo. Recordemos que esto se hace as porque en el proceso del Juego de la Vida se va cambiando la matriz y se ha de tener una copia en sobre la que ir mirando las vecindades de los elementos que se van a actualizar. Una vez se ha cogido la memoria, se inicializa el vector y se obtiene el valor para la variable inicial del tiempo. Se hace la primera copia del array y se comienzan las iteraciones. En cada una de las iteraciones, si no se est computando el tiempo (macro COMPUTE_TIME a cero) se realiza la impresin del grid invocando al procedimiento PrintMat, para ir viendo el proceso en un fichero. Realmente el tiempo se computa siempre y esta macro slo sirve para determinar si se escribe o no en el fichero de salida. Obviamente, adems de dicha impresin, en cada iteracin se invoca al procedimiento GameOfLive anteriormente descrito. Una vez finaliza el bucle se determina el tiempo final. Posteriormente, se imprime el ltimo estado del grid (teniendo en cuenta el valor de COMPUTE_TIME) y se muestra por pantalla un mensaje que indica el tamao del grid, las iteraciones realizadas y el tiempo transcurrido en dicha operacin. Antes de pasar al siguiente valor para las iteraciones o tamao, se libera la memoria alojada para el vector principal y la copia del mismo. 2.2.- CpenM La paralelizacin del cdigo usando la librera OpenMP es casi inmediata. Simplemente es necesario para empezar incluir la librera omp.h. La solucin en la que se ha usado OpenMP se encuentra en el fichero openmp.cpp. En el proceso de paralelizacin del cdigo es preciso tener en cuenta que oportunidades de procesamiento paralelo se tienen en l. En nuestro desarrollo se encuentran principalmente dos: una de ellas es en el proceso del Juego de la Vida y la otra en la copia del vector principal en un vector soporte. 2.2.1.- CopyMat Ya que se trata de un bucle, simplemente es necesaria aadir la directiva de paralelizacin de un bucle para que OpenMP lo procese. Es decir: #pragma omp parallel for for(int i=0; i<size*size; i++) { copy[i] = m[i]; } 2.2.2.- GameCfL|ve En este caso se tienen dos bucles anidados. Se ha tomado la decisin de poner la directiva en el bucle ms externo, declarando como variable privada el contador del bucle interno y que as no sea cambiado por todos Introduccin a la Computacin Paralela Mster SIANI
Pgina 4 los hilos pudiendo causar esto un mal resultado. No se ha realizado una inversin de bucles, ya que esto puede causar ms fallos de cach y, por tanto, una reduccin de rendimiento. El cdigo quedara como: #pragma omp parallel for private(j) for(int i = 0; i<size; i++) { for(j = 0; j<size; j++) { int NumNeighbours = CountNeighbours(m,i,j,size); copy[i*size+j] = (NumNeighbours == 3) | (NumNeighbours==2 & m[i*size+j]);
} } Podra considerarse que, a parte de estas dos oportunidades de paralelizacin, existiera una tercera en la funcin que se ocupa de la escritura en fichero. Pero como esta funcin no se ejecuta cuando se estn realizando las pruebas, no tiene inters alguno en esto caso paralelizarla. 2.2.3.- Ma|n Tambin se han realizado algunos cambios en el programa principal. Se ha aadido una nueva variable al principio. Se trata de un vector en el que se indican el nmero de hilos que se van a lanzar, que sern iguales al nmero de procesadores que se van a usar en las pruebas con MPI: dos, cinco y diez. Por ello, ahora no se tienen slo los dos bucles relativos al tamao y el nmero de iteraciones, si no que adems se tiene un tercer bucle anidado referente al nmero de hilos que se usan en la ejecucin OpenMP (el bucle ms externo). Las variables para el clculo del tiempo han pasado a ser de tipo double, usndose para la estimacin la funcin omp_get_wtime() de OpenMP. El resto de cdigo sigue siendo prcticamente el mismo que en la versin secuencial. 2.3.- MI A diferencia del caso anterior, en el caso de MPI si hay que hacer algunos cambios ms, ya que ahora no se hace uso de directivas, si no de funciones de la propia librera. Es tarea del programador dividir adecuadamente el problema para que sea solucionado por cada uno de los procesadores. El fichero en el que se encuentra esta solucin es el denominado mpi.cpp. Es necesario, al igual que antes, incluir la librera de MPI, mpi.h. Como se ha hecho en las secciones anteriores, se describirn los cambios hechos en cada una de las funciones que hayan sido modificadas. En esta versin adems de las macros COMPUTE_TIME y RAND_INIT (de escritura en fichero y tipo de inicializacin respectivamente) se tiene una ms, DEBUG, para mostrar mensajes de depuracin si es preciso. 2.3.1.- CopyMat En las anteriores versiones de esta funcin, el tamao size pasado se refera a una de las dimensiones del grid, de forma que luego se recorra el vector hasta el tamao size*size. Sin embargo, como en MPI vamos a tener tamaos variables de diferentes zonas del grid, el tamao que ahora se le pasa a la funcin es el correspondiente a todo el trozo de vector directamente y no slo el referente a una dimensin. Es as que ahora el lmite superior del bucle es directamente size. 2.3.2.- CountNe|ghbours Se sigue la misma filosofa que en las implementaciones anteriores, salvo que ahora hay algunos controles que no se hacen. Al igual que antes, se sigue teniendo en cuenta el caso en el que se est explorando el interior de la matriz y tambin se tiene en cuenta que se est en lo que representara la primera columna o la ltima. Sin embargo, se ha quitado todo el tratamiento de las esquinas y de los bordes superior e inferior. Esto es as porque ahora en cada trozo de matriz que se vaya a tratar, existir una copia de la fila inmediatamente superior e inferior al trozo que se tiene. De esta manera, no habr riesgo de salirse por esas regiones. Introduccin a la Computacin Paralela Mster SIANI
Pgina 5 Sobre cmo se divide el grid se hablar ms en produndidad en la seccin 2.3.5. 2.3.3.- GameCfL|fe La diferencia ms significativa en este procedimiento es que ahora no se recorre la altura del grid por completo, si no desde uno hasta height-1, por la razn que se ha dicho en la operacin anterior. El resto de su cdigo sigue siendo igual salvo que ahora a la funcin de contar los vecinos, ya que slo se hacen comprobaciones en el ancho del grid, se le pasa como tamao la variable width. 2.3.4.- I|||Lxtended Este procedimiento es nuevo. Debido a que siempre se tratar que cada trozo de la matriz vaya acompaado de la fila anterior y la siguiente, se crey necesario tener una funcin encargada de crear una versin extendida tanto del grid original como de la copia del mismo. Este procedimiento hace esa labor rellenando el interior del vector con los mismos valores que las originales y en aquellas posiciones que representan la fila extra superior y la fila extra inferior del grid, se ponen los valores de la ltima fila y la primera respectivamente. A estas filas se les ha denominado como phantom rows o filas fantasma. En la ilustracin 2 se puede ver una representacin grfica.
Ilustracin 2: grid con sus filas fantasma 2.3.S.- Ma|n En el programa principal es donde se ha hecho el mayor nmero de cambios. Se ha tratado de seguir el mismo estilo con los vectores de tamao e iteraciones, aunque ahora mismo cada uno de ellos tiene un solo valor. Cuando se quiere hacer una nueva prueba, simplemente es necesario cambiar cualquiera de estos dos valores. Adems de los dos punteros para el grid original y su copia, se tienen dos ms para sus versiones extendidas, de las cuales se ha hablado anteriormente. Ha de tenerse en cuenta que ya que se est usando C++, las operaciones de MPI usadas son las equivalentes para este lenguaje, ya que la librera est disponible para varios de ellos. Inicialmente se inicializa MPI y se obtiene el identificador del procesador, as como el nmero de procesadores existentes. Tras ello, se computa el tamao del trozo que se va a mandar a cada procesador, en concreto lo que representara la altura de dicho trozo (chunksize, vase chunksize en la ilustracin 3). Ntese que se ha supuesto de entrada que el alto del grid es totalmente divisible entre el nmero de procesadores. Con este conjunto de operaciones previas realizadas, se inicializa el contador de tiempo y empieza al proceso. Si se trata del procesador master (el que tiene el identificador cero) se comienza por tomar memoria para el grid, la copia, las versiones extendidas, un vector de tamaos de los trozos, otro de desplazamientos y dos buffers locales, uno para el trozo en si y otro para su copia. Despus se pasa a inicializar el vector, hacer la copia y rellenar las versiones extendidas. Tras ello se rellenan los vectores de tamaos y desplazamientos. El tamao que se va a retransmitir siempre ser (chunksize+2)*size, es decir, la altura del trozo ms las dos filas extra por el ancho de esas filas (vase ilustracin 3). En relacin a los desplazamientos, para el primer trozo ser cero, mientras para los restantes ser el equivalente al rea ocupada por un trozo bsico, es decir, Introduccin a la Computacin Paralela Mster SIANI
Pgina 6 sin las dos filas extra, multiplicado adems por el ndice del bucle. Se parte del trozo bsico para este cmputo puesto que siempre se ha de dejar la ltima fila del trozo para ser enviada como primera fila fantasma de la siguiente porcin de grid. En el caso de no ser el procesador master, simplemente se coge memoria para los buffers locales. Una vez se tienen las inicializaciones, se pasa al bucle de las iteraciones. En primer lugar, si se trata del procesador master y no est activa la macro COMPUTE_TIME se imprime el estado del grid. A continuacin se realiza la reparticin entre todos los procesadores usando la operacin Scatterv. A sta se le indica que se va a repartir partiendo de la versin extendida, con los tamaos indicados en el vector de tamaos y los desplazamientos del vector correspondiente. Se transmiten valores enteros y sern recibidos en el buffer local, cuyo tamao es el indicado anteriormente (chunksize+2)*size. Este buffer ser tambin de valores enteros.
Ilustracin 3: divisin por procesador suponiendo un tamao de grid 9 y 3 procesadores Tras ello, se hace una copia del buffer local a su copia y se invoca a GameOfLive para ejecutar el Juego de la Vida de Conway con el trozo actual. El ancho indicado ser size y el alto chunksize+2. A continuacin, si se trata del procesador master, se hace un envo del resultado del Juego de la Vida, usando la funcin Send de MPI. Tngase en cuenta que en todos los envos no se enva el buffer entero, si no que se obvian la primera y ltima fila, ya que el resultado slo se encuentra en la regin restante. Es por ello que el buffer en el envo se desplaza un tamao size y que el tamao a enviar es directamente chunksize*size, dejndose as la ltima fila. Posteriormente, para todos los procesadores, el master recibir los resultados mediante el mtodo Recv, que recibe de forma estndar en modo bloqueante. Hay que tener en cuenta que la recepcin se hace sobre la copia del grid. La direccin inicial va siendo desplazada en funcin del procesador del que se recibe y del tamao de cada trozo. En el caso de no estarse en el master, simplemente se hace el envo del resultado de la misma forma que se ha indicado antes. Despus se actualiza el grid principal con el resultado almacenado en la copia y se actualizan tambin las extendidas. Con ello terminara el bucle de iteraciones. Tras salir de l, si se est en el master y no se encuentra activa la macro indicada antes, se procede a imprimir el que sera el estado final del grid. Para terminar se obtiene nuevamente el tiempo. En el caso, nuevamente, de estar en el master, se imprime por pantalla el resultado obtenido, indicndose el tamao del grid, el nmero de iteraciones, el nmero de procesadores y el tiempo transcurrido. Por ltimo se finaliza MPI.
Introduccin a la Computacin Paralela Mster SIANI
Pgina 7 3.- kesu|tados En esta seccin se describen los resultados obtenidos para las tres implementaciones desarrolladas as como las mejoras que se observan entre ellas. 3.1.- Secuenc|a| En la ilustracin 4 se puede observar la simulacin del algoritmo secuencial para tamaos de grid 10, 50, 100, 250, 500 y 1000. A la izquierda la grfica corresponde a la ejecucin de 500 iteraciones en cada configuracin y a la derecha de 1000 iteraciones. 500 Iteraciones
1000 38,386500 Tabla 1: resultados de la implementacin secuencial
Ilustracin 4: implementacin secuencial para tamaos 10, 50, 100, 250, 500 y 1000. A la izquierda 500 iteraciones y a la derecha 1000 Puede observarse como el tiempo de ejecucin permanece estable hasta un tamao de grid de 100x100. En cuanto se pasa tamaos superiores el tiempo aumenta de forma significativa. Obviamente, este aumento es superior en los experimentos en los que se ejecutan mil iteraciones, llegndose a alcanzar los 38 segundos para un grid de 1000x1000 elementos.
Introduccin a la Computacin Paralela Mster SIANI
Pgina 8 3.2.- CpenM En la ilustracin 5 se muestran los resultados de simular en la implementacin OpenMP con tamaos de grid de 10, 50, 100, 250, 500 y 1000, usando 2, 5 y 10 hilos. En la grfica de la izquierda se representan los experimentos en los que se han ejecutado 500 iteraciones y a la derecha 1000. 500 Iteraciones Tamao OMP2(seg) OMP5(seg) OMP10(seg) 10 0,022084 0,0343311 0,021143 50 0,0470359 0,048281 0,047272 100 0,125776 0,132642 0,128779 250 0,693114 0,690712 0,684054 500 2,8273 2,75767 2,69044 1000 11,1124 10,6826 11,0304 Tabla 2: resultados implementacin OpenMP 500 iteraciones 1000 Iteraciones Tamao OMP2(seg) OMP5(seg) OMP10(seg) 10 0,042707 0,0639539 0,042717 50 0,0942791 0,0958819 0,0946531 100 0,249386 0,308115 0,250017 250 1,37422 1,35994 1,36421 500 5,95425 5,23784 5,28778 1000 22,8925 22,6414 22,3838 Tabla 3: resultados implementacin OpenMP 1000 iteraciones
Ilustracin 5: implementacin OpenMP para tamaos de grid de 10, 50, 100, 250, 500 y 1000, usando 2, 5 y 10 hilos. A la izquierda 500 iteraciones y a la derecha 1000 Al igual que sucediera con el cdigo secuencial, existe cierta estabilidad hasta el grid de tamao 100x100 tanto en 500 como en 1000 iteraciones. A partir de ese tamao el tiempo de ejecucin aumenta, siendo mayor en el caso de la ejecucin de 1000 iteraciones. No obstante, hay una ganancia significativa en tiempo en comparacin con la implementacin anterior, ya que en el peor de los casos (grid de 1000x1000 con 1000 iteraciones) la ejecucin tarda unos 22,38 segundos. Esto supone una reduccin de unos 16 segundos respecto al mismo caso en la implementacin secuencial. A continuacin se muestra el clculo del Speedup tanto para 500 como para 100 iteraciones.
Speedup 1000 Tamao OMP2 OMP5 OMP10 10 0,093895 0,064108 0,093873 50 1,014498 0,997539 1,010489 100 1,532708 1,240562 1,528840 250 1,730261 1,748430 1,742957 500 1,597480 1,815977 1,798826 1000 1,676815 1,695411 1,714923 Tabla 5: Speedup 1000 iteraciones OpenMP Speedup promedio: OMP2 = 1,274276 OMP5 = 1,260337 OMP10 = 1,314984 Tanto en 500 como en 1000 iteraciones se puede observar que el Speedup promedio mejora en todos los casos a la implementacin secuencial, correspondiendo la mayor mejora a aquella que usa un mayor nmero de hilos. Donde nico se puede ver que el programa con OpenMP no mejora al secuencial es para tamaos de matriz pequeos, en los que incluso la versin paralela parece ser ligeramente ms lenta que la secuencial, dando un Speedup inferior a 1. 3.3.- MI En la ilustracin 6 se representan los resultados de la ejecucin de la implementacin usando MPI para grids de tamao 10, 50, 100 y 250, usando 2, 5 y 10 procesadores. En la grfica de la izquierda se muestran los resultados para la ejecucin de 500 iteraciones y en la derecha para 1000. 500 Iteraciones Tamao MPI2(seg) MPI5(seg) MPI10(seg) 10 0,00342512 0,0203719 0,03423 50 0,0483229 0,037904 0,0462348 100 0,175886 0,135032 0,275421 250 1,07531 0,810167 0,765726 Tabla 6: resultados implementacin MPI 500 iteraciones 1000 Iteraciones Tamao MPI2(seg) MPI5(seg) MPI10(seg) 10 0,00826001 0,0158761 0,026705 50 0,091737 0,072882 0,103934 100 0,322796 0,272283 0,282494 250 1,9295 1,53234 1,67017 Tabla 7: resultados implementacin MPI 1000 iteraciones Introduccin a la Computacin Paralela Mster SIANI
Pgina 10
Ilustracin 6: implementacin MPI para grids de tamao 10, 50, 100 y 250, usando 2, 5 y 10 procesadores. Al izquierda 500 iteraccines y a la derecha 1000 A diferencia de las dos implementaciones anteriores, en este caso no se encuentra tanta estabilidad en las primeras configuraciones de grid, aunque esto tambin puede ser debido a que en este experimento se exploran tamaos menores y se ve ms en detalle los tiempos para dichos tamaos. No obstante, se observa que despus de 100x100 el tiempo de ejecucin aumenta significativamente, siendo ms evidente en la configuracin en la que se usan dos procesadores. Aunque en el caso de 250x250 para 500 iteraciones la configuracin con 10 procesadores da un tiempo ligeramente menor, es la que usa 5 la que parece tener un comportamiento ms regular con tiempos generalmente inferiores a las otras, tanto para 500 como para 1000 iteraciones. Speedup 500 Tamao MPI2 MPI5 MPI10 10 0,697493 0,117269 0,069792 50 0,991765 1,264378 1,036556 100 1,088068 1,417264 0,694848 250 1,106015 1,467981 1,553179 Tabla 8: Speedup 500 iteraciones MPI Speedup promedio: MPI2 = 0,970835 MPI5 = 1,066723 MPI10 = 0,838593
Speedup 1000 Tamao MPI2 MPI5 MPI10 10 0,485471 0,252580 0,150159 50 1,042610 1,312340 0,920225 100 1,184141 1,403818 1,353076 250 1,232319 1,551718 1,423663 Tabla 9: Speedup 1000 iteraciones MPI Speedup promedio: MP2 = 0,986135 MPI5 = 1,130114 MPI10 = 0,961780 Al contrario que sucediese en la implementacin OpenMP, en MPI no parece que el Speedup promedio mejore demasiado a la implementacin original secuencial, salvo en el caso del uso de 5 procesadores. No obstante, la mejora obtenida en este caso, es inferior a cualquiera de las obtenidas para la implementacin OpenMP. Tambin puede verse que, como sucediese en OpenMP, no existe ganancia para tamaos de grid muy pequeos, ya que el valor del Speedup es inferior a 1. Introduccin a la Computacin Paralela Mster SIANI
Pgina 11 4.- Conc|us|ones En este trabajo se ha visto el proceso de implementacin del Juego de la Vida de Conway tanto secuencialmente como dos versiones paralelas, una usando OpenMP y otra con MPI. Se ha descrito en detalle el desarrollo de todas y cada una de ellas, as como las distintas pruebas que se han realizado con el objetivo de ver si las implementaciones paralelas mejoran a la versin secuencial. De forma general la implementacin en OpenMP ha mejorado en tiempo a la implementacin secuencial, obteniendo un Speedup promedio que vara entre el 1,26 y el 1,31 veces ms rpido. Como se ha descrito con anterioridad, esta mejora no se aprecia para tamaos de grid pequeos, en los que incluso se empeora. En el caso de MPI la mejora no es tan apreciable. No existe gran diferencia entre usar 5 10 procesadores, obtenindose un mejor Speedup promedio en el caso de 5, dando que es 1,13 veces mejor que la implementacin secuencial. De igual forma, la mejora es poco apreciable para tamaos de grid pequeos. En suma, las tcnicas de paralelizacin pueden ayudar sustancialmente a reducir el tiempo de ejecucin de los programas, siempre que se puedan encontrar oportunidades de paralelismo claras en el cdigo. No obstante, es preciso tomar en cuenta el esfuerzo que conlleva la realizacin del cdigo paralelo y la ganancia que con este cambio se obtiene. En funcin de los resultados obtenidos y, considerando esto ltimo, quizs la mejor opcin sea el uso de OpenMP, ya que apenas requiere de esfuerzo y de cambios en el cdigo, permitiendo probar fcilmente si nuestro algoritmo mejora al ser paralelizado. En el caso de MPI se requiere un mayor esfuerzo, puesto que hay que rehacer partes del cdigo, siendo la paralelizacin ms complicada que en el caso de usar OpenMP. De hecho, puede observarse como en ocasiones la mejora obtenida con MPI es inferior a la que se obtiene con OpenMP, lo que podra llevar a replantearse el uso de aquella librera.
Introduccin a la Computacin Paralela Mster SIANI
Pgina 12 S.- Anexo: Ind|cac|ones de comp||ac|n y e[ecuc|n Todas las implementaciones han sido compiladas y ejecutadas desde consola. A continuacin se muestran las instrucciones necesarias para la compilacin y ejecucin de las tres implementaciones desarrolladas para el presente trabajo. S.1.- Secuenc|a| En el caso del programa secuencial la lnea de compilacin es: g++ sequential.cpp o sequential Para la ejecucin simplemente es necesario ejecutar ./sequential y el programa realizar todas las pruebas mostrando por pantalla los resultados. Es preciso recordar que si se quiere que se imprima en fichero la configuracin del grid, hay que desactivar la macro COMPUTE_TIME. S.2.- CpenM La compilacin de la versin OpenMP se realiza mediante la siguiente instruccin: g++ -fopenmp o openmp openmp.cpp Para la ejecucin, al igual que antes, simplemente es necesario hacer ./openmp y el sistema har todas las pruebas que han sido descritas. Igualmente si se quiere que se imprima en fichero hay que desactivar la macro mencionada anteriormente. S.3.- MI En el caso de la implementacin usando MPI la compilacin se realiza como: mpic++ -o mpi mpi.cpp De la misma forma slo es necesario ejecutar ./mpi para que el programa funcione. No obstante, como se describi en la etapa de desarrollo, esto slo ejecuta una configuracin. Es decir, un tamao de grid con un nmero de iteraciones determinado. Si se desea cambiarlo simplemente habra que cambiar el nmero que se pasa a la funcin push_back de los vectores de tamao e iteraciones en el programa principal del cdigo. De la misma forma que se ha dicho para las otras dos versiones, si se desea que el resultado aparezca en el fichero hay que desactivar la macro COMPUTE_TIME. Adems esta implementacin cuenta con una macro DEBUG que muestra por dnde va pasando la ejecucin indicando procesador, dimensiones del grid o funcin en la que se encuentra.