Escolar Documentos
Profissional Documentos
Cultura Documentos
Paralelas
Nombre Mail
Rafael Juan Jiménez Godoy rafaeljjg@gmail.com
María del Carmen Martín Tejedor mmartin@isys.dia.fi.upm.es
Santiago González Tortosa sgonzalez@isys.dia.fi.upm.es
Fernando Nieto Moraleda fernandonm@wanadoo.es
MPI y Librerías Paralelas Grupo 15
ÍNDICE DE FIGURAS...............................................................................................................................3
1 INTRODUCCIÓN....................................................................................................................................5
7 BIBLIOGRAFÍA.....................................................................................................................................43
Índice de Figuras
1 Introducción
En 1966 Flynn estableció una clasificación según la cual todo sistema de
cómputo pertenece a una de las siguientes cuatro categorías:
Dentro del tipo SIMD se consideran los sistemas que a partir de una única
instrucción son capaces de procesar un conjunto o array de datos en forma simultánea,
con la particularidad de ejecutar la misma instrucción sobre cada uno de sus elementos.
Estos sistemas se componen de varias unidades funcionales idénticas (Unidades
Aritmético-Lógicas, Registros de Direcciones de Memoria, etc.) controladas todas ellas
desde una única unidad de control encargada de recibir y procesar cada instrucción a
ejecutar. Podría considerarse que, aunque en forma restringida, esta arquitectura
incorpora en cierta medida el paralelismo desde el momento que es capaz de ejecutar
"algunas tareas" (no "cualquier tarea") de forma simultánea, concretamente puede
realizar N operaciones O, sobre N datos D, al mismo tiempo.
Sin embargo, no se puede ejecutar tareas sobre segmentos de datos, tales que
absolutamente todos tengan aspecto vectorial. Para que esta modalidad pueda ser
implementada, la arquitectura correspondiente deberá combinar necesariamente las
prestaciones SIMD con las SISD.
de modo que, al ejecutar cada una de ellas un determinado flujo de instrucciones (SI)
sobre su propio flujo de datos (SD), se logra que, en conjunto, múltiples flujos de
instrucciones (MI) actúen sobre otros tantos flujos de datos (MD).
MPI es, como su nombre indica, una interfaz, lo que quiere decir que el estándar
no exige una determinada implementación del mismo. Lo importante es dar al
programador una colección de funciones para que éste diseñe su aplicación, sin que
tenga necesariamente que conocer el hardware concreto sobre el que se va a ejecutar, ni
la forma en la que se han implementado las funciones que emplea.
• básico (basic)
• con buffer (buffered)
• síncrono (synchronous)
• listo (ready).
En cuanto al envío en modo listo, sólo se puede hacer si antes el otro extremo
está preparado para una recepción inmediata. No hay copias adicionales del mensaje
(como en el caso del modo con buffer), y tampoco podemos confiar en bloquearnos
hasta que el receptor esté preparado.
2.2.2Comunicación básica
Dest es, en las funciones de envío, el identificador del proceso destinatario del
mensaje.
Source es, en las funciones de recepción, el identificador del emisor del cual
esperamos un mensaje. Si no nos importa el origen del mensaje, podemos poner
MPI_ANY_SOURCE.
Status es un resultado que se obtiene cada vez que se completa una recepción, y
nos informa de aspectos tales como el tamaño del mensaje recibido, la etiqueta del
mensaje y el emisor del mismo.
typedef struct {
int MPI_SOURCE;
int MPI_TAG;
/* otros campos no accesibles */
} MPI_Status;
Figura 2: Definición de la estructura MPI_Status
te rm in a e n v ío
esperar
d e d a to s
S S
R R
esperar L a ta re a c o n tin u a c u a n d o la
b u ffe r d e l tra n s fe re n c ia d e d a to s a l
re c e p to r b u ffe r d e l u su a rio s e te rm in a
MPI_RECV M P _I R E C V
Uno de los problemas más fundamentales que tiene el envío básico es, que el
programador no tiene control sobre cuánto tiempo va a tardar en completarse la
operación.
Para que se pueda usar el envío con buffer es necesario que el programador
asigne un buffer de salida. Para ello hay reservar, primeramente, una zona de memoria,
de forma estática o con malloc. Luego, se debe indicar al sistema que dicha zona se va a
emplear como buffer. Esta última operación la hace MPI_Buffer_attach(). Dicho buffer
se puede recuperar usando MPI_Buffer_detach().
Una peculiaridad de los envíos con buffer es, el posible fracaso cuando dicho
buffer no tiene suficiente espacio para contener el mensaje, por lo que el programa
aborta. El programador puede, entonces, asignar un buffer más grande para una
ejecución posterior, o bien cambiar el modo de comunicación a otro con menos
requerimientos de buffer pero con más riesgo de bloqueo.
Por ejemplo, podemos estar a la espera de mensajes de tres clases, cada una
asociada a un tipo de datos diferente, y la clase nos viene dada por el valor de la
etiqueta. Por lo tanto, nos gustaría saber el valor de la etiqueta antes de leer el mensaje.
También puede ocurrir que nos llegue un mensaje de longitud desconocida, y resulte
necesario averiguar primero el tamaño para así asignar dinámicamente el espacio de
memoria requerido por el mensaje.
2.2.5Tipos de datos
Los mensajes gestionados por MPI son secuencias de X elementos del tipo
datatype. MPI define una colección de tipos de datos primitivos, correspondientes a los
tipos de datos existentes en C.
Los procesadores que participan en una aplicación MPI no tienen por qué ser
todos iguales. Es posible que existan diferencias en la representación de los datos en las
distintas máquinas, como puedan ser procesadores RISC y CISC. Para eliminar los
problemas que puedan surgir, MPI realiza, si son necesarias, transformaciones de
sintaxis que posibilitan la comunicación en entornos heterogéneos. La excepción la
constituyen los datos de tipo MPI_BYTE, que se copian sin más de una máquina a otra.
Aparte de los tipos simples definidos de forma primitiva por MPI, se permite la
definición de tipos de usuario, más complejos, pero este apartado no lo vamos a ver ya
que se aleja mucho del tema propuesto.
2.2.6Etiquetas y comunicadores
Todo mensaje que se envía con MPI va etiquetado: parámetro tag. El proceso
receptor puede elegir entre aceptar mensajes con una cierta etiqueta (parámetro tag con
un valor concreto), o decir que acepta un mensaje cualquiera (MPI_ANY_TAG).
MPI_COMM_WORLD está siempre definido, e incluye a todos los procesos que forman parte
de la aplicación.
MPI_Comm_free(MPI_Comm *Comm);
Los miembros del grupo tienen asignada una dirección dentro de él. Un proceso
puede pertenecer simultáneamente a varios grupos, y tener una dirección distinta en
cada uno de ellos.
#include "mpi.h";
comm_world = MPI_COMM_WORLD;
MPI_Comm_group(comm_world, &group_world);
MPI_Group_excl(group_world, 1, 0, &group_worker);
MPI_Comm_create(comm_world, group_worker, &comm_worker);
/* ACCIONES */
MPI_Comm_free(&comm_worker);
Figura 10: Ejemplo de uso de comunicadores
• Barreras de sincronización
• Broadcast (difusión)
• Gather (recolección)
• Scatter (distribución)
• Operaciones de reducción (suma, multiplicación, mínimo, etc.)
• Combinaciones de todas ellas
Una operación colectiva tiene que ser invocada por todos los participantes,
aunque los roles que juegen no sean los mismos. La mayor parte de las operaciones
requieren la designación de un proceso como root, o raíz de la operación.
Se ha optado por realizar un reparto dinámico por filas, es decir, una estructura
cliente-servidor, en el que el servidor (rango 0) únicamente reparte el trabajo entre los
clientes (rango distinto de 0) que esten libres.
Los clientes irán, pues, solicitando mas trabajo una vez hayan terminado con el
trabajo anterior. Es por ello, que se trata de un reparto dinámico. Además, la forma de
repartir el trabajo es tal que, a cada cliente se le proporcionan los datos necesarios para
que halle una fila completa de la matriz resultado. Específicamente, al cliente se le
proporciona una fila i de una de las matrices y la otra matriz entera.
#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
#include "aux.c" // Funciones auxiliares
double inicial,final,tiempo;
int nodo;
int i=0,j=0,m,res[C2];
int fila,col;
int posicion;
MPI_Status st;
MPI_Status sth;
int control;
/* Se inicializa el MPI */
MPI_Init(&argc, &argv);
if (argc != 2){
fprintf(stderr,"Usage:\n\tmatmul_dist_fila <result-file>\n");
return -1;
}
inicio();
inicial=MPI_Wtime();
MPI_Comm_rank(MPI_COMM_WORLD,&rango);
if (rango==0){
/*fprintf(stderr,"PADRE: Comienzo\n"); */
for (m=1;m<=maq;m++){
MPI_Send(&i,1,MPI_INT,m,0,MPI_COMM_WORLD);
/*fprintf(stderr,"PADRE: Envio Trabajo al Nodo %d\n",m); */
i++;
}
while(!fin){
MPI_Recv(&posicion,1,MPI_INT,MPI_ANY_SOURCE,0,MPI_COMM_WORLD,&st);
nodo=st.MPI_SOURCE;
MPI_Recv(&res,C2,MPI_INT,nodo,0,MPI_COMM_WORLD,&st);
/*fprintf(stderr,"PADRE: Recibo solucion del trabajo del
Nodo %d\n",nodo); */
for (j=0;j<C2;j++){
matrizr[posicion][j]=res[j];
}
if (i != F1){
/*fprintf(stderr,"PADRE: Envio Trabajo al Nodo
%d\n",nodo); */
MPI_Send(&i,1,MPI_INT,nodo,0,MPI_COMM_WORLD);
i++;
}else{
fin=1;
control=-1;
MPI_Send(&control,1,MPI_INT,nodo,0,MPI_COMM_WORLD);
for (m=1;m<maq;m++){
MPI_Recv(&posicion,1,MPI_INT,MPI_ANY_SOURCE,0,MPI_COMM_WORLD,&st);
nodo=st.MPI_SOURCE;
MPI_Recv(&res,C2,MPI_INT,nodo,0,MPI_COMM_WORLD,&st);
/*fprintf(stderr,"PADRE: Recibo solucion del
trabajo del Nodo %d\n",nodo); */
for (j=0;j<C2;j++){
matrizr[posicion][j]=res[j];
}
MPI_Send(&control,1,MPI_INT,nodo,0,MPI_COMM_WORLD);
/*fprintf(stderr,"PADRE: Paro el Nodo %d\n",m);
*/
}
}
}
final=MPI_Wtime();
tiempo=final-inicial;
guarda_res(argv[1]);
printf("TIME: %f seconds\n",tiempo);
}else{
/*fprintf(stderr,"HIJO %d: Comienzo\n",rango); */
control=1;
while(control!=-1){
MPI_Recv(&control,1,MPI_INT,0,0,MPI_COMM_WORLD,&sth);
/*fprintf(stderr,"HIJO %d: Recibo Trabajo: %d\n",rango,control);
*/
if (control!=-1){
/*fprintf(stderr,"HIJO: Fila: %d Columna:
%d\n",fila,col);*/
for (i=0;i<C2;i++){
res[i]=0;
for (j=0;j<C1;j++){
res[i]+=(matriz1[control][j]*matriz2[j][i]);
}
}
MPI_Send(&control,1,MPI_INT,0,0,MPI_COMM_WORLD);
MPI_Send(&res,C2,MPI_INT,0,0,MPI_COMM_WORLD);
/*fprintf(stderr,"HIJO %d: Envio Trabajo\n",rango); */
}
}
}
MPI_Finalize();
return 0;
}
Figura 11: Ejemplo MPI. Producto de matrices con distribución dinámica de filas
3 Librerías Paralelas
3.1 Motivación
Las librerías software ofrecen algunas ventajas:
Por esta razón las librerías paralelas están a menudo escritas en un estilo con el
cual la ejecución del código de usuario y el código de las librerías esta alternado de
manera estricta, de tal forma que se evite que se esté transmitiendo un mensaje cuando
el control pasa de un usuario a una librería paralela y al revés (este estado es llamado
comúnmente “quiescence”). Este tipo de diseño, heredado de los modelos secuenciales,
introduce barreras de sincronización que inhiben la ejecución de estos problemas, y
también evitan otros problemas sin resolver.
topologías, que permiten a los usuarios describir las relaciones entre los
procesos que ellos deseen,
Los contextos son manejados por el sistema por motivos de seguridad; si los
usuarios definieran contextos, dos librerías podrían definir por equivocación el mismo
contexto. Los usuarios no trabajan directamente con los contextos; más bien, trabajan
con los grupos de procesos y con los comunicadores.
#include “ibcast.h”
Ibcast_handle *request;
…
Ibcast(buf, count, datatype, root, comm, &request);
…
Ibcast_wait(&request);
#include “mpi.h”
/* manejo de las operaciones ibcast en un comunicador */
typedef struct ibcast_syshandle
{
MPI_Comm comm;
int ordering_tag;
} Ibcast_syshandle;
MPI_Status *stat_array;
int num_sends;
int num_recvs;
} Ibcast_handle;
Figura 13: Contenido del fichero ibcast.h
Ibcast construye un árbol de difusión de enviados y recibidos dada una raíz. Para
cada llamada, asigna una nueva etiqueta de orden (ordering tag), para prevenir
cualquier interferencia de las anteriores, a la llamada a Ibcast en curso. A este tipo de
interferencia se la denomina “back-masking”, la cual produce que una llamada posterior
a Ibcast pudiera interponer sus datos a los datos de una llamada anterior Ibcast,
produciendo un resultado erróneo.
El contexto duplicado (el cual protege a Ibcast del usuario y de otras librerías),
junto a la estrategia de etiquetas incrementales (el cual protege una llamada a la
librería Ibcast de otras llamadas), elimina la posibilidad de que se produzcan
interferencias “back-masking”. De esta forma, Ibcast es aislado de todas las demás
MPI_KEYVAL_FREE(keyval, ierror)
integer keyval, ierror
En algunos casos, una librería debe asegurar que la información de los atributos
es propagada a los nuevos comunicadores creados con MPI_Comm_dup. Para ello, tan
solo es necesario hacer un simple cambio para añadir esta característica. Cambiando una
línea de Ibcast, podemos añadir las llamadas externas de copia y borrado.
#include “ibcast.h”
int Ibcast_work(Ibcast_handle *handle){
/* si no tengo ninguna recepción, comienzo todos mis envíos – la
raíz */
if (handle->num_recvs == 0)
MPI_Startall (handle->num_sends, handle->req_array);
/* comienza todas mis recepciones */
else
MPI_Starall (handle->num_recvs, &handle->req_array[handle-
>num_sens]);
Return (MPI_SUCESS);
}
Figura 17: Funcion Ibcast_work
En sistemas que soportan hilos o mensajes activos, las ideas y el código descrito
arriba podrían ser usados para crear una reducción y una barrera no bloqueantes, así
como otras llamadas colectivas no directamente soportadas por el estándar MPI.
BLACS trabaja siempre con una red bidimensional de MxN procesos, de tal
forma que un conjunto de procesos 0, 1, ..., Np-1 se mapea como una malla de PxQ
procesos, donde PxQ = Nq <= Np. Estas redes siempre son matrices aunque en BLACS
podemos trabajar tanto con matrices rectangulares como trapezoidales.
5.1.2 Funciones
BLACS tiene definida una nomenclatura muy clara para todas sus funciones.
Todas las rutinas siguen el patrón de nombrado CvXXYY2d. Siendo v el tipo de datos
(I(Integer), s (String), d (double), c (char), z), XX la forma de la matriz (trapezoidal o
rectangular, ge o tr) e YY el tipo de comunicación (sd o rv para indicar si es envío o
recepción, bs o br para indicar si es broadcast). Este esquema de nombrado es el que
aparece en la siguiente tabla:
CvXXYY2d
Tipo de datos: v I(Integer), s (String), d
(double), c (char), z
Forma de la matriz: Ge, tr
XX
Tipo de comunicación: Isd, rv (Send o Receive)
YY Bs, br (Broadcast)
Es una rutina de broadcast para el envía a todos los procesos indicados por un
par (Contexto, SCOPE). Siendo SCOPE = Todos, columna o fila usando una
distribución dada por la topología TOP.
Rutina que realiza la suma dentro de una matriz sobre el par designado por
(Contexto, Scope), usando la topología TOP y dejando el resultado en el proceso
{RDEST,CDEST}.
Todos los programas que usen BLACS deben llamar esta rutina, o a su rutina
hermana BLACS_GRIDMAP. Esta rutina toma los procesos disponibles, y los asignan
en un GRID de procesos de BLACS. Cada GRID de BLACS está contenido en un
contexto, de modo que no interfiera con las operaciones distribuidas que ocurren dentro
del otros grids/contextos. Estas rutinas de creación de GRIDS se pueden llamar en
varias ocasiones para definir contextos/GRIDS adicionales.
1. BLACS_GRIDEXIT( ICONTXT )
2. BLACS_EXIT( CONTINUE )
Rutina que debe ser llamada cuando un proceso deja de utilizar BLACS, si se
llama a la rutina con CONTINUE = 1 se podrá seguir utilizando MPI.
5.1.3 Ejemplo
Este ejemplo representa el envío de un bloque por filas desde los procesadores
de la columna 0.
(1) Cblacs_get(0,0,&ICTXT);
(2) Cblacs_gridinit(&ICTXT, "R",
(3) nP_Row, nP_Col);
(4) Cblacs_gridinfo(ICTXT &nP_Row,&nP_Col,&my_P_Row, &my_P_Col);
(5) printf("(Proc MPI %d) fila %d col %d \n", pr, my_P_Row,
my_P_Col);
(6) MPI_Barrier(MPI_COMM_WORLD );
(7) for (i=0;i<nP_Row;i++) {
(8) nBlkTamPorFil[i] = nDim / nP_Row;
(9) if (my_P_Row < nDim nDim % nP_Row)
(10) nBlkTamPorFil[i]++;
(11) }
(12) for (j=0;j<nP_Col;j++) {
(13) nBlkTamPorCol[j] = nDim / nP_Col;
(14) if (my_P_Col < nDim % nP_Col)
(15) nBlkTamPorCol[j]++;
(16) }
(17) aLclBlk = (double *)
(18) malloc(sizeof(double *
(19) nBlkTamPorFil[my_P_Row] *
(20) nBlkTamPorCol[my_P_Col]);
(21)
(22) if (my_P_Col == 0) {
(23) RellenarBlk(aLclBlk,
(24) nBlkTamPorFil[my_P_Row],
(25) nBlkTamPorCol[my_P_Col], my_P_Row);
(26) Cdgebs2d(ICTXT, "ROW", " ",
(27) nBlkTamPorFil[my_P_Row],
(28) nBlkTamPorCol[my_P_Col], aLclBlk,
(29) nBlkTamPorFil[my_P_Row]);
(30) } else {
(31) Cdgebr2d(ICTXT, "ROW", " ",
(32) nBlkTamPorFil[my_P_Row],
(33) nBlkTamPorCol[my_P_Col], aLclBlk,
(34) nBlkTamPorFil[my_P_Row],
(35) my_P_Row, 0);
(36) }
(37) Cblacs_gridexit(ICTXT);
(38) Cblacs_exit(1);
(39) MPI_Finalize();
Figura 21: Ejemplo del uso de la librería BLACS
5.2 PBLAS
5.2.1 Introducción
PBLAS son las siglas de “Parallel Basic Linear Algebra Subprograms”, esta
librería es la unión de BLAS y BLACS, mas en concreto es BLAS paralelo para
máquinas de memoria distribuida (multicomputadores) tanto es así que las llamadas a
PBLAS son muy parecidas a las de BLAS. De este modo PBLAS ofrece a ScaLAPACK
rutinas equivalentes a las ofrecidas por BLAS a LAPACK. Como ejemplo podríamos
tener las llamadas a DGEMM de BLAS y su correspondiente PDGEMM en PBLAS.
Toda matriz distribuida esta siempre definida por sus dimensiones M_ x N_, el
tamaño de bloque MB_ x NB_ usado en su descomposición, las coordenadas del
proceso que tiene en memoria la primera estrada de la matriz (RSRC_, CSRC_) y el
contexto BLACS: CTXT_ en el que esta matriz está definida. Finalmente, para el
almacenamiento local de la matriz, se asocia una dimensión LLD_.
Campo Significado
TYPE El tipo del Descriptor
CTXT Contexto BLAS
M Filas Globales
N Columnas Globales
MB Tamaño de bloques de
filas
NB Tamaño de bloques de
Columnas
RSRC Número de procesos por
fila
CSRC Número de procesos por
columna
LLD Dimensión principal o de
almacenamiento
Otro factor a tomar en cuenta de PBLAS es que las rutinas pueden tener como
entrada submatrices. En este caso, los índices de Fila y Columna Global de la primera
fila y columna de la submatriz deben indicarse en IA, JA. En caso de utilizar una matriz
completa, estos deben tomar los valores 1,1.
5.2.2Funciones
PXYYZZZ(PBLAS 2 y 3)
Tipo de datos: X S(String), d(Double), c(char) y
z
Tipo de matriz: YY Ge, sy, he, tr
Operación: ZZZ Mm, mv, r, r2, rk, r2k, sm, sv.
La mayoría de las rutinas son análogas a las de BLAS o a las de BLACS por lo
que a continuación solo se describirán aquellas rutinas propias de PBLAS.
1. void descinit_( int desc[9],int *m, int *n, int *mb, int *nb, int *irsrc ,int *icsrc, int
*ictxt, int *lld, int *info)
Rutina que crea un descriptor con los datos comentados en el apartado anterior.
1. int numroc_(int *n, int *nb, int *myPos, int *First, int *nP)
• n es la dimensión Global.
• nb es el tamaño de Bloque.
• First es el Proceso que tiene la Primera Fila/Col.
• myPos es la posición del Proceso en la dimensión Cartesiana en Cuestión.
• nP es el N es el Número de Procesos en la dimensión Cartesiana.
• ceil(M/MB)/NPROW )*MB.
• ceil(N/NB)/NPCOL )*NB.
1. pdscal_(int *n, double *alfa, double *x, int *ix, int *jx, int *descx, int *incx)
1. pdgemm_(char *TRANSA, int *M, int *N, int *K, double *ALPHA, double *A, int
*IA, int *JA, int *DESCA, double *B, int *IB, int *JB, int *DESCB, double
*BETA, double *C, int *IC, int *JC, int *DESCC)
5.2.3Compilación
Para poder usar estas bibliotecas, es necesario importar una serie de ficheros, a
continuación se indican algunos:
1. Ficheros de Cabecera
• mpi.h
2. Bibliotecas
• pblas : pblas_LINUX.a, tools_LINUX.a, redist_LINUX.a
• blacs: blacs_MPII-LINUX-0.a, blacsCinit_MPI-LINUX-0.a
• mpi: libmpich.a
• blas: libblas.a
• Fortran: libf2c.a
5.2.4Ejemplo
(40) Cblacs_gridexit(ICTXT);
(41) Cblacs_exit(1);
(42) // Fin MPI
(43) MPI_Finalize();
(44) }
Figura 26: Ejemplo del uso de la librería PBLAS
Este es un simple ejemplo que utiliza las funciones básicas de PBLAS. Como
podemos observar las líneas de la 7 a la 11 en las cuales se inicializa MPI.
Seguidamente en las líneas que van desde las 13 a la 18 se encuentra las rutinas de
iniciación de BLACS. Como hemos comentado anteriormente en las aplicaciones que
usen PBLAS es recomendable utilizar las rutinas de BLACS únicamente para inicializar
y en el resto de la aplicación debemos usar las rutinas de PBLAS.
Las siguientes líneas (19 - 29) se limitan a completar el valor de un vector, sobre
el que más adelante se realizarán los cálculos. No obstante es útil observar el uso que se
hace de la función descinit, la cual la usamos para crear nuestro descriptor, como hemos
comentado antes, los descriptores son nuestro modo de definir el ámbito del grind.
Finalmente imprime el valor de salida por pantalla (líneas 35 a 37) y llama a las
rutinas de finalización de PBLAS y MPI.
5.3 SCALAPACK
5.3.1 Introducción
ScaLAPACK son las siglas de “Scalable Linear Algebra PACKage” que es una
librería software para la computación de álgebra lineal en computadores con memoria
distribuida. Las rutinas disponibles sirven para solucionar sistemas del tipo A*x=b, o
para encontrar el “valor propio” de distintos tipos de matrices. ScaLAPACK tiene como
bases en su construcción las versiones distribuidas de BLAS y PBLAS y para las tareas
de comunicación a BLACS.
ScaLAPACK asume que las matrices están distribuidas de forma cíclica por
bloques, de tal forma que las rutinas se basan en algoritmos que trabajan por bloques y
así minimizar los movimientos de datos.
5.3.2Funciones
Son las que resuelven un problema completo, como pueden ser por ejemplo
sistemas de ecuaciones lineales o valores propios de una matriz real simétrica.
• Ecuaciones lineales
• Para la resolución en modo simple (AX = B) podemos usar pxyySV
• Para la resolución en modo experto (AX = B) podemos usar pxyySVX.
La resolución en modo experto permite, además de solucionar el problema,
realizar otros cálculos relacionados como pueden ser:
Se dividen en:
6 Conclusiones
Como ya se ha dicho, MPI es una librería estandarizada para la realización de
aplicaciones paralelas basada en el paso de mensajes. La meta de MPI es desarrollar un
estándar para escribir programas que implementen el paso de mensajes, por lo que el
interfaz intenta establecer para esto un estándar práctico, portable, eficiente y flexible.
La creación de librerías paralelas usando MPI, que es el tema tratado en este trabajo,
requiere tratar dos puntos de vista: como desarrollador y como usuario.
Por tanto, podemos comprobar que el uso de librerías paralelas facilita, de gran manera,
la programación del usuario. No obstante, debemos tener en cuenta que, el desarrollo de
una librería paralela conlleva a dificultades de implementacion, no solo por ser una
aplicación paralela (que ya de por si puede ser complicado), sino que además se añade
la complejidad de interferencias en la comunicación.
7 Bibliografía
Using MPI: Portable Parallel Programming with the Message-Passing Interface.
William Gropp, Ewing Lusk, and Anthony Skjellum. Published in 1999 by MIT
Press
MPICH: http://www-unix.mcs.anl.gov/mpi/mpich/
LAM: http://www.lam-mpi.org
http://www-unix.mcs.anl.gov/mpi/usingmpi/examples/libraries/main.htm
http://asds.dacya.ucm.es/nacho/pp.html
www.dirinfo.unsl.edu.ar/~prgparal/Teorías/Clase4PPMPI2004.ppt
http://icl.cs.utk.edu/ftmpi/index.html
http://public.lanl.gov/lampi/
http://www.hlrs.de/organization/pds/projects/pacx-mpi/
8.1 ibcast.h
/*
* cabecera de la libreria ibcast, implementa difusion no bloqueante
*/
#include <mpi.h>
/* estructura ibcast_syshandle */
typedef struct {
MPI_Comm comm;
int ordering_tag;
} Ibcast_syshandle;
/* funciones */
int Ibcast(void *buf, int count, MPI_Datatype datatype, int root,
MPI_Comm comm, Ibcast_handle **handle_out);
int Ibcast_wait(Ibcast_handle **handle_out);
8.2 ibcast.c
/*
* ibcast.c
* implementa un broadcast no bloqueante
*/
#include <stdlib.h>
#include "ibcast.h"
/* funciones internas */
static int Ibcast_work(Ibcast_handle *handle);
int relrank;
int no_handles;
int retn;
int size;
int rank;
int req_no = 0;
MPI_Copy_function Ibcast_copy;
MPI_Delete_function Ibcast_delete;
/* si el tamaño es 1, ha terminado */
if (size == 1) {
(*handle_out) = NULL;
return(MPI_SUCCESS);
}
mask = 0x1;
relrank = (rank - root + size) % size;
while ((mask & relrank) == 0 && mask < size) {
if ((relrank | mask) < size) {
handle->num_sends++;
}
mask <<= 1;
}
if (mask < size) {
handle->num_recvs++;
}
mask = 0x1;
while ((mask & relrank) == 0 && mask < size) {
if ((relrank | mask) < size) {
MPI_Send_init(buf, count, datatype, ((relrank | mask) + root)
%size,
syshandle->ordering_tag, syshandle->comm,
&(handle->req_array[req_no++]));
}
mask <<= 1;
}
if (mask < size) {
MPI_Recv_init(buf, count, datatype, ((relrank & (~ mask)) + root)
%size,
syshandle->ordering_tag, syshandle->comm,
&(handle->req_array[req_no++]));
}
++(syshandle->ordering_tag);
if (handle == NULL) {
return(MPI_SUCCESS);
}
/* libero la estructura */
free(handle->req_array);
free(handle->stat_array);
free(handle);
new_syshandle = (Ibcast_syshandle *)
malloc(sizeof(Ibcast_syshandle));
/* destructor */
int Ibcast_delete(MPI_Comm comm, int keyval, void *attr_val, void
*extra) {
Ibcast_syshandle *syshandle = (Ibcast_syshandle *) attr_val;
MPI_Comm_free(&(syshandle->comm));
free(syshandle);
return(MPI_SUCCESS);
}
8.3 ibctest.c
/* ibctest.c
prueba la libreria ibcast
como minimo 2 procesos !!
*/
#include <stdio.h>
#include "ibcast.h"
MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &me);
if (me == 0) {
data1 = 42;
}
if (me == 1) {
data2 = 4711;
}
/* PROGRAMA PRINCIPAL */
/* imprimo resultados */
printf("me = %3d, data1 = %8d data2 = %8d\n", me, data1, data2);
MPI_Finalize();
return 0;
}