(5 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
+
=1 Introducción=
 
+
==Resumen==
+
 
+
En este artículo se propone una implementación en paralelo usando un entorno multicore con MPI a la solución del sistema lineal resultante de la discretización de la ecuación de Poisson en 2D usando diferencias finitas y el método iterativo de Jacobi. El tamaño del dominio y su correspondiente discretización resultan en un sistema de ecuaciones lineales donde el número de variables puede alcanzar los miles de millones. La misma magnitud del problema permite que el algoritmo sea muy escalable paralelamente; esto significa, que al aumentar el número de procesadores disponibles para resolver el sistema, el tiempo de ejecución mejorará considerablemente. Sin embargo, al aumentar el número de procesadores, el trabajo de comunicación que realiza el algoritmo es demasiado y esto detiene su desempeño. Por lo tanto, en este artículo se propone una re-ingeniería del algoritmo paralelo enfocado en el manejo de la memoria para poder acelerar su ejecución y mejorar su efectividad.
+
 
+
'''Solución de la ecuación de Poisson en 2D usando algoritmos en paralelo en entornos multicore'''
+
 
+
Miguel Uh Zapata<math>^a</math>, Josué Pinzón Vivas<math>^b</math>
+
 
+
<math display="inline">^a</math>CONACYT - CIMAT Unidad Mérida, Merida, Yucatan, Mexico.
+
 
+
angeluh@cimat.mx
+
 
+
<math display="inline">^b</math>Facultad de Matemáticas, UADY, Mérida, México.
+
 
+
==1 Introducción==
+
  
 
Uno de los grandes desafíos en muchas ramas de la ciencia, es la obtención de simulaciones numéricas precisas, de mayor alcance, rápidas y de forma accesible. En particular, la complejidad de los problemas en dinámica de fluidos es tal, que las simulaciones numéricas requieren mucho tiempo de ejecución, cuyos resultados pueden durar varios días, incluso hasta meses. En la mayoría de estos problemas, la solución de la ecuación de Poisson representa el mayor costo computacional, inclusive más del 90% de la simulación total del problema <span id='citeF-1'></span><span id='citeF-2'></span><span id='citeF-3'></span><span id='citeF-4'></span>[[#cite-1|[1,2,3,4]]]. Parte del problema, es que su discretización proporciona un sistema de ecuaciones lineales donde el número de variables puede alcanzar los miles de millones.
 
Uno de los grandes desafíos en muchas ramas de la ciencia, es la obtención de simulaciones numéricas precisas, de mayor alcance, rápidas y de forma accesible. En particular, la complejidad de los problemas en dinámica de fluidos es tal, que las simulaciones numéricas requieren mucho tiempo de ejecución, cuyos resultados pueden durar varios días, incluso hasta meses. En la mayoría de estos problemas, la solución de la ecuación de Poisson representa el mayor costo computacional, inclusive más del 90% de la simulación total del problema <span id='citeF-1'></span><span id='citeF-2'></span><span id='citeF-3'></span><span id='citeF-4'></span>[[#cite-1|[1,2,3,4]]]. Parte del problema, es que su discretización proporciona un sistema de ecuaciones lineales donde el número de variables puede alcanzar los miles de millones.
Line 29: Line 13:
 
El objetivo de este trabajo es diseñar e implementar un algoritmo en paralelo para obtener la solución de manera más rápida de sistemas de ecuaciones lineales obtenidas del método de diferencias finitas y el método de descomposición de dominios. Más aún en este trabajo se propone mejorar los algoritmos en paralelo usando diferentes técnicas de cómputo como el manejo de la forma de acceso a la memoria. Las siguientes secciones están organizadas como se explica a continuación. En la segunda sección se introduce la discretización de la ecuación de Poisson usando diferencias finitas. En la tercera sección se presenta el algoritmo en paralelo. La cuarta sección presenta los resultados numéricos. Finalmente, en la última sección se dan las conclusiones finales de este trabajo.
 
El objetivo de este trabajo es diseñar e implementar un algoritmo en paralelo para obtener la solución de manera más rápida de sistemas de ecuaciones lineales obtenidas del método de diferencias finitas y el método de descomposición de dominios. Más aún en este trabajo se propone mejorar los algoritmos en paralelo usando diferentes técnicas de cómputo como el manejo de la forma de acceso a la memoria. Las siguientes secciones están organizadas como se explica a continuación. En la segunda sección se introduce la discretización de la ecuación de Poisson usando diferencias finitas. En la tercera sección se presenta el algoritmo en paralelo. La cuarta sección presenta los resultados numéricos. Finalmente, en la última sección se dan las conclusiones finales de este trabajo.
  
==2 Discretización de la ecuación de Poisson en 2D usando diferencias finitas==
+
=2 Discretización de la ecuación de Poisson en 2D usando diferencias finitas=
  
 
En esta sección se describirá brevemente el método de diferencias finitas usado para discretizar la ecuación de Poisson en dos dimensiones. Además, una vez obtenido el sistema de ecuaciones lineales resultantes, se presentará el método iterativo para su correspondiente solución.
 
En esta sección se describirá brevemente el método de diferencias finitas usado para discretizar la ecuación de Poisson en dos dimensiones. Además, una vez obtenido el sistema de ecuaciones lineales resultantes, se presentará el método iterativo para su correspondiente solución.
Line 48: Line 32:
 
sobre un dominio <math display="inline">\Omega = \{ (x,y)  |  a < x < b, c< y < d\} </math>. Además, se considera condiciones de frontera de Dirichlet en la frontera <math display="inline">\partial \Omega </math>, es decir, la solución es conocida de manera exacta en todas las fronteras del dominio.
 
sobre un dominio <math display="inline">\Omega = \{ (x,y)  |  a < x < b, c< y < d\} </math>. Además, se considera condiciones de frontera de Dirichlet en la frontera <math display="inline">\partial \Omega </math>, es decir, la solución es conocida de manera exacta en todas las fronteras del dominio.
  
===2.1 El método de diferencias finitas===
+
==2.1 El método de diferencias finitas==
  
 
La forma discreta del dominio viene dada por una malla estructurada rectangular. Se considera los índices <math display="inline">i=1,2,\dots ,N_x</math> y <math display="inline">j=1,2,\dots ,N_y</math>, donde <math display="inline">N_x</math> y <math display="inline">N_y</math> son el número de puntos en la dirección <math display="inline">x</math> y <math display="inline">y</math>, respectivamente. Los tamaños de paso están definidos como <math display="inline">\Delta x=(b-a)/(N_x-1)</math> y <math display="inline">\Delta y=(d-c)/(N_y-1)</math>. Finalmente los puntos donde la solución es aproximada están dados por <math display="inline">(x_i,y_j)= (a+i\Delta x, c + j\Delta y)</math>. Para mayor detalles véase la Figura [[#img-1|1]].
 
La forma discreta del dominio viene dada por una malla estructurada rectangular. Se considera los índices <math display="inline">i=1,2,\dots ,N_x</math> y <math display="inline">j=1,2,\dots ,N_y</math>, donde <math display="inline">N_x</math> y <math display="inline">N_y</math> son el número de puntos en la dirección <math display="inline">x</math> y <math display="inline">y</math>, respectivamente. Los tamaños de paso están definidos como <math display="inline">\Delta x=(b-a)/(N_x-1)</math> y <math display="inline">\Delta y=(d-c)/(N_y-1)</math>. Finalmente los puntos donde la solución es aproximada están dados por <math display="inline">(x_i,y_j)= (a+i\Delta x, c + j\Delta y)</math>. Para mayor detalles véase la Figura [[#img-1|1]].
Line 55: Line 39:
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
|[[Image:Draft_Uh Zapata_355896138-Fig01_Domain.png|540px|Dominio y esquema de diferencias finitas empleados. La zona sombreada es donde los valores son aproximados.]]
+
|[[Image:Review_872701252065-Fig01_Domain.png|540px|Dominio y esquema de diferencias finitas empleados. La zona sombreada es donde los valores son aproximados.]]
 
|- style="text-align: center; font-size: 75%;"
 
|- style="text-align: center; font-size: 75%;"
 
| colspan="1" | '''Figura 1:''' Dominio y esquema de diferencias finitas empleados. La zona sombreada es donde los valores son aproximados.
 
| colspan="1" | '''Figura 1:''' Dominio y esquema de diferencias finitas empleados. La zona sombreada es donde los valores son aproximados.
Line 102: Line 86:
 
donde <math display="inline">\beta =\Delta x/\Delta y</math>. Se puede observar que el esquema resultante relaciona un valor <math display="inline">p_{i,j}</math> con sus cuatro vecinos más cercanos, como es ilustrado en la Figura [[#img-1|1]].
 
donde <math display="inline">\beta =\Delta x/\Delta y</math>. Se puede observar que el esquema resultante relaciona un valor <math display="inline">p_{i,j}</math> con sus cuatro vecinos más cercanos, como es ilustrado en la Figura [[#img-1|1]].
  
===2.2 El sistema lineal resultante===
+
==2.2 El sistema lineal resultante==
  
 
Todos los valores de la frontera son conocidos, es decir, ya no es necesario calcular <math display="inline">p_{1,j}</math>, <math display="inline">p_{N_x,j}</math>, <math display="inline">p_{i,1}</math> y <math display="inline">p_{i,N_y}</math>. Así, sólo se aproximan los valores <math display="inline">p_{i,j}</math> donde <math display="inline">i=2,3,\dots ,N_x-2,N_x-1</math> y <math display="inline">j=2,3\dots ,N_y-2,N_y-1</math>. Se establece un indexado ''natural'' para resolver el sistema. Este consiste en ordenar todas las variables en un vector, enumerando las variables por columna, así, el vector de incógnitas a resolver es
 
Todos los valores de la frontera son conocidos, es decir, ya no es necesario calcular <math display="inline">p_{1,j}</math>, <math display="inline">p_{N_x,j}</math>, <math display="inline">p_{i,1}</math> y <math display="inline">p_{i,N_y}</math>. Así, sólo se aproximan los valores <math display="inline">p_{i,j}</math> donde <math display="inline">i=2,3,\dots ,N_x-2,N_x-1</math> y <math display="inline">j=2,3\dots ,N_y-2,N_y-1</math>. Se establece un indexado ''natural'' para resolver el sistema. Este consiste en ordenar todas las variables en un vector, enumerando las variables por columna, así, el vector de incógnitas a resolver es
Line 116: Line 100:
 
|}
 
|}
  
donde cada elemento <math display="inline">\mathbf{p}_i</math> (<math display="inline">i=2,3,...,N_x</math>) es un vector columna dada por
+
donde cada elemento <math display="inline">\mathbf{p}_i</math> (<math display="inline">i=2,3,...,N_x-1</math>) es un vector columna dada por
  
 
{| class="formulaSCP" style="width: 100%; text-align: left;"  
 
{| class="formulaSCP" style="width: 100%; text-align: left;"  
Line 164: Line 148:
 
y las matrices <math display="inline">I</math> corresponden a la matriz identidad. Los valores del lado derecho están dados por los valores correspondientes <math display="inline">f_{ij}</math> mas los valores conocidos de la frontera.
 
y las matrices <math display="inline">I</math> corresponden a la matriz identidad. Los valores del lado derecho están dados por los valores correspondientes <math display="inline">f_{ij}</math> mas los valores conocidos de la frontera.
  
===2.3 Solución del sistema por el método de Jacobi===
+
==2.3 Solución del sistema por el método de Jacobi==
  
Es bien conocido que el método de Jacobi converge lentamente a la solución del problema. Sin embargo, este tiene la notoria característica que para calcular una nueva iteración sólo se requieren de todos los elementos en en el paso anterior. Esta propiedad es raramente compartida por otros solucionadores de sistemas lineales. Por ejemplo, en los métodos de Gauss-Seidel y SOR, la actualización de un elemento depende de otros elementos ya calculados en la misma iteración. Eso implica que el método se realice de a uno por uno en un orden estricto, imposibilitando la paralelización por bloques de dicho método. Existen técnicas en las cuales se puede realizar la paralelización; sin embargo, en este trabajo únicamente se propone un algoritmo en paralelo para el método iterativo de Jacobi, dejando las otras técnicas para trabajos futuros.
+
Es bien conocido que el método de Jacobi converge lentamente a la solución del problema. Sin embargo, este tiene la notoria característica que para calcular una nueva iteración sólo se requieren de todos los elementos en el paso anterior. Esta propiedad es raramente compartida por otros solucionadores de sistemas lineales. Por ejemplo, en los métodos de Gauss-Seidel y SOR, la actualización de un elemento depende de otros elementos ya calculados en la misma iteración. Eso implica que el método se realice de a uno por uno en un orden estricto, imposibilitando la paralelización por bloques de dicho método. Existen técnicas en las cuales se puede realizar la paralelización; sin embargo, en este trabajo únicamente se propone un algoritmo en paralelo para el método iterativo de Jacobi, dejando las otras técnicas para trabajos futuros.
  
 
Para obtener el método iterativo de Jacobi se considera la descomposición de la matriz <math display="inline">A= D - E - F</math> en donde <math display="inline">D</math> , <math display="inline">-E</math> y <math display="inline">-F</math> son las matrices diagonal, tridiagonal inferior y triangular superior de <math display="inline">A</math>, respectivamente.  Con esta descomposición podemos sustituir en ([[#eq-6|6]]) para obtener <math display="inline">(D - E - F)\mathbf{p} = \mathbf{f}</math> de la cual se obtiene la iteración para el método de Jacobi
 
Para obtener el método iterativo de Jacobi se considera la descomposición de la matriz <math display="inline">A= D - E - F</math> en donde <math display="inline">D</math> , <math display="inline">-E</math> y <math display="inline">-F</math> son las matrices diagonal, tridiagonal inferior y triangular superior de <math display="inline">A</math>, respectivamente.  Con esta descomposición podemos sustituir en ([[#eq-6|6]]) para obtener <math display="inline">(D - E - F)\mathbf{p} = \mathbf{f}</math> de la cual se obtiene la iteración para el método de Jacobi
Line 196: Line 180:
 
hasta que el residuo del método sea menor o igual a una tolerancia <math display="inline">\varepsilon </math> dada. Notar que los índices se han cambiado por <math display="inline">s</math> y <math display="inline">r</math> para recalcar que estos no son los mismos de los índices <math display="inline">i</math>, <math display="inline">j</math> correspondientes a la malla, sino que estos representan la posición de los elementos en la matriz global. En otras palabras, un elemento <math display="inline">p_s=p_{i,j}</math> para cierto indice <math display="inline">i</math> y <math display="inline">j</math>.
 
hasta que el residuo del método sea menor o igual a una tolerancia <math display="inline">\varepsilon </math> dada. Notar que los índices se han cambiado por <math display="inline">s</math> y <math display="inline">r</math> para recalcar que estos no son los mismos de los índices <math display="inline">i</math>, <math display="inline">j</math> correspondientes a la malla, sino que estos representan la posición de los elementos en la matriz global. En otras palabras, un elemento <math display="inline">p_s=p_{i,j}</math> para cierto indice <math display="inline">i</math> y <math display="inline">j</math>.
  
El método iterativo de Jacobi ([[#eq-8|8]]) puede ser fácilmente aplicable al problema, dado que cada fila de la matriz tiene a lo más 5 componentes, así fácilmente se puede ver que
+
El método iterativo de Jacobi [[#eq-8|(8)]] puede ser fácilmente aplicable al problema, dado que cada fila de la matriz tiene a lo más 5 componentes, así fácilmente se puede ver que
  
 
<span id="eq-9"></span>
 
<span id="eq-9"></span>
Line 209: Line 193:
 
|}
 
|}
  
Notar que los índices en ([[#eq-9|(9)]]) han sido cambiados a su notación original para mayor facilidad de identificación del esquema.
+
Notar que los índices en [[#eq-9|(9)]] han sido cambiados a su notación original para mayor facilidad de identificación del esquema.
  
Para poder calcular el criterio de paro del método iterativo es necesario obtener el residuo, éste viene definido por <math display="inline">r = ||\mathbf{f}-A\mathbf{p}||</math>. En esta tesis usaremos la norma <math display="inline">L_2</math>. Finalmente, para el cálculo más eficiente del residuo y de la solución, el método iterativo ([[#eq-9|9]]) se modifica levemente como:
+
Para poder calcular el criterio de paro del método iterativo es necesario obtener el residuo, éste viene definido por <math display="inline">r = ||\mathbf{f}-A\mathbf{p}||</math>. En este artículo usaremos la norma <math display="inline">L_2</math>. Finalmente, para el cálculo más eficiente del residuo y de la solución, el método iterativo ([[#eq-9|9]]) se modifica levemente como:
  
 
<span id="eq-10"></span>
 
<span id="eq-10"></span>
Line 224: Line 208:
 
|}
 
|}
  
==3 Algoritmo en paralelo en entornos multicore==
+
=3 Algoritmo en paralelo en entornos multicore=
  
En esta sección se describen el método de descomposición de dominios para la programación en paralelo del sistema lineal resultante usando el método iterativo de Jacobi en un entorno ''multicore'', incluyendo las técnicas de comunicación entre procesos.
+
En esta sección se describe el método de descomposición de dominios para la programación en paralelo del sistema lineal resultante usando el método iterativo de Jacobi en un entorno ''multicore'', incluyendo las técnicas de comunicación entre procesos.
  
Aunque existen diversos modelos de computación paralela, en este trabajo se emplea el paso de mensajes. Este modelo se caracteriza principalmente en que cada proceso tiene su propia memoria local, pero son capaces de comunicarse entre sí enviando y recibiendo mensajes. MPI (''Message Passing Interface'') es una especificación de dicho modelo. Este no es un lenguaje de programación, MPI es una librería que especifíca los nombres y las llamadas de funciones o subrutinas. Dichos programas pueden ser compilados con los compiladores ordinarios de cada lenguaje y únicamente necesitan ser vinculados con la librería MPI <span id='citeF-21'></span>[[#cite-21|[21]]].
+
Aunque existen diversos modelos de computación paralela, en este trabajo se emplea el paso de mensajes. Éste modelo se caracteriza principalmente en que cada proceso tiene su propia memoria local, pero son capaces de comunicarse entre sí enviando y recibiendo mensajes. MPI (''Message Passing Interface'') es una especificación de dicho modelo. Este no es un lenguaje de programación, MPI es una librería que especifíca los nombres y las llamadas de funciones o subrutinas. Dichos programas pueden ser compilados con los compiladores ordinarios de cada lenguaje y únicamente necesitan ser vinculados con la librería MPI <span id='citeF-21'></span>[[#cite-21|[21]]].
  
===3.1 Método de descomposición del dominio===
+
==3.1 Método de descomposición del dominio==
  
 
La estrategia del algoritmo en paralelo planteada en este artículo requiere que el dominio sea dividido en varios sub-dominios. Cada sub-dominio será asignado a un proceso, y por medio del método iterativo de Jacobi cada proceso resolverá su propio sistema en su sub-dominio asignado. En la Figura&nbsp;[[#img-2|2]] se ejemplifica la descomposición de un dominio estructurado en tres sub-dominios. Se puede observar que a cada sub-dominio se le han incorporado los elementos adicionales de los sub-dominios vecinos.
 
La estrategia del algoritmo en paralelo planteada en este artículo requiere que el dominio sea dividido en varios sub-dominios. Cada sub-dominio será asignado a un proceso, y por medio del método iterativo de Jacobi cada proceso resolverá su propio sistema en su sub-dominio asignado. En la Figura&nbsp;[[#img-2|2]] se ejemplifica la descomposición de un dominio estructurado en tres sub-dominios. Se puede observar que a cada sub-dominio se le han incorporado los elementos adicionales de los sub-dominios vecinos.
Line 237: Line 221:
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
|[[Image:Draft_Uh Zapata_355896138-Fig02_DomainMPI.png|570px|División del dominio con un mallado rectangular en sub-dominios y sus comunicaciones.]]
+
|[[Image:Review_872701252065-Fig02_DomainMPI.png|570px|División del dominio con un mallado rectangular en sub-dominios y sus comunicaciones.]]
 
|- style="text-align: center; font-size: 75%;"
 
|- style="text-align: center; font-size: 75%;"
 
| colspan="1" | '''Figura 2:''' División del dominio con un mallado rectangular en sub-dominios y sus comunicaciones.
 
| colspan="1" | '''Figura 2:''' División del dominio con un mallado rectangular en sub-dominios y sus comunicaciones.
Line 245: Line 229:
 
Como hemos mencionado anteriormente, el método de Jacobi [[#eq-10|(10)]] sólo requiere de los elementos de la iteración anterior para actualizar todos los elementos del siguiente paso. Sin embargo, al inicio de la nueva iteración, los elementos adicionales en los bordes no han sido actualizados. Así, los procesos deben comunicarse enviando y recibiendo los resultados de sus elementos frontera después de cada iteración. De este modo, cada proceso tendrá la información actualizada que requiere para generar los nuevos resultados. En la siguiente sección profundizaremos un poco más en el tema de la comunicación entre procesadores.
 
Como hemos mencionado anteriormente, el método de Jacobi [[#eq-10|(10)]] sólo requiere de los elementos de la iteración anterior para actualizar todos los elementos del siguiente paso. Sin embargo, al inicio de la nueva iteración, los elementos adicionales en los bordes no han sido actualizados. Así, los procesos deben comunicarse enviando y recibiendo los resultados de sus elementos frontera después de cada iteración. De este modo, cada proceso tendrá la información actualizada que requiere para generar los nuevos resultados. En la siguiente sección profundizaremos un poco más en el tema de la comunicación entre procesadores.
  
===3.2 Comunicación entre procesos===
+
==3.2 Comunicación entre procesos==
  
 
Una de las partes más importantes a considerar a la hora de realizar un algoritmo en paralelo en MPI es la comunicación entre los procesos, ya que en la mayoría de los casos, varios procesos estarán haciendo una misma operación u operaciones parecidas sobre una sección del problema global. Así, será necesario que los procesos comuniquen sus resultados a otros procesos antes de continuar o empezar de nuevo un ciclo de operaciones.
 
Una de las partes más importantes a considerar a la hora de realizar un algoritmo en paralelo en MPI es la comunicación entre los procesos, ya que en la mayoría de los casos, varios procesos estarán haciendo una misma operación u operaciones parecidas sobre una sección del problema global. Así, será necesario que los procesos comuniquen sus resultados a otros procesos antes de continuar o empezar de nuevo un ciclo de operaciones.
Line 258: Line 242:
 
* Igualmente se puede obtener en qué fila de sub-dominios se encuentra el proceso (<nowiki>idy</nowiki>) al aproximar al entero inferior más cercano el resultado de dividir el valor de su identificador entre el número de sub-dominios a lo ancho (<nowiki>idy = rank/numx</nowiki>).
 
* Igualmente se puede obtener en qué fila de sub-dominios se encuentra el proceso (<nowiki>idy</nowiki>) al aproximar al entero inferior más cercano el resultado de dividir el valor de su identificador entre el número de sub-dominios a lo ancho (<nowiki>idy = rank/numx</nowiki>).
  
En ''Fortran 90'' dado que los operadores están definidos como <nowiki>INTEGER</nowiki>, automáticamente el valor toma sólo la parte entera, lo que resulta equivalente a obtener el entero inferior más cercano. En el Código cod:edge_communication1 se presenta la implementación de la comunicación a un programa con un dominio regular rectangular y mallado estructurado.
+
En ''Fortran 90'' dado que los operadores están definidos como <nowiki>INTEGER</nowiki>, automáticamente el valor toma sólo la parte entera, lo que resulta equivalente a obtener el entero inferior más cercano. En el código de la Figura [[#img-3|3]] se presenta la implementación de la comunicación a un programa con un dominio regular rectangular y mallado estructurado.
  
 +
<div id='img-3'></div>
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
 
+
|[[Image:Review_872701252065-FigC_Codigo1.png|594px|Código correspondiente a la subrutina de comunicación entre sub-dominios.]]
<pre>
+
|- style="text-align: center; font-size: 75%;"
SUBROUTINE edge_communication(B,My,Mx,newcolumn,rank,idy,idx, &
+
| colspan="1" | '''Figura 3:''' Código correspondiente a la subrutina de comunicación entre sub-dominios.
numy,numx,count,MPI_COMM_WORLD,status,ierr)
+
INTEGER, intent(in) :: rank,idy,idx,numy,numx,count,MPI_COMM_WORLD
+
INTEGER, intent(inout) :: status(*), ierr
+
real*8, intent(inout) :: B(My+2,Mx+2)
+
! Enviar
+
if (idx.ne.(numx-1)) then ! Lado derecho - borde derecho no envia
+
call MPI_Send(B(2,Mx+1), 1, newcolumn, rank+1, count*4+1, &
+
MPI_COMM_WORLD, ierr)
+
endif
+
if (idx.ne.0) then ! Lado izquierdo - borde izquierdo no envia
+
call MPI_Send(B(2,2), 1 , newcolumn, rank-1, count*4+2, &
+
MPI_COMM_WORLD, ierr)
+
endif
+
! Recibir
+
if (idx.ne.0) then        ! Lado derecho - borde izquierdo no recibe
+
call MPI_Recv(B(2,1), 1, newcolumn, rank-1, count*4+1, &
+
MPI_COMM_WORLD, status, ierr)
+
endif
+
if (idx.ne.(numx-1)) then ! Lado izquierdo - borde derecho no recibe
+
call MPI_Recv(B(2,Mx+2), 1, newcolumn, rank+1, count*4+2, &
+
MPI_COMM_WORLD, status, ierr)
+
endif
+
. . .
+
END SUBROUTINE
+
</pre>
+
 
+
 
+
 
|}
 
|}
  
==4 Simulaciones Numéricas==
+
=4 Simulaciones Numéricas=
  
En esta sección se presenta las simulaciones numéricas usando el algoritmo propuesto en paralelo. Inicialmente se propone un ejemplo donde se analiza el desempeño de la subdivisión de dominios, la comunicación y el manejo de memoria. Posteriormente se analiza la solución de la ecuación de Poisson en 2D y su correspondiente desempeño en paralelo. Los resultados son reportados usando un máquina estándar de 20 cores 3.0 GHz Intel Xeon. Todos los códigos fueron implementados en el lenguaje ''Fortran 90''.
+
En esta sección se presentan las simulaciones numéricas usando el algoritmo propuesto en paralelo. Inicialmente se propone un ejemplo donde se analiza el desempeño de la subdivisión de dominios, la comunicación y el manejo de memoria. Posteriormente se analiza la solución de la ecuación de Poisson en 2D y su correspondiente desempeño en paralelo. Los resultados son reportados usando una máquina estándar de 20 cores 3.0 GHz Intel Xeon. Todos los códigos fueron implementados en el lenguaje ''Fortran 90''.
  
===4.1 Ejemplo 1: Pruebas de desempeño===
+
==4.1 Ejemplo 1: Pruebas de desempeño==
  
 
Con el objetivo de medir el desempeño del algoritmo en paralelo propuesto, se desarrolló este ejemplo que consiste en aplicar un esquema en el que cada elemento es sustituido por el promedio de sí mismo y sus cuatro vecinos (superior, inferior, derecho e izquierdo). Dicho proceso es repetido 10 veces. La matriz global <math display="inline">A(:,:)</math> es de dimensiones <math display="inline">4\times  232792562</math>. Con este número (equivalente a <math display="inline">20\cdot 19\cdot 18\cdot 17\cdot 13\cdot 7\cdot 11\cdot 2 + 2</math>) el dominio es divisible exactamente por todos los enteros del 1 al 20. La suma del número 2 es porque al dividir el dominio no se consideran los elementos del borde.
 
Con el objetivo de medir el desempeño del algoritmo en paralelo propuesto, se desarrolló este ejemplo que consiste en aplicar un esquema en el que cada elemento es sustituido por el promedio de sí mismo y sus cuatro vecinos (superior, inferior, derecho e izquierdo). Dicho proceso es repetido 10 veces. La matriz global <math display="inline">A(:,:)</math> es de dimensiones <math display="inline">4\times  232792562</math>. Con este número (equivalente a <math display="inline">20\cdot 19\cdot 18\cdot 17\cdot 13\cdot 7\cdot 11\cdot 2 + 2</math>) el dominio es divisible exactamente por todos los enteros del 1 al 20. La suma del número 2 es porque al dividir el dominio no se consideran los elementos del borde.
  
====4.1.1 Prueba 1. División en sub-dominios====
+
===4.1.1 Prueba 1. División en sub-dominios===
  
 
En esta prueba el dominio únicamente es dividido a lo ancho, es decir, que los sub-dominios están únicamente uno a lado del otro, sin tener sub-dominios vecinos superiores e inferiores. De esta manera, cada procesador únicamente tiene que comunicar sus bordes a sus vecinos derecho e izquierdo, evitando así el tener que crear comunicaciones entre vecinos superiores e inferiores.
 
En esta prueba el dominio únicamente es dividido a lo ancho, es decir, que los sub-dominios están únicamente uno a lado del otro, sin tener sub-dominios vecinos superiores e inferiores. De esta manera, cada procesador únicamente tiene que comunicar sus bordes a sus vecinos derecho e izquierdo, evitando así el tener que crear comunicaciones entre vecinos superiores e inferiores.
Line 311: Line 269:
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
|[[Image:Draft_Uh Zapata_355896138-Fig03_Ex1_tp_h_vs_v.png|270px|]]
+
|[[Image:Review_872701252065-Fig03_Ex1_tp_h_vs_v.png|270px|]]
|[[Image:Draft_Uh Zapata_355896138-Fig03_Ex1_ep_h_vs_v.png|270px|Tiempo de ejecución (en segundos) y eficiencia comparando la configuración horizontal contra configuración vertical usando diferente número de procesos.]]
+
|[[Image:Review_872701252065-Fig03_Ex1_ep_h_vs_v.png|270px|Tiempo de ejecución (en segundos) y eficiencia comparando la configuración horizontal contra configuración vertical usando diferente número de procesos.]]
 
|- style="text-align: center; font-size: 75%;"
 
|- style="text-align: center; font-size: 75%;"
 
| colspan="2" | '''Figura 4:''' Tiempo de ejecución (en segundos) y eficiencia comparando la configuración horizontal contra configuración vertical usando diferente número de procesos.
 
| colspan="2" | '''Figura 4:''' Tiempo de ejecución (en segundos) y eficiencia comparando la configuración horizontal contra configuración vertical usando diferente número de procesos.
 
|}
 
|}
Se puede observar que la eficiencia tiende a alejarse del ideal mientras aumenta el número de procesos en paralelo. Los tiempo de ejecución de la configuración vertical fueron reducidos en comparación a los datos obtenidos en la horizontal. Sin embargo, la mejora en la eficiencia no es consistente para todos los números de procesos. Más aún, la caída de rendimiento en el algoritmo en ambos casos es más de lo que se espera. Aunque los resultados no son mostrados aquí, los tiempo de comunicación no varían se mantienen estables con el cambio del número de procesos. Por lo tanto, la comunicación de los bordes no está contribuyendo a reducir de manera significativa el rendimiento del algoritmo.
+
Se puede observar que la eficiencia tiende a alejarse del ideal mientras aumenta el número de procesos en paralelo. Los tiempos de ejecución de la configuración vertical fueron reducidos en comparación a los datos obtenidos en la horizontal. Sin embargo, la mejora en la eficiencia no es consistente para todos los números de procesos. Más aún, la caída de rendimiento en el algoritmo en ambos casos es más de lo que se espera. Aunque los resultados no son mostrados aquí, los tiempos de comunicación no varían se mantienen estables con el cambio del número de procesos. Por lo tanto, la comunicación de los bordes no está contribuyendo a reducir de manera significativa el rendimiento del algoritmo.
  
====4.1.2 Prueba 2. Mejoras en el acceso a memoria====
+
===4.1.2 Prueba 2. Mejoras en el acceso a memoria===
  
Como mencionamos anteriormente, el orden de acceder a la memoria por medio de los índices de las matrices en ''Fortran'' puede variar la velocidad de dicho acceso. Así en esta segunda prueba se presentan los resultados de cambiar el orden en cómo se ejecuta el esquema numérico, invirtiendo el orden de los índices de los ciclos <nowiki>do</nowiki>, como se muestra en el extracto de los Códigos cod:stencil_original y cod:stencil_x-y.
+
Como mencionamos anteriormente, el orden de acceder a la memoria por medio de los índices de las matrices en ''Fortran'' puede variar la velocidad de dicho acceso. Así en esta segunda prueba se presentan los resultados de cambiar el orden en cómo se ejecuta el esquema numérico, invirtiendo el orden de los índices de los ciclos <nowiki>do</nowiki>, como se muestra en el extracto de los códigos en las Figuras [[#img-5|5]] y [[#img-6|6]].
  
 +
<div id='img-5'></div>
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
 
+
|[[Image:Review_872701252065-FigC_Codigo2.png|594px|Código de la configuración del stencil original.]]
<pre>
+
|- style="text-align: center; font-size: 75%;"
! Aplicando el esquema
+
| colspan="1" | '''Figura 5:''' Código de la configuración del stencil original.
do i=2,My+1
+
do j=2,Mx+1
+
C(i,j) = (B(i,j)+B(i-1,j)+B(i,j+1)+B(i+1,j)+B(i,j-1))/5.0d0
+
enddo
+
enddo
+
! Actualizando matrices locales
+
B(2:My+1,2:Mx+1) = C(2:My+1,2:Mx+1)
+
</pre>
+
 
+
 
+
 
|}
 
|}
 +
<div id='img-6'></div>
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
 
+
|[[Image:Review_872701252065-FigC_Codigo3.png|594px|Código de la configuración del stencil con acceso invertido.]]
<pre>
+
|- style="text-align: center; font-size: 75%;"
! Aplicando el esquema
+
| colspan="1" | '''Figura 6:''' Código de la configuración del stencil con acceso invertido.
do j=2,Mx+1
+
do i=2,My+1
+
C(i,j) = (B(i,j)+B(i-1,j)+B(i,j+1)+B(i+1,j)+B(i,j-1))/5.0d0
+
enddo
+
enddo
+
! Actualizando matrices locales
+
B(2:My+1,2:Mx+1) = C(2:My+1,2:Mx+1)
+
</pre>
+
 
+
 
+
 
|}
 
|}
En el código cod:stencil_original, siguiendo el orden de los ciclos <nowiki>do</nowiki> de la configuración original, la matriz se recorre haciendo barridos horizontales a través del índice <math display="inline">j</math> mientras avanza a lo vertical con el índice <math display="inline">i</math> después de cada barrido horizontal. Dicha configuración fue cambiada de tal manera que se invirtiera el orden del acceso, como se ve en el código cod:stencil_x-y. En dicho código se recorre la matriz haciendo barridos verticales a través del índice <math display="inline">i</math> mientras avanza a lo horizontal con el índice <math display="inline">j</math> después de cada barrido vertical.
+
En el código [[#img-5|5]], siguiendo el orden de los ciclos <nowiki>do</nowiki> de la configuración original, la matriz se recorre haciendo barridos horizontales a través del índice <math display="inline">j</math> mientras avanza a lo vertical con el índice <math display="inline">i</math> después de cada barrido horizontal. Dicha configuración fue cambiada de tal manera que se invirtiera el orden del acceso, como se ve en el código [[#img-6|6]]. En dicho código se recorre la matriz haciendo barridos verticales a través del índice <math display="inline">i</math> mientras avanza a lo horizontal con el índice <math display="inline">j</math> después de cada barrido vertical.
  
 
Los resultados con estos cambios se presentan en la Figura [[#img-7|7]]. Se puede observar que el cambiar el orden de acceso a la matriz al momento de aplicar el esquema numérico mejora significativamente el tiempo de ejecución y consecuentemente el ''speed-up'' y la eficiencia. También se puede observar que la configuración vertical obtiene mejores resultados consiguiendo hasta un 72% de eficiencia para 20 procesos en paralelo, comparado con el primer código que se usó en el que sólo se obtuvo un 41%, ver Figura  [[#img-4|4]].
 
Los resultados con estos cambios se presentan en la Figura [[#img-7|7]]. Se puede observar que el cambiar el orden de acceso a la matriz al momento de aplicar el esquema numérico mejora significativamente el tiempo de ejecución y consecuentemente el ''speed-up'' y la eficiencia. También se puede observar que la configuración vertical obtiene mejores resultados consiguiendo hasta un 72% de eficiencia para 20 procesos en paralelo, comparado con el primer código que se usó en el que sólo se obtuvo un 41%, ver Figura  [[#img-4|4]].
Line 361: Line 301:
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
|[[Image:Draft_Uh Zapata_355896138-Fig04_Ex1_sp_v_vs_v_inv.png|288px|]]
+
|[[Image:Review_872701252065-Fig04_Ex1_sp_v_vs_v_inv.png|288px|]]
|[[Image:Draft_Uh Zapata_355896138-Fig04_Ex1_ep_v_vs_v_inv.png|288px|''Speed-up'' y eficiencia comparando la configuración horizontal contra la vertical usando diferente número de procesos y cambiando el orden de acceso a la memoria.]]
+
|[[Image:Review_872701252065-Fig04_Ex1_ep_v_vs_v_inv.png|288px|''Speed-up'' y eficiencia de la configuración vertical usando diferente número de procesos y cambiando el orden de acceso a la memoria (versión invertida).]]
 
|- style="text-align: center; font-size: 75%;"
 
|- style="text-align: center; font-size: 75%;"
| colspan="2" | '''Figura 7:''' ''Speed-up'' y eficiencia comparando la configuración horizontal contra la vertical usando diferente número de procesos y cambiando el orden de acceso a la memoria.
+
| colspan="2" | '''Figura 7:''' ''Speed-up'' y eficiencia de la configuración vertical usando diferente número de procesos y cambiando el orden de acceso a la memoria (versión invertida).
 
|}
 
|}
  
====4.1.3 Prueba 3. Implementación de punteros====
+
===4.1.3 Prueba 3. Implementación de punteros===
  
Aunque se logró mejorar, aún existe una caída significativa del rendimiento del algoritmo. Notar que en los Códigos cod:stencil_original y cod:stencil_x-y, después de aplicar el esquema numérico a la matriz <math display="inline">B</math>, es necesario actualizar dicha matriz. Una matriz <math display="inline">C</math> almacena temporalmente los nuevos resultados y posteriormente es copiada a <math display="inline">B</math>. Este proceso de actualización de la matriz local consume relativamente mucho tiempo, ya que los procesos de lectura y escritura son procesos computacionales que se consideran “costosos”, ya que son lentos si los comparamos con los procesos lógico-aritméticos.
+
Aunque se logró mejorar, aún existe una caída significativa del rendimiento del algoritmo. Notar que en los Códigos [[#img-5|5]] y [[#img-6|6]], después de aplicar el esquema numérico a la matriz <math display="inline">B</math>, es necesario actualizar dicha matriz. Una matriz <math display="inline">C</math> almacena temporalmente los nuevos resultados y posteriormente es copiada a <math display="inline">B</math>. Este proceso de actualización de la matriz local consume relativamente mucho tiempo, ya que los procesos de lectura y escritura son procesos computacionales que se consideran “costosos”, ya que son lentos si los comparamos con los procesos lógico-aritméticos.
  
Para solucionar el problema planteado, se consideró hacer uso de punteros como medio alternativo para actualizar las matrices locales luego de aplicar el esquema numérico. En ''Fortran'', el uso de punteros no supone de mucha complicación, ya que basta con editar y agregar unas cuantas líneas para hacer uso de ellos. Como se observa en el código cod:implementation_pointers, una vez declarado los punteros y las matrices locales <math display="inline">B</math> y <math display="inline">C</math>, sólo es necesario apuntar con el puntero <code>pB</code> a la matriz local <math display="inline">B</math> y con el puntero <code>pC</code> a la matriz <math display="inline">C</math>.
+
Para solucionar el problema planteado, se consideró hacer uso de punteros como medio alternativo para actualizar las matrices locales luego de aplicar el esquema numérico. En ''Fortran'', el uso de punteros no supone mucha complicación, ya que basta con editar y agregar unas cuantas líneas para hacer uso de ellos. Como se observa en el código de la Figura [[#img-8|8]], una vez declarados los punteros y las matrices locales <math display="inline">B</math> y <math display="inline">C</math>, sólo es necesario apuntar con el puntero <code>pB</code> a la matriz local <math display="inline">B</math> y con el puntero <code>pC</code> a la matriz <math display="inline">C</math>.
  
 +
<div id='img-8'></div>
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
 
+
|[[Image:Review_872701252065-FigC_Codigo4.png|594px|Código de la implementación de punteros para actualizar matriz local.]]
<pre>
+
|- style="text-align: center; font-size: 75%;"
pB => B
+
| colspan="1" | '''Figura 8:''' Código de la implementación de punteros para actualizar matriz local.
pC => C
+
. . .
+
DO count=1,10
+
!Aplicando el esquema
+
do j=2,Mx+1
+
do i=2,My+1
+
pC(i,j) = (pB(i,j)+pB(i-1,j)+pB(i,j+1)+pB(i+1,j)+pB(i,j-1))/5.0d0
+
enddo
+
enddo
+
! Actualizando matrices locales y re-direccionando punteros
+
pSwap => pB
+
pB => pC
+
pC => pSwap
+
. . .
+
</pre>
+
 
+
 
+
 
|}
 
|}
 
En la sección del código en el que se aplica el esquema, es necesario remplazar el uso de las matrices locales <math display="inline">B</math> y <math display="inline">C</math> por sus respectivos punteros. De esta manera, la actualización de las matrices locales consistirá en hacer que los punteros  <code>pB</code> y  <code>pC</code> intercambien sus direcciones a las que apuntan. Para ello es necesario hacer que el puntero  <code>pB</code> apunte a lo que apuntaba  <code>pC</code> y que el puntero  <code>pC</code> apunte a lo que apuntaba  <code>pB</code>, para este proceso es necesario utilizar un puntero auxiliar, <code>pSwap</code>.
 
En la sección del código en el que se aplica el esquema, es necesario remplazar el uso de las matrices locales <math display="inline">B</math> y <math display="inline">C</math> por sus respectivos punteros. De esta manera, la actualización de las matrices locales consistirá en hacer que los punteros  <code>pB</code> y  <code>pC</code> intercambien sus direcciones a las que apuntan. Para ello es necesario hacer que el puntero  <code>pB</code> apunte a lo que apuntaba  <code>pC</code> y que el puntero  <code>pC</code> apunte a lo que apuntaba  <code>pB</code>, para este proceso es necesario utilizar un puntero auxiliar, <code>pSwap</code>.
  
En la Tabla&nbsp;[[#table-1|1]] se encuentran los mejores resultados obtenidos, los cuales se lograron con la configuración vertical y acceso a matriz con barridos horizontales. Cabe mencionar que se esperaba que con el acceso a la matriz por barridos verticales se obtuviesen los mejores resultados, ya que en las versiones de código con configuración vertical fueron en las que mejor eficiencia se lograron. Sin embargo, a pesar de que se obtuvieron casi los mismos niveles de ''speed-up'' y eficiencia que con los barridos horizontales, se consiguió un mejor tiempo de ejecución general para el segundo caso (barridos horizontales).
+
En la Tabla&nbsp;[[#table-1|1]] se encuentran los mejores resultados obtenidos, los cuales se lograron con la configuración vertical y acceso a matriz con barridos horizontales. En la Figura&nbsp;[[#img-9|9]] se observan los tiempos de ejecución y la eficiencia, en las que se aprecia el nivel de rendimiento obtenido de la configuración vertical con acceso a la matriz por barridos horizontales e implementando punteros. Notar que inclusive con 20 procesadores podemos obtener una eficiencia de $97%$.
  
  
Line 453: Line 377:
 
| 8.802   
 
| 8.802   
 
| 0.978
 
| 0.978
|- style="border-bottom: 2px solid;"
+
|-
 
| 10   
 
| 10   
 
| 6.030   
 
| 6.030   
 
| 9.647   
 
| 9.647   
 
| 0.964
 
| 0.964
 
+
|-
|}
+
| 11   
<span style="text-align: center; font-size: 75%;"> &nbsp; </span>
+
{|  class="floating_tableSCP wikitable" style="text-align: center; margin: 1em auto;min-width:50%;"
+
|- style="border-top: 2px solid;"
+
|  Procesos
+
| Tiempo (s)
+
| Speed-up 
+
| Eficiencia
+
|- style="border-top: 2px solid;"
+
|   11   
+
 
| 5.499   
 
| 5.499   
 
| 10.577   
 
| 10.577   
Line 519: Line 434:
  
 
|}
 
|}
 
En la Figura&nbsp;[[#img-9|9]] se observan los tiempos de ejecución y la eficiencia, en las que se aprecia el nivel de rendimiento obtenido de la configuración vertical con acceso a la matriz por barridos horizontales e implementando punteros. Notar que inclusive con 20 procesadores podemos obtener una eficiencia de 97%
 
  
 
<div id='img-9'></div>
 
<div id='img-9'></div>
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
|[[Image:Draft_Uh Zapata_355896138-Fig05_Ex1_tp_v_pointers.png|288px|]]
+
|[[Image:Review_872701252065-Fig05_Ex1_tp_v_pointers.png|288px|]]
|[[Image:Draft_Uh Zapata_355896138-Fig05_Ex1_ep_v_pointers.png|288px|Tiempo de ejecución (en segundos) y eficiencia usando la configuración horizontal e implementando punteros con diferente número de procesos.]]
+
|[[Image:Review_872701252065-Fig05_Ex1_ep_v_pointers.png|288px|Tiempo de ejecución (en segundos) y eficiencia usando la configuración horizontal e implementando punteros con diferente número de procesos.]]
 
|- style="text-align: center; font-size: 75%;"
 
|- style="text-align: center; font-size: 75%;"
 
| colspan="2" | '''Figura 9:''' Tiempo de ejecución (en segundos) y eficiencia usando la configuración horizontal e implementando punteros con diferente número de procesos.
 
| colspan="2" | '''Figura 9:''' Tiempo de ejecución (en segundos) y eficiencia usando la configuración horizontal e implementando punteros con diferente número de procesos.
 
|}
 
|}
  
===4.2 Ejemplo 2: Solución de la ecuación de Poisson en paralelo===
+
==4.2 Ejemplo 2: Solución de la ecuación de Poisson en paralelo==
  
 
En este ejemplo se muestran los resultados obtenidos con el programa desarrollado para resolver sistemas lineales por el método de Jacobi provenientes de la ecuación de Poisson y el método de diferencias finitas aplicado a un dominio rectangular con mallado estructurado.  Considérese la ecuación de Poisson en 2D dada por [[#eq-1|(1)]] donde la solución analítica y la función del lado derecho están dadas por
 
En este ejemplo se muestran los resultados obtenidos con el programa desarrollado para resolver sistemas lineales por el método de Jacobi provenientes de la ecuación de Poisson y el método de diferencias finitas aplicado a un dominio rectangular con mallado estructurado.  Considérese la ecuación de Poisson en 2D dada por [[#eq-1|(1)]] donde la solución analítica y la función del lado derecho están dadas por
Line 548: Line 461:
 
respectivamente. Con estas funciones se aplican condiciones de frontera Dirichlet nulas sobre el dominio dado por <math display="inline">\Omega = (0,1)\times (0,1)</math>.
 
respectivamente. Con estas funciones se aplican condiciones de frontera Dirichlet nulas sobre el dominio dado por <math display="inline">\Omega = (0,1)\times (0,1)</math>.
  
Para esta prueba se eligió un mallado de dimensiones <math display="inline">362\times 362</math>. Dicho tamaño se escogió pensando en generar un tiempo considerable de ejecución y una malla que tenga una gran cantidad de divisores de manera que, sin contar sus fronteras, es exactamente divisible entre la cantidad de procesos que se ejecuten en paralelo. Por último se escogió una tolerancia de <math display="inline">10^{-5}</math> para determinar la condición de paro del método iterativo. En la Figura&nbsp;[[#img-10|10]] se puede observar la solución exacta y numérica, así como de los errores absolutos de este ejemplo. El algoritmo propuesto resuelve de manera correcta el problema y otorga una solución precisa a la solución exacta.
+
Para esta prueba se eligió un mallado de dimensiones <math display="inline">362\times 362</math>. Dicho tamaño se escogió pensando en generar un tiempo considerable de ejecución y una malla que tenga una gran cantidad de divisores de manera que, sin contar sus fronteras, es exactamente divisible entre la cantidad de procesos que se ejecuten en paralelo. Por último se escogió una tolerancia de <math display="inline">10^{-5}</math> para determinar la condición de paro del método iterativo. En la Figura&nbsp;[[#img-10|10]] se puede observar la solución exacta y numérica, así como los errores absolutos de este ejemplo. El algoritmo propuesto resuelve de manera correcta el problema y otorga una solución precisa a la solución exacta.
  
 
<div id='img-10'></div>
 
<div id='img-10'></div>
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
|[[Image:Draft_Uh Zapata_355896138-Fig06_Ex2_pexact.png|192px|]]
+
|[[Image:Review_872701252065-Fig06_Ex2_pexact.png|192px|]]
|[[Image:Draft_Uh Zapata_355896138-Fig06_Ex2_paprox.png|192px|]]
+
|[[Image:Review_872701252065-Fig06_Ex2_paprox.png|192px|]]
|[[Image:Draft_Uh Zapata_355896138-Fig06_Ex2_errP.png|192px|Solución exacta, solución numérica y error absoluto de la ecuación de Poisson en 2D.]]
+
|[[Image:Review_872701252065-Fig06_Ex2_errP.png|192px|Solución exacta, solución numérica y error absoluto de la ecuación de Poisson en 2D.]]
 
|- style="text-align: center; font-size: 75%;"
 
|- style="text-align: center; font-size: 75%;"
 
| colspan="3" | '''Figura 10:''' Solución exacta, solución numérica y error absoluto de la ecuación de Poisson en 2D.
 
| colspan="3" | '''Figura 10:''' Solución exacta, solución numérica y error absoluto de la ecuación de Poisson en 2D.
 
|}
 
|}
A continuación se presentan los resultados obtenidos de los tiempos de ejecución logrados en las configuraciones con distintos número de procesos. Hay que recordar que la máxima eficiencia del programa se logra si la dimensión de la malla (sin fronteras) es divisible exacto entre el número de procesos en paralelo ejecutándose. El mallado de <math display="inline">362 \times 362</math>  es divisible entre 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18 y 20. La Tabla&nbsp;[[#img-11a|11a]] muestra  la mediana de los tiempos de ejecución después de ejecutar varias veces el programa. Los resultados de ''speed-up'' y eficiencia también pueden ser observados en sus representaciones gráficas en la Figura&nbsp;[[#img-12|12]].
+
A continuación se presentan los resultados obtenidos de los tiempos de ejecución logrados en las configuraciones con distintos número de procesos. Hay que recordar que la máxima eficiencia del programa se logra si la dimensión de la malla (sin fronteras) es divisible exacto entre el número de procesos en paralelo ejecutándose. El mallado de <math display="inline">362 \times 362</math>  es divisible entre 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18 y 20. La Tabla&nbsp;[[#table-2|2]] muestra  la mediana de los tiempos de ejecución después de ejecutar varias veces el programa. Los resultados de ''speed-up'' y eficiencia también pueden ser observados en sus representaciones gráficas en la Figura&nbsp;[[#img-12|12]].
  
<div id='img-11a'></div>
+
 
<div id='img-11'></div>
+
{| class="floating_tableSCP wikitable" style="text-align: right; margin: 1em auto;min-width:50%;"
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
+
|+ style="font-size: 75%;" |<span id='table-2'></span>Tabla. 2 Eficiencia de la aproximación de la ecuación de Poisson en 2D con una malla de <math>362\times 362</math>.
|-
+
|
+
{|  style="text-align: right; margin: 1em auto;min-width:50%;width:100%;"
+
 
|- style="border-top: 2px solid;"
 
|- style="border-top: 2px solid;"
| style="text-align: center;" |   Procesos  
+
| style="text-align: center;" | Procesos  
| Tiempo (s)  
+
| Tiempo (s)  
| Speed-up  
+
| Speed-up  
| Eficiencia
+
| Eficiencia
 
|- style="border-top: 2px solid;"
 
|- style="border-top: 2px solid;"
| style="text-align: center;" |   1   
+
| style="text-align: center;" |   1   
|   198.256   
+
| 198.256   
|   1.000   
+
| 1.000   
|   1.000
+
| 1.000
 
|-
 
|-
| style="text-align: center;" |   2   
+
| style="text-align: center;" | 2   
|   102.978   
+
| 102.978   
|   1.925   
+
| 1.925   
|   0.962
+
| 0.962
 
|-
 
|-
| style="text-align: center;" |   3   
+
| style="text-align: center;" | 3   
|   68.377   
+
| 68.377   
|   2.899   
+
| 2.899   
|   0.966
+
| 0.966
 
|-
 
|-
| style="text-align: center;" |   4   
+
| style="text-align: center;" | 4   
|   50.172   
+
| 50.172   
|   3.951   
+
| 3.951   
|   0.987
+
| 0.987
 
|-
 
|-
| style="text-align: center;" |   5   
+
| style="text-align: center;" | 5   
|   41.049   
+
| 41.049   
|   4.829   
+
| 4.829   
|   0.965
+
| 0.965
 
|-
 
|-
| style="text-align: center;" |   6   
+
| style="text-align: center;" | 6   
|   33.978   
+
| 33.978   
|   5.834   
+
| 5.834   
|   0.972
+
| 0.972
 
|-
 
|-
| style="text-align: center;" |   8   
+
| style="text-align: center;" | 8   
|   25.112   
+
| 25.112   
|   7.894   
+
| 7.894   
|   0.986
+
| 0.986
 
|-
 
|-
| style="text-align: center;" |   9   
+
| style="text-align: center;" | 9   
|   22.041   
+
| 22.041   
|   8.994   
+
| 8.994   
|   0.999
+
| 0.999
 
|-
 
|-
| style="text-align: center;" |   10   
+
| style="text-align: center;" | 10   
|   20.177   
+
| 20.177   
|   9.825   
+
| 9.825   
|   0.982
+
| 0.982
 
|-
 
|-
| style="text-align: center;" |   12   
+
| style="text-align: center;" | 12   
|   17.763   
+
| 17.763   
|   11.160   
+
| 11.160   
|   0.930
+
| 0.930
 
|-
 
|-
| style="text-align: center;" |   15   
+
| style="text-align: center;" | 15   
|   13.689   
+
| 13.689   
|   14.482   
+
| 14.482   
|   0.965
+
| 0.965
 
|-
 
|-
| style="text-align: center;" |   18   
+
| style="text-align: center;" | 18   
|   11.323   
+
| 11.323   
|   17.508   
+
| 17.508   
|   0.972
+
| 0.972
 
|- style="border-bottom: 2px solid;"
 
|- style="border-bottom: 2px solid;"
| style="text-align: center;" |   20   
+
| style="text-align: center;" | 20   
|   10.289   
+
| 10.289   
|   19.268   
+
| 19.268   
|   0.963
+
| 0.963
  
 
|}
 
|}
  
 +
<div id='img-11'></div>
 +
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
|[[Image:Draft_Uh Zapata_355896138-Fig07_Ex2_estructurado_tp.png|300px|Eficiencia de la aproximación de la ecuación de Poisson en 2D con una malla de 362×362.]]
+
|[[Image:Review_872701252065-Fig07_Ex2_estructurado_tp.png|300px|Tiempos de ejecución usando diferente número de procesos.]]
|- style="text-align: center; font-size: 75%;"
+
| (a) Eficiencia de la aproximación de la ecuación de Poisson en 2D con una malla de <math>362\times 362</math>.
+
 
|- style="text-align: center; font-size: 75%;"
 
|- style="text-align: center; font-size: 75%;"
 
| colspan="1" | '''Figura 11:''' Tiempos de ejecución usando diferente número de procesos.
 
| colspan="1" | '''Figura 11:''' Tiempos de ejecución usando diferente número de procesos.
 
|}
 
|}
Como se puede observar, los resultados muestran altos niveles de eficiencia. Por ejemplo, la menor eficiencia del programa se obtiene usando 12 procesadores con un 93% Más aún, todas las configuraciones mantienen un nivel parecido de eficiencia, es decir, que no se logra apreciar una pérdida de rendimiento conforme más procesos se ejecuten. Lo anterior da como consecuencia que se logre hasta un ''speed-up'' de 19.2 con 20 procesos, muy cercano a lo ideal que sería 20. En tiempos de cómputo reales, una simulación en serie que duraba más de tres minutos, puede ser ahora obtenida en tan sólo 10 segundos con la misma precisión.
+
 
 +
Como se puede observar, los resultados muestran altos niveles de eficiencia. Por ejemplo, la menor eficiencia del programa se obtiene usando 12 procesadores con un <math display="inline">93%</math>. Más aún, todas las configuraciones mantienen un nivel parecido de eficiencia, es decir, que no se logra apreciar una pérdida de rendimiento conforme más procesos se ejecuten. Lo anterior da como consecuencia que se logre hasta un ''speed-up'' de 19.2 con 20 procesos, muy cercano a lo ideal que sería 20. En tiempos de cómputo reales, una simulación en serie que duraba más de tres minutos, puede ser ahora obtenida en tan sólo 10 segundos con la misma precisión.
  
 
<div id='img-12'></div>
 
<div id='img-12'></div>
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
{| class="floating_imageSCP" style="text-align: center; border: 1px solid #BBB; margin: 1em auto; width: 100%;max-width: 100%;"
 
|-
 
|-
|[[Image:Draft_Uh Zapata_355896138-Fig07_Ex2_estructurado_sp.png|294px|]]
+
|[[Image:Review_872701252065-Fig07_Ex2_estructurado_sp.png|294px|]]
|[[Image:Draft_Uh Zapata_355896138-Fig07_Ex2_estructurado_ep.png|294px|Rendimiento de la aproximación de la ecuación de Poisson en 2D empleando una malla de 362×362.]]
+
|[[Image:Review_872701252065-Fig07_Ex2_estructurado_ep.png|294px|Rendimiento de la aproximación de la ecuación de Poisson en 2D empleando una malla de 362×362.]]
 
|- style="text-align: center; font-size: 75%;"
 
|- style="text-align: center; font-size: 75%;"
 
| colspan="2" | '''Figura 12:''' Rendimiento de la aproximación de la ecuación de Poisson en 2D empleando una malla de <math>362\times 362</math>.
 
| colspan="2" | '''Figura 12:''' Rendimiento de la aproximación de la ecuación de Poisson en 2D empleando una malla de <math>362\times 362</math>.
 
|}
 
|}
  
==5 Conclusiones==
+
=5 Conclusiones=
  
En este trabajo se presentó el método iterativo de Jacobi en paralelo para resolver el sistema de ecuaciones lineales resultante de discretizar la ecuación de Poisson en 2D usando el método de diferencias finitas. Aunque este método tiene la desventaja de una lenta convergencia en comparación con otros métodos iterativos más avanzados, el tiempo de ejecución se compensa con la fácil implementación del algoritmo en paralelo. En este trabajo se plantearon varias alternativas para implementar el algoritmo incluyendo diferentes opciones de acceso a la memoria. La configuración con pivotes mostró los resultados con mayor rendimiento. Se tuvo una eficiencia por arriba del 90% y un ''speed-up'' de hasta 19.2 con 20 procesos en paralelo. En trabajos futuros se implementará el mismo análisis para aproximaciones de la ecuación de Poisson en 2D usando volúmenes finitos y mallados triangulares no estructurados. También se implementará métodos iterativos con mayor velocidad de convergencia, lo cual ayudará a reducir aún más el tiempo de ejecución de las simulaciones numéricas.
+
En este trabajo se presentó el método iterativo de Jacobi en paralelo para resolver el sistema de ecuaciones lineales resultante de discretizar la ecuación de Poisson en 2D usando el método de diferencias finitas. Aunque este método tiene la desventaja de una lenta convergencia en comparación con otros métodos iterativos más avanzados, el tiempo de ejecución se compensa con la fácil implementación del algoritmo en paralelo. Se plantearon varias alternativas para implementar el algoritmo incluyendo diferentes opciones de acceso a la memoria. La configuración con pivotes mostró los resultados con mayor rendimiento. Se tuvo una eficiencia por arriba del 90% y un ''speed-up'' de hasta 19.2 con 20 procesos en paralelo. En trabajos futuros se implementarán el mismo análisis para aproximaciones de la ecuación de Poisson en 2D usando volúmenes finitos y mallados triangulares no estructurados. También se implementará métodos iterativos con mayor velocidad de convergencia, lo cual ayudará a reducir aún más el tiempo de ejecución de las simulaciones numéricas.
  
==Agradecimientos==
+
=Agradecimientos=
  
 
Se agradece al Consejo Nacional de Ciencia y Tecnología (CONACYT) por el apoyo para la realización de este trabajo con el proyecto de ''Investigadoras e Investigadores por México''.
 
Se agradece al Consejo Nacional de Ciencia y Tecnología (CONACYT) por el apoyo para la realización de este trabajo con el proyecto de ''Investigadoras e Investigadores por México''.
  
===BIBLIOGRAFÍA===
+
==BIBLIOGRAFÍA==
  
 
<div id="cite-1"></div>
 
<div id="cite-1"></div>

Latest revision as of 06:17, 19 December 2021

1 Introducción

Uno de los grandes desafíos en muchas ramas de la ciencia, es la obtención de simulaciones numéricas precisas, de mayor alcance, rápidas y de forma accesible. En particular, la complejidad de los problemas en dinámica de fluidos es tal, que las simulaciones numéricas requieren mucho tiempo de ejecución, cuyos resultados pueden durar varios días, incluso hasta meses. En la mayoría de estos problemas, la solución de la ecuación de Poisson representa el mayor costo computacional, inclusive más del 90% de la simulación total del problema [1,2,3,4]. Parte del problema, es que su discretización proporciona un sistema de ecuaciones lineales donde el número de variables puede alcanzar los miles de millones.

Existen diferentes métodos para la solución de la ecuación de Poisson en dos dimensiones (2D). Aunque cada método tiene sus propias fortalezas y limitaciones. Debido a sus características y al éxito que ha tenido al ser implementado en numerosos fenómenos físicos, el método de diferencias finitas [5,6,7] es el método numérico implementado en este trabajo.

La discretización de la ecuación de Poisson usando diferencias finitas proporciona un sistema de ecuaciones lineales donde el número de incógnitas es proporcional al número de puntos usados en el mallado. Tradicionalmente los sistemas lineales son resueltos usando métodos iterativos, los cuales aproximan la solución mediante un número de iteraciones hasta que cierta tolerancia se satisface. Métodos como el Gradiente Conjugado [8,9] y el Generalized Minimal Residual (GMRES) [10,11] pueden ser considerados. Sin embargo, estos últimos tienen una mayor dificultad en el momento de implementar y especialmente al tratar de obtener un programa más eficiente en paralelo. Por otro lado, métodos clásicos como Jacobi, Gauss-Seidel y Succesive-Over Relaxation (SOR) pueden ser usados con cierto grado de dificultad de implementación y convergencia razonable [12,13,14]. Como primera aproximación, este trabajo se concentrará en el método de Jacobi, el cual es el más sencillo de implementar pero es base para solución de otros algoritmos más complejos.

Varios algoritmos en paralelo han sido desarrollados para resolver grandes sistemas lineales en distintos campos de la ciencia, incluyendo diversas plataformas como los son MPI(Message Passing Interface), GPU Graphics Processing Unit), OpenMP(Open Multi-Processing), entre otros. Por ejemplo, se han utilizado para algoritmos involucrados con imágenes médicas [15], Finanzas Computacionales [16], Bioinformática [17] y Dinámica de Fluidos [18], por citar algunos.

Cada metodología en paralelo tiene sus diferentes grados de dificultad y eficiencia, sin embargo, algunas de las ventajas de usar MPI [20,19,21] es su grado de universalidad. El modelo de paso de mensajes se adapta bien a la mayoría de las actuales super-computadoras debido a su arquitectura de procesadores separados pero conectados por una red de comunicación. En cuanto a su desempeño y rendimiento, con el modelo de paso de mensajes uno puede asociar explícitamente un espacio de memoria a un proceso lo cual otorga un mejor uso de las caches y jerarquías de memoria, lo cual se ve reflejado directamente en el rendimiento. Aún más, en sistemas de memoria compartida se puede mejorar el rendimiento al tener más control sobre la localidad de los datos en la memoria.

El objetivo de este trabajo es diseñar e implementar un algoritmo en paralelo para obtener la solución de manera más rápida de sistemas de ecuaciones lineales obtenidas del método de diferencias finitas y el método de descomposición de dominios. Más aún en este trabajo se propone mejorar los algoritmos en paralelo usando diferentes técnicas de cómputo como el manejo de la forma de acceso a la memoria. Las siguientes secciones están organizadas como se explica a continuación. En la segunda sección se introduce la discretización de la ecuación de Poisson usando diferencias finitas. En la tercera sección se presenta el algoritmo en paralelo. La cuarta sección presenta los resultados numéricos. Finalmente, en la última sección se dan las conclusiones finales de este trabajo.

2 Discretización de la ecuación de Poisson en 2D usando diferencias finitas

En esta sección se describirá brevemente el método de diferencias finitas usado para discretizar la ecuación de Poisson en dos dimensiones. Además, una vez obtenido el sistema de ecuaciones lineales resultantes, se presentará el método iterativo para su correspondiente solución.

Considérese la siguiente ecuación de Poisson en dos dimensiones:

(1)

sobre un dominio . Además, se considera condiciones de frontera de Dirichlet en la frontera , es decir, la solución es conocida de manera exacta en todas las fronteras del dominio.

2.1 El método de diferencias finitas

La forma discreta del dominio viene dada por una malla estructurada rectangular. Se considera los índices y , donde y son el número de puntos en la dirección y , respectivamente. Los tamaños de paso están definidos como y . Finalmente los puntos donde la solución es aproximada están dados por . Para mayor detalles véase la Figura 1.

Dominio y esquema de diferencias finitas empleados. La zona sombreada es donde los valores son aproximados.
Figura 1: Dominio y esquema de diferencias finitas empleados. La zona sombreada es donde los valores son aproximados.

Como se tiene un dominio rectangular y una malla regular, la ecuación diferencial puede ser discretizada usando diferencias finitas centradas para la segunda derivada parcial. Ésta es obtenida usando desarrollos de series de Taylor. Así, las aproximaciones están dadas por

(2)
(3)

donde . Sustituyendo (2)-(3) en la ecuación (1), se obtiene

donde . Finalmente la discretización en diferencias finitas de la ecuación de Poisson (1) está dada por

(4)

donde . Se puede observar que el esquema resultante relaciona un valor con sus cuatro vecinos más cercanos, como es ilustrado en la Figura 1.

2.2 El sistema lineal resultante

Todos los valores de la frontera son conocidos, es decir, ya no es necesario calcular , , y . Así, sólo se aproximan los valores donde y . Se establece un indexado natural para resolver el sistema. Este consiste en ordenar todas las variables en un vector, enumerando las variables por columna, así, el vector de incógnitas a resolver es

(5)

donde cada elemento () es un vector columna dada por

Así, se tiene el sistema lineal

(6)

donde

Notar que la matriz es una matriz tridiagonal a bloques, donde los bloques son a su vez unas matrices tridiagonales dadas por

y las matrices corresponden a la matriz identidad. Los valores del lado derecho están dados por los valores correspondientes mas los valores conocidos de la frontera.

2.3 Solución del sistema por el método de Jacobi

Es bien conocido que el método de Jacobi converge lentamente a la solución del problema. Sin embargo, este tiene la notoria característica que para calcular una nueva iteración sólo se requieren de todos los elementos en el paso anterior. Esta propiedad es raramente compartida por otros solucionadores de sistemas lineales. Por ejemplo, en los métodos de Gauss-Seidel y SOR, la actualización de un elemento depende de otros elementos ya calculados en la misma iteración. Eso implica que el método se realice de a uno por uno en un orden estricto, imposibilitando la paralelización por bloques de dicho método. Existen técnicas en las cuales se puede realizar la paralelización; sin embargo, en este trabajo únicamente se propone un algoritmo en paralelo para el método iterativo de Jacobi, dejando las otras técnicas para trabajos futuros.

Para obtener el método iterativo de Jacobi se considera la descomposición de la matriz en donde , y son las matrices diagonal, tridiagonal inferior y triangular superior de , respectivamente. Con esta descomposición podemos sustituir en (6) para obtener de la cual se obtiene la iteración para el método de Jacobi

(7)

en donde y son las aproximaciones obtenidas mediante y iteraciones, respectivamente. Finalmente, el método iterativo de Jacobi resulta de iterar repetidamente

(8)

hasta que el residuo del método sea menor o igual a una tolerancia dada. Notar que los índices se han cambiado por y para recalcar que estos no son los mismos de los índices , correspondientes a la malla, sino que estos representan la posición de los elementos en la matriz global. En otras palabras, un elemento para cierto indice y .

El método iterativo de Jacobi (8) puede ser fácilmente aplicable al problema, dado que cada fila de la matriz tiene a lo más 5 componentes, así fácilmente se puede ver que

(9)

Notar que los índices en (9) han sido cambiados a su notación original para mayor facilidad de identificación del esquema.

Para poder calcular el criterio de paro del método iterativo es necesario obtener el residuo, éste viene definido por . En este artículo usaremos la norma . Finalmente, para el cálculo más eficiente del residuo y de la solución, el método iterativo (9) se modifica levemente como:

(10)

3 Algoritmo en paralelo en entornos multicore

En esta sección se describe el método de descomposición de dominios para la programación en paralelo del sistema lineal resultante usando el método iterativo de Jacobi en un entorno multicore, incluyendo las técnicas de comunicación entre procesos.

Aunque existen diversos modelos de computación paralela, en este trabajo se emplea el paso de mensajes. Éste modelo se caracteriza principalmente en que cada proceso tiene su propia memoria local, pero son capaces de comunicarse entre sí enviando y recibiendo mensajes. MPI (Message Passing Interface) es una especificación de dicho modelo. Este no es un lenguaje de programación, MPI es una librería que especifíca los nombres y las llamadas de funciones o subrutinas. Dichos programas pueden ser compilados con los compiladores ordinarios de cada lenguaje y únicamente necesitan ser vinculados con la librería MPI [21].

3.1 Método de descomposición del dominio

La estrategia del algoritmo en paralelo planteada en este artículo requiere que el dominio sea dividido en varios sub-dominios. Cada sub-dominio será asignado a un proceso, y por medio del método iterativo de Jacobi cada proceso resolverá su propio sistema en su sub-dominio asignado. En la Figura 2 se ejemplifica la descomposición de un dominio estructurado en tres sub-dominios. Se puede observar que a cada sub-dominio se le han incorporado los elementos adicionales de los sub-dominios vecinos.

División del dominio con un mallado rectangular en sub-dominios y sus comunicaciones.
Figura 2: División del dominio con un mallado rectangular en sub-dominios y sus comunicaciones.

El esquema numérico de cinco puntos en cruz proporcionado por el método de Jacobi (10), ver Figura 1, combinado con el método de descomposición de dominio requiere que cada elemento del sub-dominio recopile información de sus elementos vecinos por cada iteración. Esto da como resultado que los elementos del borde (o de frontera artificial) de cada sub-dominio requieran información de elementos que pertenecen al sub-dominio vecino. Lo anterior representa la primera dificultad técnica del algoritmo en paralelo dado que los procesos no tienen acceso directo a la memoria de los demás procesos. Para solucionar este problema, en este artículo, los sub-dominios son creados de tal manera que estén sobrepuestos. Así los sub-dominios incluyen los elementos vecinos colindantes entre ellos.

Como hemos mencionado anteriormente, el método de Jacobi (10) sólo requiere de los elementos de la iteración anterior para actualizar todos los elementos del siguiente paso. Sin embargo, al inicio de la nueva iteración, los elementos adicionales en los bordes no han sido actualizados. Así, los procesos deben comunicarse enviando y recibiendo los resultados de sus elementos frontera después de cada iteración. De este modo, cada proceso tendrá la información actualizada que requiere para generar los nuevos resultados. En la siguiente sección profundizaremos un poco más en el tema de la comunicación entre procesadores.

3.2 Comunicación entre procesos

Una de las partes más importantes a considerar a la hora de realizar un algoritmo en paralelo en MPI es la comunicación entre los procesos, ya que en la mayoría de los casos, varios procesos estarán haciendo una misma operación u operaciones parecidas sobre una sección del problema global. Así, será necesario que los procesos comuniquen sus resultados a otros procesos antes de continuar o empezar de nuevo un ciclo de operaciones.

Un solo algoritmo debe ser programado para diferentes números de procesos. Así la comunicación debe hacerse de forma dinámica. Es decir, las órdenes de envío y recepción de mensajes se deben ajustar automáticamente al número de procesos. No es recomendable escribir un código para cada caso particular ya que las instrucciones condicionales para elegir el caso correspondiente también consumen un tiempo valioso de ejecución.

Para poder enviar simultáneamente múltiples mensajes y que se envíen al destinatario correcto sin tener que especificar para cada proceso un caso particular, basta con que se conozca desde un principio quiénes son los vecinos de cada sub-dominio. Por la forma en que se organizó la malla, esa información es conocida desde el inicio del programa. A cada sub-dominio se le asignó un proceso, dicho proceso identificado con el rank obtenido de la sub-rutina MPI_Comm_rank. Si pensamos que la división de dominios es únicamente unidimensional, entones a su vecino derecho le corresponde el identificador rank+1. De la misma manera, a su vecino izquierdo le corresponde rank-1. De tal modo que al usar como parámetro los identificadores correspondientes a sus vecinos, dicho parámetro se vuelve un parámetro “dinámico”. Para cada proceso que ejecute la sub-rutina de envío de mensaje, el parámetro tendrá un valor distinto que será correspondiente al proceso vecino de a quién se enviará.

A pesar de que la técnica anterior de programación simplifica mucho la programación del código, aún hay que considerar ciertos casos que pueden hacer que el programa no funcione correctamente. Para esto se implementan otros índices que ayudan a localizar en dónde se encuentra el proceso asignado a un sub-dominio con respecto al dominio global. Dado que el dominio es de forma rectangular, la malla aplicada es estructurada con rectángulos del mismo tamaño, la asignación de los procesos es de orden ascendente, primero a lo ancho y luego a lo largo. Mas aún, se conoce el número de sub-dominios a lo ancho (numx) y el número de sub-dominios a lo largo (numy). Así

  • Se puede conocer en que columna de sub-dominios se encuentra el proceso (idx) al obtener el valor del identificador del proceso módulo 2 (idx = modulo(rank,numx)).
  • Igualmente se puede obtener en qué fila de sub-dominios se encuentra el proceso (idy) al aproximar al entero inferior más cercano el resultado de dividir el valor de su identificador entre el número de sub-dominios a lo ancho (idy = rank/numx).

En Fortran 90 dado que los operadores están definidos como INTEGER, automáticamente el valor toma sólo la parte entera, lo que resulta equivalente a obtener el entero inferior más cercano. En el código de la Figura 3 se presenta la implementación de la comunicación a un programa con un dominio regular rectangular y mallado estructurado.

Código correspondiente a la subrutina de comunicación entre sub-dominios.
Figura 3: Código correspondiente a la subrutina de comunicación entre sub-dominios.

4 Simulaciones Numéricas

En esta sección se presentan las simulaciones numéricas usando el algoritmo propuesto en paralelo. Inicialmente se propone un ejemplo donde se analiza el desempeño de la subdivisión de dominios, la comunicación y el manejo de memoria. Posteriormente se analiza la solución de la ecuación de Poisson en 2D y su correspondiente desempeño en paralelo. Los resultados son reportados usando una máquina estándar de 20 cores 3.0 GHz Intel Xeon. Todos los códigos fueron implementados en el lenguaje Fortran 90.

4.1 Ejemplo 1: Pruebas de desempeño

Con el objetivo de medir el desempeño del algoritmo en paralelo propuesto, se desarrolló este ejemplo que consiste en aplicar un esquema en el que cada elemento es sustituido por el promedio de sí mismo y sus cuatro vecinos (superior, inferior, derecho e izquierdo). Dicho proceso es repetido 10 veces. La matriz global es de dimensiones . Con este número (equivalente a ) el dominio es divisible exactamente por todos los enteros del 1 al 20. La suma del número 2 es porque al dividir el dominio no se consideran los elementos del borde.

4.1.1 Prueba 1. División en sub-dominios

En esta prueba el dominio únicamente es dividido a lo ancho, es decir, que los sub-dominios están únicamente uno a lado del otro, sin tener sub-dominios vecinos superiores e inferiores. De esta manera, cada procesador únicamente tiene que comunicar sus bordes a sus vecinos derecho e izquierdo, evitando así el tener que crear comunicaciones entre vecinos superiores e inferiores.

El tiempo de acceso a la memoria en Fortran depende mucho del orden de acceso de los índices de una matriz. Así, puede haber diferencias de tiempo si se accede a elementos contiguos horizontales y que a elementos contiguos verticales y . Dado lo anterior, se realizó una segunda prueba invirtiendo las dimensiones de la matriz y dividiendo el dominio a lo vertical en lugar de a lo horizontal. La Figura 4 muestra las medianas de los tiempos de cómputo y de la eficiencia usando ambas configuraciones.

Review 872701252065-Fig03 Ex1 tp h vs v.png Tiempo de ejecución (en segundos) y eficiencia comparando la configuración horizontal contra configuración vertical usando diferente número de procesos.
Figura 4: Tiempo de ejecución (en segundos) y eficiencia comparando la configuración horizontal contra configuración vertical usando diferente número de procesos.

Se puede observar que la eficiencia tiende a alejarse del ideal mientras aumenta el número de procesos en paralelo. Los tiempos de ejecución de la configuración vertical fueron reducidos en comparación a los datos obtenidos en la horizontal. Sin embargo, la mejora en la eficiencia no es consistente para todos los números de procesos. Más aún, la caída de rendimiento en el algoritmo en ambos casos es más de lo que se espera. Aunque los resultados no son mostrados aquí, los tiempos de comunicación no varían se mantienen estables con el cambio del número de procesos. Por lo tanto, la comunicación de los bordes no está contribuyendo a reducir de manera significativa el rendimiento del algoritmo.

4.1.2 Prueba 2. Mejoras en el acceso a memoria

Como mencionamos anteriormente, el orden de acceder a la memoria por medio de los índices de las matrices en Fortran puede variar la velocidad de dicho acceso. Así en esta segunda prueba se presentan los resultados de cambiar el orden en cómo se ejecuta el esquema numérico, invirtiendo el orden de los índices de los ciclos do, como se muestra en el extracto de los códigos en las Figuras 5 y 6.

Código de la configuración del stencil original.
Figura 5: Código de la configuración del stencil original.
Código de la configuración del stencil con acceso invertido.
Figura 6: Código de la configuración del stencil con acceso invertido.

En el código 5, siguiendo el orden de los ciclos do de la configuración original, la matriz se recorre haciendo barridos horizontales a través del índice mientras avanza a lo vertical con el índice después de cada barrido horizontal. Dicha configuración fue cambiada de tal manera que se invirtiera el orden del acceso, como se ve en el código 6. En dicho código se recorre la matriz haciendo barridos verticales a través del índice mientras avanza a lo horizontal con el índice después de cada barrido vertical.

Los resultados con estos cambios se presentan en la Figura 7. Se puede observar que el cambiar el orden de acceso a la matriz al momento de aplicar el esquema numérico mejora significativamente el tiempo de ejecución y consecuentemente el speed-up y la eficiencia. También se puede observar que la configuración vertical obtiene mejores resultados consiguiendo hasta un 72% de eficiencia para 20 procesos en paralelo, comparado con el primer código que se usó en el que sólo se obtuvo un 41%, ver Figura 4.

Review 872701252065-Fig04 Ex1 sp v vs v inv.png Speed-up y eficiencia de la configuración vertical usando diferente número de procesos y cambiando el orden de acceso a la memoria (versión invertida).
Figura 7: Speed-up y eficiencia de la configuración vertical usando diferente número de procesos y cambiando el orden de acceso a la memoria (versión invertida).

4.1.3 Prueba 3. Implementación de punteros

Aunque se logró mejorar, aún existe una caída significativa del rendimiento del algoritmo. Notar que en los Códigos 5 y 6, después de aplicar el esquema numérico a la matriz , es necesario actualizar dicha matriz. Una matriz almacena temporalmente los nuevos resultados y posteriormente es copiada a . Este proceso de actualización de la matriz local consume relativamente mucho tiempo, ya que los procesos de lectura y escritura son procesos computacionales que se consideran “costosos”, ya que son lentos si los comparamos con los procesos lógico-aritméticos.

Para solucionar el problema planteado, se consideró hacer uso de punteros como medio alternativo para actualizar las matrices locales luego de aplicar el esquema numérico. En Fortran, el uso de punteros no supone mucha complicación, ya que basta con editar y agregar unas cuantas líneas para hacer uso de ellos. Como se observa en el código de la Figura 8, una vez declarados los punteros y las matrices locales y , sólo es necesario apuntar con el puntero pB a la matriz local y con el puntero pC a la matriz .

Código de la implementación de punteros para actualizar matriz local.
Figura 8: Código de la implementación de punteros para actualizar matriz local.

En la sección del código en el que se aplica el esquema, es necesario remplazar el uso de las matrices locales y por sus respectivos punteros. De esta manera, la actualización de las matrices locales consistirá en hacer que los punteros pB y pC intercambien sus direcciones a las que apuntan. Para ello es necesario hacer que el puntero pB apunte a lo que apuntaba pC y que el puntero pC apunte a lo que apuntaba pB, para este proceso es necesario utilizar un puntero auxiliar, pSwap.

En la Tabla 1 se encuentran los mejores resultados obtenidos, los cuales se lograron con la configuración vertical y acceso a matriz con barridos horizontales. En la Figura 9 se observan los tiempos de ejecución y la eficiencia, en las que se aprecia el nivel de rendimiento obtenido de la configuración vertical con acceso a la matriz por barridos horizontales e implementando punteros. Notar que inclusive con 20 procesadores podemos obtener una eficiencia de $97%$.


Tabla. 1 Datos obtenidos con configuración vertical e implementando punteros.
Procesos Tiempo (s) Speed-up Eficiencia
1 58.173 1.000 1.000
2 29.199 1.992 0.996
3 19.590 2.969 0.989
4 14.572 3.991 0.997
5 11.766 4.943 0.988
6 9.846 5.908 0.984
7 8.433 6.897 0.985
8 7.404 7.856 0.982
9 6.608 8.802 0.978
10 6.030 9.647 0.964
11 5.499 10.577 0.961
12 5.040 11.542 0.961
13 4.710 12.349 0.949
14 4.362 13.336 0.952
15 4.188 13.890 0.926
16 3.766 15.445 0.965
17 3.818 15.235 0.896
18 3.476 16.731 0.929
19 3.471 16.759 0.882
20 2.975 19.547 0.977
Review 872701252065-Fig05 Ex1 tp v pointers.png Tiempo de ejecución (en segundos) y eficiencia usando la configuración horizontal e implementando punteros con diferente número de procesos.
Figura 9: Tiempo de ejecución (en segundos) y eficiencia usando la configuración horizontal e implementando punteros con diferente número de procesos.

4.2 Ejemplo 2: Solución de la ecuación de Poisson en paralelo

En este ejemplo se muestran los resultados obtenidos con el programa desarrollado para resolver sistemas lineales por el método de Jacobi provenientes de la ecuación de Poisson y el método de diferencias finitas aplicado a un dominio rectangular con mallado estructurado. Considérese la ecuación de Poisson en 2D dada por (1) donde la solución analítica y la función del lado derecho están dadas por

(11)

respectivamente. Con estas funciones se aplican condiciones de frontera Dirichlet nulas sobre el dominio dado por .

Para esta prueba se eligió un mallado de dimensiones . Dicho tamaño se escogió pensando en generar un tiempo considerable de ejecución y una malla que tenga una gran cantidad de divisores de manera que, sin contar sus fronteras, es exactamente divisible entre la cantidad de procesos que se ejecuten en paralelo. Por último se escogió una tolerancia de para determinar la condición de paro del método iterativo. En la Figura 10 se puede observar la solución exacta y numérica, así como los errores absolutos de este ejemplo. El algoritmo propuesto resuelve de manera correcta el problema y otorga una solución precisa a la solución exacta.

Review 872701252065-Fig06 Ex2 pexact.png Review 872701252065-Fig06 Ex2 paprox.png Solución exacta, solución numérica y error absoluto de la ecuación de Poisson en 2D.
Figura 10: Solución exacta, solución numérica y error absoluto de la ecuación de Poisson en 2D.

A continuación se presentan los resultados obtenidos de los tiempos de ejecución logrados en las configuraciones con distintos número de procesos. Hay que recordar que la máxima eficiencia del programa se logra si la dimensión de la malla (sin fronteras) es divisible exacto entre el número de procesos en paralelo ejecutándose. El mallado de es divisible entre 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 18 y 20. La Tabla 2 muestra la mediana de los tiempos de ejecución después de ejecutar varias veces el programa. Los resultados de speed-up y eficiencia también pueden ser observados en sus representaciones gráficas en la Figura 12.


Tabla. 2 Eficiencia de la aproximación de la ecuación de Poisson en 2D con una malla de .
Procesos Tiempo (s) Speed-up Eficiencia
1 198.256 1.000 1.000
2 102.978 1.925 0.962
3 68.377 2.899 0.966
4 50.172 3.951 0.987
5 41.049 4.829 0.965
6 33.978 5.834 0.972
8 25.112 7.894 0.986
9 22.041 8.994 0.999
10 20.177 9.825 0.982
12 17.763 11.160 0.930
15 13.689 14.482 0.965
18 11.323 17.508 0.972
20 10.289 19.268 0.963
Tiempos de ejecución usando diferente número de procesos.
Figura 11: Tiempos de ejecución usando diferente número de procesos.

Como se puede observar, los resultados muestran altos niveles de eficiencia. Por ejemplo, la menor eficiencia del programa se obtiene usando 12 procesadores con un . Más aún, todas las configuraciones mantienen un nivel parecido de eficiencia, es decir, que no se logra apreciar una pérdida de rendimiento conforme más procesos se ejecuten. Lo anterior da como consecuencia que se logre hasta un speed-up de 19.2 con 20 procesos, muy cercano a lo ideal que sería 20. En tiempos de cómputo reales, una simulación en serie que duraba más de tres minutos, puede ser ahora obtenida en tan sólo 10 segundos con la misma precisión.

Review 872701252065-Fig07 Ex2 estructurado sp.png Rendimiento de la aproximación de la ecuación de Poisson en 2D empleando una malla de 362×362.
Figura 12: Rendimiento de la aproximación de la ecuación de Poisson en 2D empleando una malla de .

5 Conclusiones

En este trabajo se presentó el método iterativo de Jacobi en paralelo para resolver el sistema de ecuaciones lineales resultante de discretizar la ecuación de Poisson en 2D usando el método de diferencias finitas. Aunque este método tiene la desventaja de una lenta convergencia en comparación con otros métodos iterativos más avanzados, el tiempo de ejecución se compensa con la fácil implementación del algoritmo en paralelo. Se plantearon varias alternativas para implementar el algoritmo incluyendo diferentes opciones de acceso a la memoria. La configuración con pivotes mostró los resultados con mayor rendimiento. Se tuvo una eficiencia por arriba del 90% y un speed-up de hasta 19.2 con 20 procesos en paralelo. En trabajos futuros se implementarán el mismo análisis para aproximaciones de la ecuación de Poisson en 2D usando volúmenes finitos y mallados triangulares no estructurados. También se implementará métodos iterativos con mayor velocidad de convergencia, lo cual ayudará a reducir aún más el tiempo de ejecución de las simulaciones numéricas.

Agradecimientos

Se agradece al Consejo Nacional de Ciencia y Tecnología (CONACYT) por el apoyo para la realización de este trabajo con el proyecto de Investigadoras e Investigadores por México.

BIBLIOGRAFÍA

[1] Notay, Y., & Napov, A. (2015). A massively parallel solver for discrete Poisson-like problems. Journal of computational physics, 281, 237-250.
[2] Bolis, A., Cantwell, C. D., Moxey, D., Serson, D., & Sherwin, S. J. (2016). An adaptable parallel algorithm for the direct numerical simulation of incompressible turbulent flows using a Fourier spectral/hp element method and MPI virtual topologies. Computer physics communications, 206, 17-25.
[3] Uh Zapata, M., Zhang, W., Van Bang, D. P., & Nguyen, K. D. (2019). A parallel second-order unstructured finite volume method for 3D free-surface flows using a coordinate. Computers & Fluids, 190, 15-29.
[4] Zhang, W., Uh Zapata, M., Bai, X., Pham-Van-Bang, D., & Nguyen, K. D. (2020). Three-dimensional simulation of horseshoe vortex and local scour around a vertical cylinder using an unstructured finite-volume technique. International Journal of Sediment Research, 35(3), 295-306.
[5] Nicholls, A., & Honig, B. (1991). A rapid finite difference algorithm, utilizing successive over?relaxation to solve the Poisson?Boltzmann equation. Journal of computational chemistry, 12(4), 435-445.
[6] Gupta, M. M., & Kouatchou, J. (1998). Symbolic derivation of finite difference approximations for the three?dimensional Poisson equation. Numerical Methods for Partial Differential Equations: An International Journal, 14(5), 593-606.
[7] Balls, G. T., & Colella, P. (2002). A finite difference domain decomposition method using local corrections for the solution of Poisson's equation. Journal of Computational Physics, 180(1), 25-53.
[8] Basermann, A., Reichel, B., & Schelthoff, C. (1997). Preconditioned CG methods for sparse matrices on massively parallel machines. Parallel computing, 23(3), 381-398.
[9] Gordon, D., & Gordon, R. (2010). CARP-CG: A robust and efficient parallel solver for linear systems, applied to strongly convection dominated PDEs. Parallel Computing, 36(9), 495-515.
[10] De Sturler, E., & van der Vorst, H. A. (1995). Reducing the effect of global communication in GMRES (m) and CG on parallel distributed memory computers. Applied Numerical Mathematics, 18(4), 441-459.
[11] Ghysels, P., Ashby, T. J., Meerbergen, K., & Vanroose, W. (2013). Hiding global communication latency in the GMRES algorithm on massively parallel machines. SIAM journal on scientific computing, 35(1), C48-C71.
[12] Evans, D. J. (1984). Parallel SOR iterative methods. Parallel computing, 1(1), 3-18.
[13] Tang, T., Liu, W., & McDonough, J. M. (2013). Parallelization of linear iterative methods for solving the 3-D pressure Poisson equation using various programming languages. Procedia Engineering, 61, 136-143.
[14] Uh Zapata, M., Van Bang, D. P., & Nguyen, K. D. (2018). Parallel simulations for a 2D x/z two-phase flow fluid-solid particle model. Computers & Fluids, 173, 103-110.
[15] Smelyanskiy, M., et al. (2009). Mapping high-fidelity volume rendering for medical imaging to CPU, GPU and many-core architectures. IEEE transactions on visualization and computer graphics, 15(6), 1563-1570.
[16] Abbas?Turki, L. A., Vialle, S., Lapeyre, B., & Mercier, P. (2014). Pricing derivatives on graphics processing units using Monte Carlo simulation. Concurrency and Computation: Practice and Experience, 26(9), 1679-1697.
[17] Liu, Y., Schmidt, B., & Maskell, D. L. (2011). DecGPU: distributed error correction on massively parallel graphics processing units using CUDA and MPI. BMC bioinformatics, 12(1), 1-13.
[18] Lehmkuhl, O., Perez-Segarra, C. D., Borrell, R., Soria, M., & Oliva, A. (2009). TERMOFLUIDS: A new Parallel unstructured CFD code for the simulation of turbulent industrial problems on low cost PC Cluster. In Parallel computational fluid dynamics 2007 (275-282). Springer, Berlin, Heidelberg.
[19] Becker, D. J., Sterling, T., Savarese, D., Dorband, J. E., Ranawak, U. A., & Packer, C. V. (1995, August). BEOWULF: A parallel workstation for scientific computation. In Proceedings, International Conference on Parallel Processing (Vol. 95, pp. 11-14).
[20] Schikuta, E. (1994). Message-passing-interface-forum: Mpi: A message-passing interface standard. Techn. Ber., University of Tennessee, Knoxville, Tennesee, 30.

[21] Gropp, W., Gropp, W. D., Lusk, E., Skjellum, A., & Lusk, A. D. F. E. E. (1999). Using MPI: portable parallel programming with the message-passing interface (Vol. 1). MIT press.

Back to Top

Document information

Published on 14/12/21
Submitted on 13/11/21

Licence: CC BY-NC-SA license

Document Score

0

Views 210
Recommendations 0

Share this document

claim authorship

Are you one of the authors of this document?