Você está na página 1de 49

MPI y Librerías

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

Patricia López Cueva plopez@datsi.fi.upm.es


Índice

ÍNDICE DE FIGURAS...............................................................................................................................3

1 INTRODUCCIÓN....................................................................................................................................5

2 MESSAGE PASSING INTERFACE (MPI)...........................................................................................8


2.1 INTRODUCCIÓN A MPI.............................................................................................................................8
2.2 COMUNICACIÓN PUNTO A PUNTO...............................................................................................................9
2.2.1 Modelos y modos de comunicación............................................................................................9
2.2.2 Comunicación básica................................................................................................................10
2.2.3 Comunicación con buffer..........................................................................................................12
2.2.4 Recepción por encuesta............................................................................................................13
2.2.5 Tipos de datos...........................................................................................................................14
2.2.6 Etiquetas y comunicadores.......................................................................................................14
2.3 COMUNICACIONES COLECTIVAS................................................................................................................16
2.4 EJEMPLO MPI......................................................................................................................................16
3 LIBRERÍAS PARALELAS...................................................................................................................19
3.1 MOTIVACIÓN.........................................................................................................................................19
3.2 DEFICIENCIAS DE MPI...........................................................................................................................19
3.3 REVISIÓN DE LAS CARACTERÍSTICAS QUE SOPORTAN LAS LIBRERÍAS................................................................20
4 UNA PRIMERA LIBRERÍA MPI........................................................................................................22

5 OTRAS LIBRERÍAS PARALELAS.....................................................................................................27


5.1 BLACS..............................................................................................................................................27
5.1.1 Introducción..............................................................................................................................27
5.1.2 Funciones..................................................................................................................................28
5.1.3 Ejemplo.....................................................................................................................................31
5.2 PBLAS..............................................................................................................................................32
5.2.1 Introducción..............................................................................................................................32
5.2.2 Funciones..................................................................................................................................34
5.2.3 Compilación..............................................................................................................................37
5.2.4 Ejemplo.....................................................................................................................................37
5.3 SCALAPACK...................................................................................................................................38
5.3.1 Introducción..............................................................................................................................38
5.3.2 Funciones..................................................................................................................................39
6 CONCLUSIONES...................................................................................................................................41

7 BIBLIOGRAFÍA.....................................................................................................................................43

8 ANEXO: IMPLEMENTACIÓN IBCAST...........................................................................................44


8.1 IBCAST.H .............................................................................................................................................44
8.2 IBCAST.C ..............................................................................................................................................44
8.3 IBCTEST.C ............................................................................................................................................47
8.4 COMPILACIÓN Y EJECUCIÓN....................................................................................................................48

Procesamiento Vectorial y Paralelo Página 2 de 49


MPI y Librerías Paralelas Grupo 15

Índice de Figuras

FIGURA 1: FUNCIONES MPI_SEND Y MPI_RECV.........................................................................10

FIGURA 2: DEFINICIÓN DE LA ESTRUCTURA MPI_STATUS....................................................11

FIGURA 3: ESTRUCTURA MPI_SEND Y MPI_RECV.....................................................................11

FIGURA 4: FUNCIONES DE EMISIÓN Y RECEPCIÓN NO BLOQUEANTES............................12

FIGURA 5: CABECERA DE LA FUNCIÓN MPI_BSEND.................................................................12

FIGURA 6: CABECERAS DE LAS FUNCIONES MPI_BUFFER_ATTACH Y


MPI_BUFFER_DETACH.........................................................................................................................13

FIGURA 7: CABECERAS DE LAS FUNCIONES MPI_IPROBE Y MPI_PROBE.........................13

FIGURA 8: RELACIÓN DE TIPOS DE MPI Y C/C++........................................................................14

FIGURA 9: CABECERAS DE FUNCIONES DE CREACIÓN Y ELIMINACIÓN DE


COMUNICADORES.................................................................................................................................15

FIGURA 10: EJEMPLO DE USO DE COMUNICADORES...............................................................15

FIGURA 11: EJEMPLO MPI. PRODUCTO DE MATRICES CON DISTRIBUCIÓN DINÁMICA


DE FILAS...................................................................................................................................................18

FIGURA 12: CABECERAS DE FUNCIONES IBCAST E IBCAST_WAIT......................................22

FIGURA 13: CONTENIDO DEL FICHERO IBCAST.H.....................................................................23

FIGURA 14: LLAMADAS EN C MPI QUE NECESITA IBCAST, Y LLAMADAS


RELACIONADAS.....................................................................................................................................25

FIGURA 15: LLAMADAS EN FORTRAN MPI QUE NECESITA IBCAST, Y LLAMADAS


RELACIONADAS.....................................................................................................................................25

FIGURA 16: FRAGMENTO DE CODIGO DEL FICHERO IBCAST.C...........................................25

FIGURA 17: FUNCION IBCAST_WORK.............................................................................................26

FIGURA 18: ESTRUCTURA Y JERARQUÍA DE LIBRERIAS.........................................................27

FIGURA 19: REPRESENTACIÓN DE PROCESADORES TRABAJANDO CON BLACS............28

FIGURA 20: REPRESENTACIÓN DE ENVÍO DE BLOQUES POR FILAS, UTILIZANDO


BLACS........................................................................................................................................................31

FIGURA 21: EJEMPLO DEL USO DE LA LIBRERÍA BLACS........................................................32

FIGURA 22: ESTRUCTURA Y JERARQUÍA DE LIBRERIAS (II)..................................................32

FIGURA 23: REPRESENTACIÓN DE LA DISTRIBUCIÓN DE MATRICES POR BLOQUES. .33

FIGURA 24: DIVISIÓN GLOBAL DE LAS MATRICES....................................................................34

Procesamiento Vectorial y Paralelo Página 3 de 49


MPI y Librerías Paralelas Grupo 15

FIGURA 25: REPRESENTACIÓN DEL DESCRIPTOR, USANDO PBLAS....................................35

FIGURA 26: EJEMPLO DEL USO DE LA LIBRERÍA PBLAS.........................................................38

FIGURA 27: ESTRUCTURA Y JERARQUÍA DE LIBRERIAS (II)..................................................38

Procesamiento Vectorial y Paralelo Página 4 de 49


MPI y Librerías Paralelas Grupo 15

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:

 SISD (Single Instruction stream -Single Data stream),


 SIMD (Single Instruction stream - Multiple Data stream),
 MISD (Multiple Instruction stream - Single Data stream) y
 MIMD (Multiple Instruction stream - Multiple Data stream).

A la categoría SISD corresponde el modelo de computador tradicional (Von


Neumann), en el que más allá de las variantes, se lleva acabo un procesamiento
secuencial mediante la ejecución de una única instrucción sobre un único dato por vez
(ej. cargar el contenido de una dirección de memoria en un acumulador, sumar 1 al
contenido de un registro, etc.).

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.

La categoría MISD corresponde a configuraciones en las que un tren de


unidades de procesamiento se surte por uno de sus extremos de un dato extraído de
memoria y devuelve un resultado a la misma memoria por el extremo opuesto, en tanto
cada unidad del tren toma por entrada al dato procesado por la unidad anterior y da por
salida el que será tomado como entrada por la unidad siguiente. La característica MI
está en el hecho de que cada unidad de procesamiento va ejecutando una operación
distinta sobre un único dato (SD) que va avanzando hasta convertirse en el resultado.

MIMD es un modelo en el cual el sistema puede considerarse de alguna forma


particionado en unidades con capacidad de,

 Desarrollar su propio cómputo y


 Comunicarse con las demás,

Procesamiento Vectorial y Paralelo Página 5 de 49


MPI y Librerías Paralelas Grupo 15

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).

SISD constituye, como ya se dijo, el modelo clásico de arquitectura de


computadoras en el que se basaron los diseños a lo largo de muchos años. En el intento
de lograr aumentos de performance, la producción de máquinas comerciales ha ido
incorporando elementos de los demás modelos. En mayor medida SIMD y en menor
MISD sirven al diseño de computadores de propósito específico pero no de equipos de
distribución masiva, en tanto la última categoría MIMD constituye no sólo la más
ambiciosa combinación posible de las dos variables (Instruction stream - Data stream)
sino el fundamento mismo del procesamiento paralelo en su expresión más completa.

Estos sistemas, a su vez, pueden clasificarse en dos grandes categorías, los


Multiprocesadores (o sistemas de memoria compartida), en los cuales la comunicación
entre las distintas unidades se realiza, en forma implícita a través de variables
compartidas dentro de una única memoria principal y los Multicomputadores (o
sistemas de memoria distribuida) en los que la comunicación explícita entre unidades se
realiza a través de una Red de paso de mensajes y donde cada unidad de procesamiento
opera sobre su propia memoria.

El mecanismo de paso de mensajes (MPI) es el que permite a los procesos de un


Multicomputador correr en forma integrada, coherente y sincronizada a fin de que la
ejecución de cada uno resulte una cooperación con la ejecución global de una aplicación
que, en pos de obtener un mejor rendimiento, haya sido distribuida entre las distintas
unidades.

En el apartado 2 vamos a comentar este mecanismo. Primeramente se explica el


concepto MPI, los objetivos que persiguen sus desarrolladores y se presentan
alternativas a este entorno de desarrollo de aplicaciones paralelas. Seguidamente se
realiza un estudio sobre el funcionamiento de las comunicaciones punto a punto con
MPI, es decir, entre dos procesos, indicando los distintos modos de comunicación que
existen, los tipos de datos utilizados y por último una breve explicación del uso de las
etiquetas para recepción de mensajes y de los comunicadores (entornos de
comunicación) y su uso. Por último se presenta un ejemplo en el que se utiliza la librería
MPI.

En el apartado 3 se presentan las librerías paralelas incluyendo las ventajas que


presentan, las dificultades que nos encontramos a la hora de desarrollar librerías
paralelas con MPI, un resumen de las herramientas de abstracción utilizadas por MPI
para evitar dichas deficiencias y por último se presentan las características de MPI que
soportan las librerías paralelas.

En el apartado 4 se presenta un ejemplo de una librería muy sencilla llamada


Ibcast donde se puede observar el uso del contexto duplicado, que protege a la librería
del usuario y de otras librerías, y una estrategia de etiquetas incrementales que protegen
una llamada a la librería de otras llamadas.

Procesamiento Vectorial y Paralelo Página 6 de 49


MPI y Librerías Paralelas Grupo 15

En el apartado 5 se exponen otras librerías paralelas como son BLACS, PBLAS


y SCALAPACK utilizadas en el campo del álgebra lineal. Dichas librerías utilizan
como base paquetes software de paso de mensajes como pueden ser MPI, PVM, etc...

Por último en el apartado 6 se presentan las conclusiones del estudio realizado.

En el anexo del documento encontraremos la implementación de la librería


Ibcast presentada con anterioridad así como un programa de prueba que ejecuta dicha
librería.

Procesamiento Vectorial y Paralelo Página 7 de 49


MPI y Librerías Paralelas Grupo 15

2 Message Passing Interface (MPI)

2.1 Introducción a MPI


MPI (Message Passing Interface) es una interfaz estandarizada para la
realización de aplicaciones paralelas, basada en paso de mensajes. El modelo de
programación que subyace tras MPI es MIMD (múltiple instrucción, múltiples datos).
Se dan especiales facilidades para la utilización del modelo SPMD, (programa único,
múltiples datos) un caso particular de MIMD, en el que todos los procesos ejecutan el
mismo programa, aunque no necesariamente la misma instrucción al mismo tiempo.

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.

Los objetivos fundamentales de los desarrolladores de MPI son los siguientes:

1. Definir un entorno de programación único que garantice la portabilidad de las


aplicaciones paralelas.

2. Definir totalmente el interfaz de programación, sin especificar cómo debe ser la


implementación del mismo

3. Ofrecer implementaciones de calidad, de dominio público, para favorecer la


extensión del estándar.

4. Convencer a los fabricantes de computadores paralelos para que ofrezcan


versiones de MPI optimizadas para sus máquinas.

MPI no es, evidentemente, el único entorno disponible para la elaboración de


aplicaciones paralelas. Existen muchas alternativas, entre las que destacamos las
siguientes:

• Utilizar las bibliotecas de programación propias del computador paralelo


disponible: NX en el Intel Paragon, MPL en el IBM SP2, etc.

• PVM (Parallel Virtual Machine): de características similares a MPI, se


desarrolló con la idea de hacer que una red de estaciones de trabajo funcionase
como un multicomputador. Funciona también en multicomputadores,
normalmente como una capa de software encima del mecanismo de
comunicaciones nativo.

• Usar, si es posible, lenguajes de programación paralelos (FORTRAN 90) o


secuenciales (C, FORTRAN 77) con directivas de paralelismo.

Procesamiento Vectorial y Paralelo Página 8 de 49


MPI y Librerías Paralelas Grupo 15

• Usar lenguajes secuenciales junto con compiladores que paralelicen


automáticamente.

Como ya hemos comentado, MPI está especialmente diseñado para desarrollar


aplicaciones SPMD. Al arrancar una aplicación se lanzan en paralelo N copias del
mismo programa (usando procesos). Estos procesos no avanzan sincronizados
instrucción a instrucción sino que la sincronización, cuando sea necesaria, tiene que ser
explícita. Los procesos tienen un espacio de memoria completamente separado.

El intercambio de información, así como la sincronización, se hacen mediante


paso de mensajes. Se dispone de funciones de comunicación punto a punto (que
involucran sólo a dos procesos), y de funciones u operaciones colectivas (que
involucran a múltiples procesos). Los procesos pueden agruparse y formar
comunicadores, lo que permite una definición del ámbito de las operaciones colectivas,
así como un diseño modular.

2.2 Comunicación Punto a Punto


Existen múltiples formas distintas de intercambiar un mensaje entre dos
procesos (punto a punto), en función del modelo y el modo de comunicación elegido.

2.2.1 Modelos y modos de comunicación

MPI define dos modelos de comunicación: bloqueante (blocking) y no


bloqueante (nonblocking). El modelo de comunicación tiene que ver con el tiempo que
un proceso pasa bloqueado tras llamar a una función de comunicación, sea ésta de envío
o de recepción. Una función bloqueante mantiene a un proceso bloqueado hasta que la
operación solicitada finalice. Una no bloqueante supone simplemente “encargar” al
sistema la realización de una operación, recuperando el control inmediatamente. El
proceso únicamente va a preocuparse, más adelante, en averiguar si la operación ha
finalizado o no.

Una cuestión importante que no queda clara utilizando distintos modelos de


comunicación es, ¿cuándo damos una operación por finalizada? En el caso de la
recepción, la operación habrá finalizado cuando tengamos un mensaje nuevo completo,
en el buffer asignado al efecto. En el caso de emisión, MPI define un envío como
finalizado cuando el emisor puede reutilizar, sin problemas de causar interferencias, el
buffer de emisión que tenía el mensaje. Dicho esto, podemos entender que tras hacer un
send (envío) bloqueante podemos reutilizar el buffer asociado sin problemas, pero tras
hacer un send no bloqueante tenemos que ser muy cuidadosos con las manipulaciones
que se realizan sobre el buffer, bajo el riesgo de alterar inadvertidamente la información
que se está enviando.

Al margen de si la función invocada es bloqueante o no, el programador puede


tener un cierto control sobre la forma en la que se realiza y completa un envío. MPI
define, en relación a este aspecto, 4 modos de envío:

Procesamiento Vectorial y Paralelo Página 9 de 49


MPI y Librerías Paralelas Grupo 15

• básico (basic)
• con buffer (buffered)
• síncrono (synchronous)
• listo (ready).

Cuando se hace un envío con buffer se guarda inmediatamente, en un buffer al


efecto en el emisor, una copia del mensaje. La operación se da por completa en cuanto
se ha efectuado esta copia. Si no hay espacio en el buffer, el envío fracasa.

Si se hace un envío síncrono, la operación se da por terminada sólo cuando el


mensaje ha sido recibido en destino. Este es el modo de comunicación habitual en los
sistemas basados en Transputers. En función de la implementación elegida, puede exigir
menos copias de la información conforme ésta circula del buffer del emisor al buffer del
receptor.

El modo de envío básico no especifica la forma en la que se completa la


operación: es algo dependiente de la implementación. Normalmente equivale a un envío
con buffer para mensajes cortos y a un envío síncrono para mensajes largos. Se intenta
así agilizar el envío de mensajes cortos a la vez que se procura no perder demasiado
tiempo realizando copias de la información.

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

El resultado de la combinación de dos modelos y cuatro modos de comunicación


nos da 8 diferentes funciones de envío. Funciones de recepción sólo hay dos, una por
modelo. Presentamos a continuación los prototipos de las funciones más habituales.
Empezamos con MPI_Send() y MPI_Recv que son, respectivamente, las funciones de
envío y recepción básicas bloqueantes.

int MPI_Send(void* buf, int count, MPI_Datatype datatype, int dest,


int tag, MPI_Comm comm);

int MPI_Recv(void* buf, int count, MPI_Datatype datatype, int source,


int tag, MPI_Comm comm, MPI_Status *status);
Figura 1: Funciones MPI_Send y MPI_Recv

El significado de los parámetros es como sigue. Buf, count y datatype forman el


mensaje a enviar o recibir: count es el tamaño de un dato del tipo datatype, que se
encuentra en memoria a partir de la dirección indicada por buf.

Dest es, en las funciones de envío, el identificador del proceso destinatario del
mensaje.

Procesamiento Vectorial y Paralelo Página 10 de 49


MPI y Librerías Paralelas Grupo 15

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.

Tag es una etiqueta que se puede poner al mensaje. El significado de la etiqueta


lo asigna el programador. Suele emplearse para distinguir entre diferentes clases de
mensajes. El emisor pone siempre una etiqueta a los mensajes, y el receptor puede elegir
entre recibir sólo los mensajes que tengan una etiqueta dada, o aceptar cualquier
etiqueta, poniendo MPI_ANY_TAG como valor de este parámetro.

Comm es un comunicador, el cual muchas ocasiones se emplea el comunicador


universal MPI_COMM_WORLD, capaz de comunicar cualquier nodo.

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.

La definición de la estructura MPI_Status es la siguiente:

typedef struct {
int MPI_SOURCE;
int MPI_TAG;
/* otros campos no accesibles */
} MPI_Status;
Figura 2: Definición de la estructura MPI_Status

Podemos acceder directamente a los campos MPI_SOURCE y MPI_TAG, pero a


ningún otro más. Si queremos saber el tamaño de un mensaje, lo haremos con la función
MPI_Get_count().

MPI_SEND envio bloqueante M P _I S E N D e n vio b lo q u e a n te

tamaño > umbral ta m a ñ o≤ u m b ra l

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

Figura 3: Estructura MPI_Send y MPI_Recv

Por otro lado tenemos las funciones de emisión y recepción básicas no


bloqueantes, las cuales son: MPI_Isend() y MPI_Irecv()

int MPI_Isend(void* buf, int count, MPI_Datatype datatype, int dest,


int tag, MPI_Comm comm, MPI_Request *request);

Procesamiento Vectorial y Paralelo Página 11 de 49


MPI y Librerías Paralelas Grupo 15

int MPI_Irecv(void* buf, int count, MPI_Datatype datatype, int source,


int tag, MPI_Comm comm, MPI_Request *request);

int MPI_Wait(MPI_Request *request, MPI_Status *status);

int MPI_Test(MPI_Request *request, int *flag, MPI_Status *status);

int MPI_Cancel(MPI_Request *request);


Figura 4: Funciones de emisión y recepción no bloqueantes

Las funciones no bloqueantes manejan un objeto request del tipo MPI_Request.


Este objeto es una especie de “recibo” de la operación solicitada. Más adelante se podrá
utilizar este objeto para saber si la operación ha finalizado o no.

La función MPI_Wait() toma como entrada un objeto request, y bloquea al


proceso hasta que la operación correspondiente termina. Por lo tanto, hacer un
MPI_Isend() seguido de un MPI_Wait() equivale a hacer un envío bloqueante. Sin
embargo, entre la llamada a la función de envío y la llamada a la función de espera el
proceso puede haber estado haciendo cosas útiles, es decir, consigue solapar parte de
cálculo de la aplicación con la comunicación.

Cuando no interesa bloquearse, sino simplemente saber si la operación ha


terminado o no, podemos usar MPI_Test(). Esta función actualiza un flag que se le pasa
como segundo parámetro. Si la función ha terminado, este flag tendrá valor 1, y si no ha
finalizado aun, toma el valor 0.

Por último, MPI_Cancel() nos sirve para cancelar una operación de


comunicación pendiente, siempre que ésta aún no se haya completado.

2.2.3Comunicación con buffer

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 evitar el riesgo de un bloqueo no deseado se puede solicitar explícitamente


que la comunicación se complete copiando el mensaje en un buffer, que tendrá que
asignar al efecto el propio proceso. Así, se elimina el riesgo de bloqueo mientras se
espera a que el interlocutor esté preparado. La función correspondiente es MPI_Bsend(),
que tiene los mismos argumentos que MPI_Send().

int MPI_Bsend(void* buf, int count, MPI_Datatype datatype, int dest,


int tag, MPI_Comm comm);
Figura 5: Cabecera de la función MPI_Bsend

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

Procesamiento Vectorial y Paralelo Página 12 de 49


MPI y Librerías Paralelas Grupo 15

emplear como buffer. Esta última operación la hace MPI_Buffer_attach(). Dicho buffer
se puede recuperar usando MPI_Buffer_detach().

int MPI_Buffer_attach(void* buffer, int size);

int MPI_Buffer_detach(void* buffer, int* size);


Figura 6: Cabeceras de las funciones MPI_Buffer_attach y 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.

2.2.4Recepción por encuesta

Las funciones de recepción de mensajes engloban, en una operación, la


sincronización con el emisor (esperar a que haya un mensaje disponible) y la de
comunicación (copiar ese mensaje). A veces, sin embargo, conviene separar ambos
conceptos.

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.

Las funciones MPI_Probe() y MPI_Iprobe() nos permiten saber si tenemos un


mensaje recibido y listo para leer, pero sin leerlo. A partir de la información de estado
obtenida con cualquiera de estas funciones, podemos averiguar la identidad del emisor
del mensaje, la etiqueta del mismo y su longitud. Una vez hecho esto, podemos
proceder a la lectura real del mensaje con la correspondiente llamada a MPI_Recv() o
MPI_Irecv(). La cabecera de estas funciones es:

int MPI_Iprobe(int source, int tag, MPI_Comm comm, int *flag,


MPI_Status *status);

int MPI_Probe(int source, int tag, MPI_Comm comm, MPI_Status *status);


Figura 7: Cabeceras de las funciones MPI_Iprobe y MPI_Probe

MPI_Probe() es bloqueante, por lo que sólo devuelve el control al proceso


cuando hay un mensaje listo. MPI_Iprobe() es no bloqueante, nos indica en el
argumento flag si hay un mensaje listo o no.

Procesamiento Vectorial y Paralelo Página 13 de 49


MPI y Librerías Paralelas Grupo 15

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.

Tipos MPI Tipos C


MPI_CHAR signed char
MPI_SHORT signed short int
MPI_INT signed int
MPI_LONG signed long int
MPI_UNSIGNED_CHAR unsigned char
MPI_UNSIGNED_SHORT unsigned short int
MPI_UNSIGNED unsigned int
MPI_UNSIGNED_LONG unsigned long int
MPI_FLOAT float
MPI_DOUBLE double
MPI_LONG_DOUBLE long double
MPI_BYTE Sin equivalente
Figura 8: Relación de tipos de MPI y C/C++

Aunque una aplicación desarrollada en C trabaja con los tipos de datos


habituales, cuando se realice un paso de mensajes habrá que facilitar a MPI un
descriptor del tipo equivalente, tal como lo define MPI.

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).

Tras la recepción, se puede saber la etiqueta concreta accediendo a


status.MPI_TAG. Las etiquetas permiten diferenciar las diferentes clases de
información que pueden intercambiarse los procesos.

La comunicación se produce dentro de un comunicador (entorno de


comunicación), que es básicamente un conjunto de procesos. El comunicador universal

Procesamiento Vectorial y Paralelo Página 14 de 49


MPI y Librerías Paralelas Grupo 15

MPI_COMM_WORLD está siempre definido, e incluye a todos los procesos que forman parte
de la aplicación.

Las funciones de creación, eliminación e informacion de comunicadores son las


siguientes:

MPI_Comm_create(MPI_Comm Old_Comm, MPI_Group Group_Proc,


MPI_Comm *New_Comm);

MPI_Comm_dup(MPI_Comm Comm, MPI_Comm *New_Comm);

MPI_Comm_free(MPI_Comm *Comm);

int MPI_Comm_size(MPI_Comm comm, int *size);

int MPI_Comm_rank(MPI_Comm comm, int *rank);


Figura 9: Cabeceras de funciones de creación y eliminación de comunicadores

MPI_Comm_size() es una función que nos ayuda a averiguar el tamaño (número


de procesos) de un comunicador, y MPI_Comm_rank() para que un proceso obtenga su
identificación dentro del comunicador.

La función MPI_Comm_dup() permite crear un nuevo comunicador, con el


mismo grupo de procesos, pero diferente con texto. Se puede usar antes de llamar a una
función de una biblioteca.

Los comunicadores permiten que diferentes grupos de procesos actúen sin


interferir, así como dividir la aplicación en fases no solapadas. MPI garantiza la entrega
ordenada de mensajes dentro de un mismo comunicador.

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.

Un ejemplo de uso de comunicadores podría ser el siguiente:

#include "mpi.h";

MPI_Comm comm_world, comm_worke;


MPI_Group group_world, group_worker;
int ierr;

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

Procesamiento Vectorial y Paralelo Página 15 de 49


MPI y Librerías Paralelas Grupo 15

2.3 Comunicaciones Colectivas


Muchas aplicaciones requieren de la comunicación entre más de dos procesos.
Esto se puede hacer combinando operaciones punto a punto, pero para que le resulte
más cómodo al programador, y para posibilitar implementaciones optimizadas, MPI
incluye una serie de operaciones colectivas:

• 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.

2.4 Ejemplo MPI


Proponemos realizar, como ejemplo, un programa que calcula el producto de dos
matrices de un tamaño específico y muy grande (por ejemplo, producto de 60x4000 *
4000x600), utilizando la librería MPI.

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.

Como se puede apreciar en el código, se ha utilizado una comunicación de envío y


recepción bloqueantes. Para ver el tiempo de duración del programa, se ha utilizado la
función MPI_Wtime.

#include "mpi.h"
#include <stdio.h>
#include <stdlib.h>
#include "aux.c" // Funciones auxiliares

double inicial,final,tiempo;

int main (int argc, char **argv){


int rango;
int num;
int maq;
int fin=0;

Procesamiento Vectorial y Paralelo Página 16 de 49


MPI y Librerías Paralelas Grupo 15

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();

/* CREAMOS LOS TRABAJOS */


MPI_Comm_size(MPI_COMM_WORLD,&num);
/* En maq tenemos el numero de maquinas que trabajan */
maq=num-1;

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++){

Procesamiento Vectorial y Paralelo Página 17 de 49


MPI y Librerías Paralelas Grupo 15

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

Procesamiento Vectorial y Paralelo Página 18 de 49


MPI y Librerías Paralelas Grupo 15

3 Librerías Paralelas
3.1 Motivación
Las librerías software ofrecen algunas ventajas:

• Garantizan consistencia en los programas.


• Ayudan a garantizar una implementación de alta calidad.
• Abstraen los detalles y la complejidad asociada a la implementación, y minimizan el
esfuerzo y el desorden de los resultados.

Incluso en librerías secuenciales, los programadores de librerías incorporan


numerosa heurística y trucos a la hora de especificar el dominio. Con la inclusión del
paralelismo basado en paso de mensajes los “detalles de ejecución” de los métodos
numéricos más típicos crecen significativamente, esto motiva enormemente el
desarrollo de librerías paralelas que encapsulen y oculten esta complejidad al usuario.
MPI es el sistema de librerías subyacente que ayudará a los científicos a construir
aplicaciones basadas en librerías paralelas fiables y fáciles de usar.

3.2 Deficiencias de MPI


En los entornos secuenciales de C y Fortran, se pueden crear fácilmente librerías,
ya que el modelo de programación orientado a proyectos ha ayudado a definir
correctamente las condiciones sobre lo que es un programa correcto y lo que no lo es.
Sin embargo, en la memoria distribuida que incluye el desarrollo del paso de mensajes,
las librerías son difíciles de escribir con cualquier software suministrado o sistema
portable. Uno de los problemas es la modularidad: una librería necesita un espacio de
comunicaciones aislado del espacio de comunicaciones del usuario, precisamente por
que es una librería. Su modelo de comunicaciones esta diseñado independientemente
del código de usuario aunque luego pueda ser conectado al mismo.

El problema más evidente que se puede tener es si se envía un mensaje desde la


librería y accidentalmente es recibido por el código de usuario o viceversa. Unas de las
posibles soluciones es el uso de etiquetas eficientemente para crear restricciones en el
envío de mensajes. Sin embargo estas etiquetas se muestran insuficientes por algunas
razones. La primera es que más de una librería (o invocaciones de la misma librería)
podría usar las mismas etiquetas. La segunda es que el uso indiscriminado de un mismo
modo de nombrado para las etiquetas (por ejemplo MPI_CUALQUIER_ETIQUETA)
omite el posible uso de estas como protección que de otra manera podrían aportar.

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.

Procesamiento Vectorial y Paralelo Página 19 de 49


MPI y Librerías Paralelas Grupo 15

Dentro de MPI están incorporados varios tipos de abstracción:

 los grupos de procesos, que describen a los procesos participantes en


operaciones colectivas,

 los rangos en el nombrado, para las comunicaciones punto a punto (contexto en


el que se separan mensajes que no están relacionados),

 topologías, que permiten a los usuarios describir las relaciones entre los
procesos que ellos deseen,

 el encapsulado de información en objetos útiles que permiten abstraer todos los


detalles al usuario final.

3.3 Revisión de las características que soportan las


librerías.
Los requisitos para realizar librerías eficientes se pueden resumir en los siguientes
puntos:

• Un espacio de comunicación segura garantiza que una librería pueda mandar y


recibir mensajes en una conexión punto a punto sin interferencias de otros
mensajes de otra conexión punto a punto realizada en el mismo sistema.

• Las operaciones colectivas tienen como participantes a un grupo de procesos


(desde un comunicador); los procesos que no participan continúan sin dificultad.

• Los nombres abstractos de los procesos están basados en topologías virtuales, o


al menos basados en la categoría del grupo de procesos, de tal modo se evitan
dependencias de hardware, e idealmente hacen que el código de las aplicaciones
sea más intuitivo.

MPI ofrece muchas características que implementan estos importantes requisitos.

Los grupos de procesos definen un nombrado por categoría relativo al grupo en


comunicaciones puntos a punto. Además, esos grupos definen el alcance de las
operaciones colectivas. (Hay que señalar que la representación interna de los nombres
de los procesos no revela el nivel de aplicación; ya que esto estropearía la portabilidad).
Este alcance hace posible afirmar la no interferencia de operaciones colectivas
secuenciales en el mismo contexto de comunicación.

Los contextos proporcionan la capacidad de tener “universos” independientes y


seguros para el paso de mensajes en MPI. Un contexto está conceptualmente
implementado por una etiqueta secundaria que diferencia un mensaje del resto. A
diferencia de las etiquetas manejadas por el usuario, esos contextos proporcionan
espacios de comunicación totalmente independientes, cada uno de ellos complementado
totalmente con etiquetas manejadas por el usuario.

Procesamiento Vectorial y Paralelo Página 20 de 49


MPI y Librerías Paralelas Grupo 15

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.

Los comunicadores encapsulan contextos, grupos y topologías virtuales en un


objeto que proporciona alcance para todas las operaciones de comunicación en MPI.
Los comunicadores asocian grupos de procesos e información de contextos para formar
espacios de comunicación segura dentro del grupo. Los comunicadores proporcionan
además un objeto que puede usarse para mantener copias locales de los datos que
necesita la librería mantener de una invocación a otra. El término MPI para esto es
“caché de atributos”.

El uso de contextos separados de comunicación por distintas librerías (o distintas


invocaciones de la librería) aísla la comunicación interna de esa ejecución de la librería
de comunicaciones externas. Esta aproximación permite invocar la librería incluso
aunque haya comunicaciones pendientes. Por otra parte, evita la necesidad de
sincronizar cada entrada o salida del código de la librería. En la mayoría de los casos no
es necesaria la sincronización, se reduce su realización, y se puede evitar por el modelo
del contexto.

Procesamiento Vectorial y Paralelo Página 21 de 49


MPI y Librerías Paralelas Grupo 15

4 Una Primera Librería MPI


En este apartado describiremos una librería muy sencilla, con solo dos
funciones, llamada Ibcast. Esta librería proporcionará una característica que no se
incluye en la definición de MPI: la difusión no bloqueante. Primeramente recordar
que, para todos los procesos excepto el especificado como raíz, MPI_Bcast es una
forma de recepción. Por lo tanto, se permite el envio de mensajes de tipo receive y, a
continuacion, la ejecucion de otras funciones, sin espera de recepción. Unicamente se
realizará una espera activa si los datos necesarios provienen de los mensajes receive.
Esta analogía con receive no es completamente precisa, ya que los procesos intermedios
del árbol de difusión tienen que enviar también un receive. Sin embargo, la difusión no
bloqueante puede ser aún útil, particularmente en sistemas multihilo. En el siguiente
fragmento de programa podemos observar el uso de esta librería:

#include “ibcast.h”
Ibcast_handle *request;

Ibcast(buf, count, datatype, root, comm, &request);

Ibcast_wait(&request);

Figura 12: Cabeceras de funciones Ibcast e Ibcast_wait

Una de las características de MPI, que aún no ha sido introducida, es la


posibilidad de adjuntar datos de usuario a los comunicadores COMM. A esta característica
se la denomina recogida de atributos (caching of attributes). Es usada, normalmente,
para mantener un puntero a una estructura de datos, usada por la librería, que almacena
información obtenida en las llamadas (en Fortran es un entero). Los atributos se
identifican por claves (key values), las cuales son asignadas por el sistema de tal modo
que, varias librerías pueden agregar atributos al mismo comunicador sin tener
conocimiento la una de la otra.

En el caso de la librería Ibcast, la información, recogida en las llamadas, incluye


un comunicador que se usará para las comunicaciones internas de Ibcast, y una etiqueta
de ordenación, que será usada para separar multiples instancias de Ibcast usadas en el
mismo comunicador.

#include “mpi.h”
/* manejo de las operaciones ibcast en un comunicador */
typedef struct ibcast_syshandle
{
MPI_Comm comm;
int ordering_tag;
} Ibcast_syshandle;

/* manejo de una operación ibcast en particular */


typedef struct ibcast_handle
{
MPI_Request *req_array;

Procesamiento Vectorial y Paralelo Página 22 de 49


MPI y Librerías Paralelas Grupo 15

MPI_Status *stat_array;
int num_sends;
int num_recvs;
} Ibcast_handle;
Figura 13: Contenido del fichero ibcast.h

La librería completa para difusión no bloqueante es la proporcionada en los


ficheros ‘ibcast.h’ y ‘ibcast.c’. En ellos se explica como usar los comunicadores y la
recepción. Al usar la llamada MPI_Comm_dup, la forma en que los atributos son
agregados a los comunicadores permite su propagación selectiva. La llamada
MPI_Comm_create, anteriormente mencionada, nunca copia los atributos al
comunicador recién creado. La librería Ibcast, necesita guardar la información de forma
persistente, por tanto, requiere del uso de un comunicador con almacenamiento, para
proteger al usuario de las acciones de comunicación de la librería. Es, por tanto,
necesario el uso de etiquetas, así como otros mecanismos, para la seguridad en una
librería ordenada de forma indeterminada.

Inicialmente Ibcast realiza un chequeo, para ver cuando ha sido llamado


anteriormente por este proceso, viendo cuando tiene un valor correcto en la variable
global ibcast_keyval (que es un entero). Si no tiene un valor correcto, Ibcast tiene que
tomar un valor clave (key value). De esta forma, se puede almacenar su información en
este atributo, al igual que todos los futuros comunicadores que puede llamar Ibcast en
este proceso. Todo este proceso es una funcion, llamada MPI_Keyval_create. En caso
de tener un valor correcto en la variable global, ibcast_keyval es ya un valor clave
para usar por esta librería.

Seguidamente, se realiza un chequeo para ver si se ha producido una difusion no


bloqueante (Ibcast) en el comunicador actual. En caso afirmativo, se incrementa el valor
del atributo clave ibcast_keyval. Si no, se crea el manejador del sistema, asociado con
este valor clave, y se agrega al comunicador. La llamada más importante aquí es
MPI_Comm_dup, que hace un duplicado completo del comunicador de entrada, pero con
un nuevo contexto de comunicación. Seran copiados aquellos atributos que el usuario
haya indicado, cuando estos fueron añadidos al comunicador.

A continuacion, Ibcast determina los datos específicos que necesitan ir en el


manejador de usuario (para esta llamada en particular), para que el trabajo de
transmisión de datos pueda comenzar. De esta forma, el usuario puede llamar
correctamente a Ibcast_wait.

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

Procesamiento Vectorial y Paralelo Página 23 de 49


MPI y Librerías Paralelas Grupo 15

comunicaciones colectivas y punto a punto de su comunicador, ya que usa un


comunicador duplicado.
Además, cada llamada a Ibcast con un comunicador específico es aislada de las
anteriores llamadas, porque se usan distintas etiquetas para los envíos y recepciones.

Con esta librería se presentan tres clases de comportamientos en los procesos:

 la raíz, el cual solo envía


 las hojas, las cuales solo reciben
 el resto de los nodos del árbol de difusión, los cuales enván y reciben

Dependiendo de la posición en el árbol de cada proceso, se determina el número


de envíos continuos y de solicitudes de recepción que Ibcast crea/almacena en el
manejador de usuario. (Las llamadas MPI_Send_init y MPI_Recv_init generan
operaciones continuas, pero no inician nada. No son usadas para permitir la reusabilidad
de las MPI_Requests. Más bien, estas funciones nos proporcionan una forma fácil de
almacenar los parámetros que, más adelante, son usados por procesos intermedios del
árbol de difusión.) Ibcast llama, entonces, a Ibcast_work para ejecutar la función
principal de la transmisión de difusión.

En la función Ibcast_work, el nodo raíz, que no tiene recepciones, inicia


inmediatamente todos sus envíos, usando la llamada MPI_Startall. Los otros nodos
del árbol de difusión comienzan sus recepciones, por medio de la funcion
MPI_Startall. Después de que estas llamadas sean iniciadas, Ibcast_work termina
devolviendo el control a Ibcast.

Finalmente, Ibcast incrementa la etiqueta de ordenación, puesto que va a ser


usada por la siguiente llamada a Ibcast, la cual podría ir antes que la llamada a
Ibcast_wait. Esta Se retornara con el manejador que contiene la información
persistente, que será proporcionada por el usuario cuando llame a Ibcast_wait
(cuando tenga que finalizar la difusión).

Ibcast_wait implementa los aspectos no cubiertos por Ibcast_work:

 Para la raíz, se espera en los envíos.

 Para las hojas, se espera en las recepciones

 Para los otros nodos del árbol de difusión, se espera en la recepción, y a


continuacion comienza su envío y espera que se complete. Las llamadas
MPI_Startall y MPI_Waitall forman parte de este proceso.

Los comunicadores son habitualmente creados, copiando comunicadores ya


existentes (con MPI_Comm_dup) y son finalmente liberados. Llegado a este punto, se
puede querer limpiar el almacenamiento asociado a un atributo. Para manejar los
atributos asociados durante estas operaciones, MPI especifica que, cuando un valor
clave es creado, el usuario puede proporcionar funciones para copiar y borrar los
valores de los atributos cuando el comunicador es copiado o liberado.

Procesamiento Vectorial y Paralelo Página 24 de 49


MPI y Librerías Paralelas Grupo 15

int MPI_Comm_dup(MPI_Comm comm, MPI_Comm *newcomm)


int MPI_Keyval_create(MPI_Copy_function *copy_fn, MPI_Delete_function
*delete_fn, int *keyval, void *extra_state)
int MPI_Attr_put(MPI_Comm comm, int keyval, void* atrtribute_val)
int MPI_Attr_get(MPI_Comm comm, int keyval, void **attribute_val,
int *flag)
int MPI_Keyval_free(int *keyval)
int MPI_Attr_delete(MPI_Comm comm, int keyval)
int Copy_fn(MPI_Comm *oldcomm, int *keyval, void *extra_state, void
*attribute_value_in, void **attribute_value_out, int *flag)
int Delete_fn(MPI_Comm *comm, int *keyval, void *attribute_value, void
*extra_state
Figura 14: Llamadas en C MPI que necesita Ibcast, y llamadas relacionadas

MPI_COMM_DUP(comm, newcomm, ierror)


integer comm., newcomm, ierror

MPI_KEYVAL_CREATE(copy_fn, delete_fn, keyval, extra_state, ierror)


external copy_fn, delete_fn
integer keyval, extra_state, ierror

MPI_ATTR_PUT(comm, keyval, attribute_val, ierror)


integer comm, keyval, attribute_val, ierror
logical flag

MPI_ATTR_GET(comm, keyval, attribute_val, ierror)


integer comm, keyval, attribute_val, ierror
logical flag

MPI_KEYVAL_FREE(keyval, ierror)
integer keyval, ierror

MPI_ATTR_DELETE(comm, keyval, ierror)


integer comm., keyval, ierror

FUNCTION COPY_FN(oldcomm, keyval, extra_state, attribute_value_in,


attribute_value_out, flag)
integer oldcomm, keyval, extra_state, attribute_value_in,
attribute_value_out, flag

FUNCTION DELETE_FN(comm, keyval, attribute_value, extra_state)


integer comm, keyval, attribute_value, extra_state
logical flag
Figura 15: Llamadas en Fortran MPI que necesita Ibcast, y llamadas relacionadas

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.

/* ver primero si esta librería ha sido llamada alguna vez. */


if (ibcast_keyval == MPI_KEYVAL_INVALID) {
/* nuestra primera mission es crear el valor clave del proceso
local para esta librería, especificando las llamadas externas */
MPI_Keyval_create(Ibcast_copy, Ibcast_delete, &ibcast_keyval, NULL);

Figura 16: Fragmento de codigo del fichero ibcast.c

Procesamiento Vectorial y Paralelo Página 25 de 49


MPI y Librerías Paralelas Grupo 15

También definimos las funciones que hacen la copia (Ibcast_copy) y el borrado


(Ibcast_delete) para completar las modificaciones. Estos cambios otorgarán a la
librería la propiedad de que las llamadas a MPI_Comm_dup y MPI_comm_free, en un
comunicador que ha usado Ibcast, se comporten de forma tan responsable como sea
posible en la liberación de memoria.

#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.

Procesamiento Vectorial y Paralelo Página 26 de 49


MPI y Librerías Paralelas Grupo 15

5 Otras Librerías Paralelas


5.1 BLACS
5.1.1 Introducción

BLACS, cuyas siglas significan “Basic Linear Algebra Communication


Subprograms”, es una librería cuya mayor utilidad es que sus programadores, al
identificar las tareas clave de comunicaciones en problemas de álgebra lineal, han
conseguido centrar los esfuerzos de optimización. BLAS hace uso de los paquetes
software de paso de mensajes.

Figura 18: Estructura y jerarquía de librerias

La principal motivación con la que se encontraron los desarrolladores fue que en


el mercado aunque ya existían varios paquetes que proporcionaban una interfaz de paso
de mensajes que permanecían invariables a través de varias plataformas (ej. PVM, MPI,
PICL), estos paquetes eran librerías generales y sus interfaces no eran tan fáciles de usar
en álgebra lineal como sería deseable.

Las principales características de BLACS son:

• Facilidad de programación, simplifica el paso de mensajes y así reduce los


errores de programación.
• Facilidad de uso, está a un nivel en el que es fácilmente usable por los
programadores de álgebra lineal.
• Portabilidad, proporciona una interfaz que está soportada por un gran abanico de
computadores paralelos.
• Con BLACS la interfaz y los métodos de uso de las rutinas, han sido
especializados y por tanto simplificados.

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.

Procesamiento Vectorial y Paralelo Página 27 de 49


MPI y Librerías Paralelas Grupo 15

Figura 19: Representación de procesadores trabajando con BLACS

Estos procesos son agrupados a través de contextos, de la misma manera que lo


hace MPI (comunicadores y de manera compatible con éstos). Un contexto permite
realizar varios tipos de agrupamientos pudiendo crearse grupos de procesos arbitrarios,
crear GRIDS solapados o disjuntos o aislar los procesos de los GRIDS para que no
interfieran con los otros procesos GRIDS. Por otro lado, este agrupamiento de los
procesos permite que las operaciones que realizan conjuntamente los procesos puedan
ser definidas en varios ámbitos como por ejemplo por filas (todos los procesos de la fila
participan en la operación), columnas (todos los procesos de la columna participan en la
operación) o todos los procesos juntos participando en una misma operación.

Los mensajes carecen de tags o etiquetas, lo que facilita la programación. No


obstante si que quieren evitar conflictos con otras librerías paralelas se puede, de
manera opcional, establecer un rango disponible de identificadores. Hay que indicar que
este recurso es innecesario si la capa subyacente de comunicadores es MPI.

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)

A continuación se muestran algunas de las funciones más importantes a la hora


de crear un programa con BLACS.

5.1.2.1 Rutinas de la comunicación punto a punto

1. CvGESD2D( ICONTXT, M, N, A, LDA, RDEST, CDEST )

Envía un mensaje dentro de la matriz indicada al proceso que se encuentra en las


coordenadas {RDEST, CDEST}.

Procesamiento Vectorial y Paralelo Página 28 de 49


MPI y Librerías Paralelas Grupo 15

Los parámetros son:

• ICONTXT, indica el contexto.


• UPLO, DIAG, M, N, A, LDA, parámetros que describen la matriz.
• RDEST, columna del proceso destino.
• CDEST, fila del proceso destino.

2. CvGERV2D( ICONTXT, M, N, A, LDA, RSRC, CSRC )

Recepción de un mensaje desde el proceso {RSRC, CSRC} de la matriz indicada.

Los parámetros son:

• ICONTXT, indica el contexto..


• UPLO, DIAG, M, N, LDA , paramentos que describen la matriz.
• RSRC, fila donde se encuentra el proceso que envía el mensaje.
• CSRC, columna donde se encuentra el proceso que envía el mensaje.

5.1.2.2 Rutinas de difusión

1. CvGEBS2D( ICONTXT, SCOPE, TOP, M, N, A, LDA )

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.

Los parámetros son:

• ICONTXT, indica el contexto..


• SCOPE, carácter que indica si el broadcast es para una fila, columna o para toda
la matriz.
• TOP, indica el diseño de la comunicación.
• UPLO, DIAG, M, N, A, LDA, parámetros que describen la matriz.

2. CvGEBR2D( ICONTXT, SCOPE, TOP, M, N, A, LDA, RSRC, CSRC )

Rutina que sirve para recibir un mensaje de broadcast.

Los parámetros son:

• ICONTXT, indica el contexto..


• SCOPE, carácter que indica si el broadcast es para una fila, columna o para toda
la matriz.
• TOP, indica el diseño de la comunicación.
• UPLO, DIAG, M, N, A, LDA, parámetros que describen la matriz.
• RSRC, columna del proceso que desea recibir el mensaje.
• CSRC, fila del proceso que desea recibir el mensaje.

Procesamiento Vectorial y Paralelo Página 29 de 49


MPI y Librerías Paralelas Grupo 15

5.1.2.3 Rutinas de reducción

1. CvGAMX2D( ICONTXT, SCOPE, TOP, M, N, A, LDA, RA, CA, RCFLAG,


RDEST, CDEST ) o
CvGAMN2D( ICONTXT, SCOPE, TOP, M, N, A, LDA, RA, CA, RCFLAG,
RDEST, CDEST )

Rutinas para calcular un máximo o mínimo sobre el par designado por


(Contexto, Scope), usando la topología TOP y dejando el resultado en el proceso
{RDEST,CDEST}.

2. CvGSUM2D( ICONTXT, SCOPE, TOP, M, N, A, LDA, RDEST, CDEST )

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}.

5.1.2.4 Rutinas de inicialización

1. BLACS_GET( ICONTXT, WHAT, VAL )

Devuelve información interna de BLACS (context handle, rango de Ids de


mensajes, nivel de depuración,...). Su uso típico es con ictxt a 0 y what a 0, con esta
configuración se consigue el contexto por defecto

2. BLACS_GRIDINIT(ICONTXT, ORDEN, NPROW, NPCOL)

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.

La creación de un GRID requiere como entrada todos los procesos que se


quieran definir en ella.

Los parámetros son:

• ICONTXT, indica el contexto del sistema.


• ORDEN, indica cómo trazar los procesos en la rejilla de BLACS.
• NPROW, indica el número de filas.
• NPCOL, indica el número de columnas.

5.1.2.5 Rutinas de información

1. BLACS_GRIDINFO( ICONTXT, NPROW, NPCOL, MYPROW, MYPCOL )

Devuelve información acerca de un proceso en un determinado contexto.

Procesamiento Vectorial y Paralelo Página 30 de 49


MPI y Librerías Paralelas Grupo 15

5.1.2.6 Rutinas de finalización

1. BLACS_GRIDEXIT( ICONTXT )

Libera un contexto BLACS, después de llamar a esta función el contexto deja de


existir y su manejador puede volver a ser usado para la definición de un nuevo contexto.

Los parámetros son:


• ICONTXT, indica el contexto.

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.

Ambas rutinas deben llamarse al final del programa.

5.1.3 Ejemplo
Este ejemplo representa el envío de un bloque por filas desde los procesadores
de la columna 0.

Figura 20: Representación de envío de bloques por filas, utilizando BLACS

(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] *

Procesamiento Vectorial y Paralelo Página 31 de 49


MPI y Librerías Paralelas Grupo 15

(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

Inicialmente en la línea 2-4 el programa obtiene información sobre las matrices,


entre otras cosas obtiene los valores de número de filas y columnas, este dato será muy
útil a continuación. En las líneas 7-16 inicializa unos arrays que mas adelante servirán
para el paso de información. No obstante, antes, en la línea 6 podemos ver un ejemplo
de utilización de la función de MPI “MPI_Barrier”, la cual es usada para la
sincronización de todos los procesos. En las línea 22-29 los procesos de la columna
número 0 envían mensajes de información al resto de procesos. El resto de procesos
toman esa información en las líneas que van de la 30 a la 36. Por ultimo antes de
finalizar el programa llamamos a las funciones que liberan los recursos
“Cblacs_gridexit” y “Cblacs_exit”.

5.2 PBLAS
5.2.1 Introducción

Figura 22: Estructura y jerarquía de librerias (II)

Procesamiento Vectorial y Paralelo Página 32 de 49


MPI y Librerías Paralelas Grupo 15

Los programadores de aplicaciones paralelas normalmente suelen usar


únicamente las rutinas de inicialización de BLACS. Una vez usadas se suelen utilizar
librerías más avanzadas como PBLAS o ScaLAPACK.

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.

DGEMM (TRANSA, TRANSAB, M, N, K, ALPHA, A(IA, JA), LDA, B(IB,JB), LDB,


BETA, C(IC,JC), LDC)
PDGEMM (TRANSA, TRANSB, M, N, K, ALPHA, IA, JA, DESC_A, B, IB, JB,
DESC_B, BETA, C, IC, JC, DESC_C)

Al crear PBLAS los programadores tuvieron como objetivo proporcionar un


estándar para memoria distribuida, como lo hace BLAS para memoria compartida. Este
estándar admite varios niveles de operaciones, los cuales son idénticos a los de BLAS.

• Nivel 1: operaciones vector-vector. Coste computacional de O(n).


• Nivel 2: operaciones matriz-vector. Coste computacional de O(n2).
• Nivel 3: operaciones matriz-matriz. Coste computacional de O(n3).

A la hora de definir una matriz tenemos la posibilidad de realizar diferentes


distribuciones, todas ellas distribuciones Cíclicas por Bloques 2D. En la siguiente figura
hay varios ejemplos de los elementos que podemos llegar a definir como pueden ser
matrices normales (la figura numero uno es una Matriz 5x5), Grid de procesos (la figura
número dos es un Grid de procesos 2x2) y bloques de diferentes tamaños (la figura
número tres es un bloque de tamaño 2x2).

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_.

Figura 23: Representación de la distribución de matrices por bloques

Procesamiento Vectorial y Paralelo Página 33 de 49


MPI y Librerías Paralelas Grupo 15

El modo que tiene PBLAS de indicar toda esta información es mediante


descriptores, los cuales encapsulan la información necesaria para describir una matriz
distribuida. El descriptor se puede inicializar llamando a la rutina descinit. Estos
descriptores suelen tener las siguientes características que se pueden definir:

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.

Figura 24: División global de las matrices

5.2.2Funciones

El nombre de las rutinas es el mismo que en BLAS anteponiendo la letra P.


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
rectángulo, ge o tr) e YY el tipo de comunicación. Este esquema de nombrado es el que
aparece en la siguiente tabla:

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.

Procesamiento Vectorial y Paralelo Página 34 de 49


MPI y Librerías Paralelas Grupo 15

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.

5.2.2.1 Generación de descriptores

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.

Los parámetros son:

• m, n son las Dimensiones Globales del Vector / Matriz.


• mb mb, nb nb son los Tamaños de Bloque.
• irsrc irsrc son las Coordenadas Cartesianas del que Contiene el bloque 0,0.
• ictxt ictxt es el Contexto del Grid.
• lld es la Dimensión Principal del Bloque Local.
• desc Contendrá el Descriptor del Vector/Matriz.
• info Contendrá el Resultado de la Operación.

Un ejemplo del uso de esta función es:


descinit_(desc, &m, &n, &mb,&nb, &cero, &cero,&ictxt, &ld,&inf)
Siendo int desc[9], int m = 8, int n = 10, int mb = 2, int nb = 3, int cero = 0, int inf, int ld
= 4, la representación del descriptor sería como sigue:

Figura 25: Representación del descriptor, usando PBLAS

5.2.2.2 Obtención de las coordenadas locales

1. int numroc_(int *n, int *nb, int *myPos, int *First, int *nP)

Sirve para obtener N que es el Número de Elementos Locales (Dimensión Principal


Local).

Procesamiento Vectorial y Paralelo Página 35 de 49


MPI y Librerías Paralelas Grupo 15

Los parámetros son:

• 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.

Una Cota Superior de los factores myPos y nP se define como:

• ceil(M/MB)/NPROW )*MB.
• ceil(N/NB)/NPCOL )*NB.

5.2.2.3 Escalado de un vector distribuido

1. pdscal_(int *n, double *alfa, double *x, int *ix, int *jx, int *descx, int *incx)

Escalado de un Vector Distribuido.

Los parámetros son:


• n es la Dimensión es la Dimensión GLOBAL del Vector.
• alfa es el Factor de Escalado.
• x es el puntero al vector (entrada y salida).
• ix ix, , jx jx son las Coordenadas Globales a las que corresponde la Primera Fila y
Columna de la matriz (Submatriz) a la que pueda corresponder el Vector.
• descx es el Descriptor del Vector Distribuido.
• incX es el tamaño del Vector.

5.2.2.4 Transposición de una matriz

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)

Esta rutina realiza la operación C = a·A·B + ß·C.

Los parámetros son:

• TRANSA, TRANSB permiten utilizar la traspuesta (“T” ) de A y/o B.


• M, N, K son las dimensiones de las matrices (A es MxK, B es KxN y C es MxN).
En caso de submatrices, las dimensiones de la submatriz (la global se encuentra en
el descriptor).
• A, B y C son los punteros a los bloques locales.
• DESCA, DESCB y DESCC son los descriptores de las matrices.
• ALPHA y BETA son los factores escalares.
• IA, JA, IB, JB, IC, JC contienen los índices de fila y columna
• Globales de las submatrices A, B y C.

Procesamiento Vectorial y Paralelo Página 36 de 49


MPI y Librerías Paralelas Grupo 15

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

(1) #include "mpi.h "


(2) void main(int argc, char* argv[]){
(3) int ICTXT;
(4) int nP_Row,my_P_Col,nP_Col,my_P_Row;
(5) int p,pr;
(6) nP_Row = 2; nP_Col = 1;
(7) // Inicialización de MPI
(8) MPI_Init(&argc, &argv);
(9) MPI_Comm_size(MPI_COMM_WORLD, &p);
(10) MPI_Comm_rank(MPI_COMM_WORLD, &pr pr);
(11) // Inicialización de BLACS
(12) Cblacs_get(0,0,&ICTXT); // Creación del contexto
(13) Cblacs_gridinit(&ICTXT, "R", nP_Row, nP_Col);
(14) Cblacs_gridinfo(ICTXT, &nP_Row,&nP_Col, &my_P_Row,&my_P_Col);
(15) Cblacs_barrier(ICTXT,"A");
(16) int n = 10, nb = 2;
(17) double *x;
(18) int uno = 1, cero = 0,info = 0;
(19) // Obtención del número de elementos de x de este proceso
(20) int x_elem = numroc_(&n, &nb, &my_P_Row, &cero, &nP_Row);
(21) printf("(%d ) x_elem %d\n", pr, x_elem);
(22) Cblacs_barrier(ICTXT, "A");
(23) // Creación del descriptor de x
(24) int descX[9];
(25) descinit_(descX, &n, &uno, &nb, &uno, &cero, &cero, &ICTXT,
&x_elem, &info);
(26) // Inicialización del vector x, x=(1,1,...,1)'
(27) x = (double*)malloc(sizeof(double) * (x_elem));
(28) for (int i=0; i<x_elem; i++)
(29) x[i] = 1.;
(30) Cblacs_barrier(ICTXT ,"A");
(31) // Escalado del vector x por 2
(32) double ddos = 2.0;
(33) pdscal_(&n, &ddos, x, &uno, &uno, descX, &uno);
(34) // Salida por pantalla de la solución
(35) for (i=0;i<x_elem; i++) {
(36) printf("[%dx%d] (%d): %f\n", my_P_Row, my_P_Col, i, x[i]);
(37) }
(38) Cblacs_barrier(ICTXT,"A");
(39) // Finalización de BLACS

Procesamiento Vectorial y Paralelo Página 37 de 49


MPI y Librerías Paralelas Grupo 15

(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.

Una vez finalizada la definición del descriptor, el código se limita a realizar un


simple cálculo (realiza el escalado del vector por dos) para este cálculo utiliza la rutina
pdscal_ de la cual ya hemos hablado con anterioridad.

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

Figura 27: Estructura y jerarquía de librerias (II)

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.

Procesamiento Vectorial y Paralelo Página 38 de 49


MPI y Librerías Paralelas Grupo 15

ScaLAPACK incluye un subconjunto de rutinas de LAPACK orientadas a


bloques para su uso en álgebra lineal, agregando un sistema especial de rutinas de
comunicación por paso de mensajes explícito (está escrito al estilo Single-Program-
Multiple-Data) entre los procesadores de tal forma que las rutinas LAPACK han sido
rediseñadas para computadores paralelos MIMD.

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.

Sin embargo, ScaLAPACK requiere que el usuario configure los procesadores y


distribuya los datos de la matriz antes de solucionar el problema. Estas tareas de
comunicación pueden llegar a ser extremadamente complicadas para un programador
sin experiencia. Además de esto, la eficiencia de ScaLAPACK está en gran parte sujeta
a la eficiencia de las implementaciones de BLAS y BLACS.

Si, por ejemplo, deseamos utilizar ScaLAPACK para solucionar el sistema


(A*x=b), el código central será casi idéntico; la llamada a SGESV es substituida por
una llamada a PSGESV. Pero también tendremos que introducir nuevo código, este
código es independiente del problema en si y es necesario para la configuración y
utilización de los procesadores en paralelo. Los pasos básicos para el uso de
ScaLAPACK son:

1. Configuración de los procesadores;


2. Distribuir los datos del problema;
3. Llamar a unas o más rutinas de ScaLAPACK;
4. Lanzar los procesadores.

5.3.2Funciones

Dado que la estructura de ScaLAPACK es análoga a la de LAPACK la mayoría


de los programadores de ScaLAPACK tiene una experiencia anterior con LAPACK, en
este apartado se indicarán algunas de las rutinas y grupos de rutinas más importantes de
ScaLAPACK pero no se indicará las estructura de las mismas. Se recomienda que si
está interesado en conocerlas consulte un manual de LAPACK o del propio
ScaLAPACK.

5.3.2.1 1. Rutinas conductoras

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:

Procesamiento Vectorial y Paralelo Página 39 de 49


MPI y Librerías Paralelas Grupo 15

• Estimar el número de condición de A, comprobar la singularidad y


crecimiento de pivotes.
• Refinar la solución y calcular cotas de errores forward y backward.
• Equilibrar el sistema A si está pobremente escalado.

• Problema de mínimos cuadrados, PxGELS.


• Problema simétrico de valores propios del tipo Az = λ z, A = AT, donde A es real.
Se pueden usar múltiples rutinas como PSSYEV, PDSYEV, PSSYEEVX,
PDSYEVX.
• Descomposición en valores singulares ( A =U ∑V ), PSGESVD o PDSGESVD.
T

• Problema generalizado simétrico definido de valores propios (GSEP), PSSYGVX,


PCHEGVX, PZHEGVX.

5.3.2.2 2. Rutinas computacionales

Realizan tareas computacionales dispares, tareas que no pueden ser abordadas


convenientemente con las rutinas conductoras como por ejemplo la descomposición LU
o la reducción de una matriz real simétrica a la forma tridiagonal.

5.3.2.3 3. Rutinas auxiliares

Se dividen en:

• Rutinas que realizan subtareas de algoritmos por bloques.


• Rutinas que realizan cálculos de bajo nivel usados habitualmente, como por ejemplo
el escalado de una matriz o el calculo de la norma matricial.
• Algunas extensiones de PBLAS como puede ser la transposición de matrices.

5.3.2.4 4. Rutinas no incluidas

Existen algunos problemas que LAPACK solucionaba y que ScaLAPACK no consigue


solucionarlos:

• Problema no simétrico de valores propios (NEP).


• Problema generalizado no simétrico de valores propios (GNEP).
• Problema generalizado de la descomposición en valores singulares (GSVD).

Procesamiento Vectorial y Paralelo Página 40 de 49


MPI y Librerías Paralelas Grupo 15

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.

El estándar MPI-1 no incluye algunas funcionalidades importantes como son:

• Las comunicaciones deben programarse explícitamente.


• Las implementaciones en “memoria compartida” no son siempre óptimas.
• No hay posibilidad de crear procesos dinámicamente.
• No hay entrada / salida en paralelo
• No proporciona mecanismos de depuración

Sin embargo en el estándar MPI-2 se incluyeron entre otras características,


funcionalidad para la creación y gestión de procesos, nuevas operaciones colectivas y
operaciones de entrada / salida (MPI-IO).

Al ser MPI un estándar muy extendido en la programación de aplicaciones


paralelas existen multitud de implementaciones desarrolladas y/o en desarrollo según
las necesidades de los programadores de aplicaciones paralelas:

o FT-MPI: Es una implementación completa de la especificación 1.2 MPI


que proporciona tolerancia a fallos a nivel de procesos y a nivel de la
API de MPI. Soporta la caída de n-1 procesos en un entorno de n
procesos y, si se requiere, puede reiniciarlos. Sin embargo, es
responsabilidad de la aplicación recuperar las estructuras de datos y los
datos de los procesos caídos.

o LA-MPI: Su desarrollo está motivado por una mayor necesidad de


tolerancia a fallos a nivel software en los sistemas computacionales de
altas prestaciones. La mayoría de los componentes actuales funcionan
correctamente en sistemas pequeños. No obstante a la hora de añadirlos a
sistemas de altas prestaciones su nivel de tolerancia a fallos no es
suficiente para poder ejecutar aplicaciones demasiado complejas. Por
esto, LA-MPI ha sido creado con los dos principales objetivos de
mejorar la tolerancia a fallos y aumentar las prestaciones.

o PACX-MPI: Es una librería que permite ejecutar aplicaciones paralelas


desarrolladas según MPI en sistemas Grid sin tener que modificarlas.

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.

o A la hora de desarrollar una librería paralela utilizando MPI, debemos


tener en cuenta que nos podemos encontrar con ciertos problemas que
dificultan dicho desarrollo. Normalmente, dichos problemas confluyen

Procesamiento Vectorial y Paralelo Página 41 de 49


MPI y Librerías Paralelas Grupo 15

en la comunicación, es decir, en posibles interferencias en las


comunicaciones. Es, por tanto, necesario diseñar y estudiar la forma en
que la librería se va a desarrollar, de tal modo que no se produzcan
interferencias con otros codigos, ya sean de usuario o de otras librerías.
Este detalle, pues, complica aun más el desarrollo de la librería paralela,
puesto que se necesita implementar un mecanismo de protección, como
hemos explicado anteriormente.

o Desde el punto de vista del usuario, el uso de librerías paralelas facilita


mucho el trabajo del usuario, puesto que no obliga al mismo a conocer
MPI y comunicaciones. Es, pues, como una capa SW, que proporciona
una interfaz, con un conjunto de cabeceras que el usuario puede ejecutar,
desconociendo el funcionamiento interno de dicha librería y los mensajes
MPI usados.

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.

Procesamiento Vectorial y Paralelo Página 42 de 49


MPI y Librerías Paralelas Grupo 15

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

 Using MPI-2: Advanced Features of the Message-Passing Interface. by William


Gropp, Ewing Lusk, and Rajeev Thakur. Published in 1999 by MIT Press.

 Programación de apliaciones paralelas con MPI (Message Passing Interface).


Jose Miguel Alonso. Facultad de Informática UPV/EHU. 1997

 GRIDS & e-Ciencia. Curso Postgrado CSIC. David Rodríguez. Consejo


Superior de Investigadores Científicas. Instituto de Fisica de Cantabria.
Santander. Junio 2004.

 Modelo de Programación basado en el Paso de Mensajes. Ignacio Martín


Llorente. Dpto. de Arquitectura de Computadores y Automática. Universidad
Complutense de Madrid.

 MPI: Message Passing Interface. Dpto. Arquitectura de Computadores II.


Universidad Carlos III de Madrid.

 Uso de Librerias Software de Alto Rendimiento en Aplicaciones en Ciencia e


Ingenieria: Bibliotecas Paralelas. V. Martín. Facultad de Informática.
Universidad Politécnica de Madrid. Julio 1999.

 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/

Procesamiento Vectorial y Paralelo Página 43 de 49


MPI y Librerías Paralelas Grupo 15

8 ANEXO: Implementación Ibcast


A continuación incluiremos la implementación de la librería Ibcast, junto con un
programa de prueba, que ejecuta dicha librería.

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;

/* estructura par alas operaciones de ibcast */


typedef struct {
MPI_Request *req_array;
MPI_Status *stat_array;
int num_sends;
int num_recvs;
} Ibcast_handle;

/* 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 ibcast_keyval = MPI_KEYVAL_INVALID;

int Ibcast(void *buf, int count, MPI_Datatype datatype, int root,


MPI_Comm comm, Ibcast_handle **handle_out) {
Ibcast_syshandle *syshandle;
Ibcast_handle *handle;
int flag;
int mask;

Procesamiento Vectorial y Paralelo Página 44 de 49


MPI y Librerías Paralelas Grupo 15

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;

/* info sobre el comunicador */


MPI_Comm_size(comm, &size);
MPI_Comm_rank(comm, &rank);

/* si el tamaño es 1, ha terminado */
if (size == 1) {
(*handle_out) = NULL;
return(MPI_SUCCESS);
}

/* primero se mira si la libreria ha sido llamada. En caso de que


no, se genera una nueva clave */
if (ibcast_keyval == MPI_KEYVAL_INVALID) {
MPI_Keyval_create(Ibcast_copy, Ibcast_delete, &ibcast_keyval,
NULL);
}

MPI_Attr_get(comm, ibcast_keyval, (void **)&syshandle, &flag);


if (flag == 0) {
/* no hay atributo previamente */
syshandle = (Ibcast_syshandle *) malloc(sizeof(Ibcast_syshandle));

/* rellenamos la info del atributo */


syshandle->ordering_tag = 0; /* start with tag zero */
MPI_Comm_dup(comm, &(syshandle->comm)); /* duplicate comm */

/* añadimos la info del comunicador */


MPI_Attr_put(comm, ibcast_keyval, (void *) syshandle);
}

/* creamos la estructura para esta operacion */


handle = (Ibcast_handle *) malloc(sizeof(Ibcast_handle));
handle->num_sends = 0;
handle->num_recvs = 0;

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++;
}

no_handles = handle->num_sends + handle->num_recvs;


handle->req_array = (MPI_Request *) malloc(no_handles *
sizeof(MPI_Request));

Procesamiento Vectorial y Paralelo Página 45 de 49


MPI y Librerías Paralelas Grupo 15

handle->stat_array = (MPI_Status *) malloc(no_handles *


sizeof(MPI_Status));

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++]));
}

/* start send/recv requests */


retn = Ibcast_work(handle);

++(syshandle->ordering_tag);

/* devolvemos la estructura y el codigo de error */


(*handle_out) = handle;
return(retn);
}

static int Ibcast_work(Ibcast_handle *handle) {


/* si no hay recv comienzan las sends */
if (handle->num_recvs == 0) {
MPI_Startall(handle->num_sends, handle->req_array);
} else {
/* empiezan todas las recv */
MPI_Startall(handle->num_recvs, &handle->req_array[handle-
>num_sends]);
}
return(MPI_SUCCESS);
}

int Ibcast_wait(Ibcast_handle **handle_out) {


Ibcast_handle *handle = (*handle_out);
int retn;
int i;

if (handle == NULL) {
return(MPI_SUCCESS);
}

/* finalizo las recv y empiezo los sends */


if (handle->num_recvs != 0) {
MPI_Waitall(handle->num_recvs, &handle->req_array[handle-
>num_sends],
&handle->stat_array[handle->num_sends]);
MPI_Startall(handle->num_sends, handle->req_array);
}

/* espero por las sends */

Procesamiento Vectorial y Paralelo Página 46 de 49


MPI y Librerías Paralelas Grupo 15

retn = MPI_Waitall(handle->num_sends, handle->req_array, handle-


>stat_array);

for (i=0; i < (handle->num_sends + handle->num_recvs); i++) {


MPI_Request_free( &(handle->req_array[i]) );
}

/* libero la estructura */
free(handle->req_array);
free(handle->stat_array);
free(handle);

/* devuelvo una estructura NULL */


(*handle_out) = NULL;
return(retn);
}
/* copia */
int Ibcast_copy(MPI_Comm oldcomm, int keyval, void *extra, void
*attr_in,
void *attr_out, int *flag) {
Ibcast_syshandle *syshandle = (Ibcast_syshandle *) attr_in;
Ibcast_syshandle *new_syshandle;

if ( (keyval != ibcast_keyval) || (syshandle = NULL) ) {


return(-1);
}

new_syshandle = (Ibcast_syshandle *)
malloc(sizeof(Ibcast_syshandle));

new_syshandle->ordering_tag = 0; /* start with tag zero */


MPI_Comm_dup(syshandle->comm, &(new_syshandle->comm));
/* dup the "hidden" communicator
*/

attr_out = (void *) new_syshandle;


(*flag) = 1;
return(MPI_SUCCESS);
}

/* destructor */
int Ibcast_delete(MPI_Comm comm, int keyval, void *attr_val, void
*extra) {
Ibcast_syshandle *syshandle = (Ibcast_syshandle *) attr_val;

/* do we have valid keyval and attr ? */


if ( (keyval != ibcast_keyval) || (syshandle == NULL) ) {
return(-1);
}

MPI_Comm_free(&(syshandle->comm));
free(syshandle);
return(MPI_SUCCESS);
}

8.3 ibctest.c
/* ibctest.c
prueba la libreria ibcast
como minimo 2 procesos !!

Procesamiento Vectorial y Paralelo Página 47 de 49


MPI y Librerías Paralelas Grupo 15

*/

#include <stdio.h>
#include "ibcast.h"

int main(int argc, char *argv[]) {


int data1;
int data2;
int me;
Ibcast_handle *request1;
Ibcast_handle *request2;

MPI_Init(&argc, &argv);
MPI_Comm_rank(MPI_COMM_WORLD, &me);

if (me == 0) {
data1 = 42;
}
if (me == 1) {
data2 = 4711;
}

/* empiezan los 2 broadcast */


Ibcast(&data1, 1, MPI_INT, 0, MPI_COMM_WORLD, &request1);
Ibcast(&data2, 1, MPI_INT, 1, MPI_COMM_WORLD, &request2);

/* PROGRAMA PRINCIPAL */

/* finaliza los broadcast */


Ibcast_wait(&request1);
Ibcast_wait(&request2);

/* imprimo resultados */
printf("me = %3d, data1 = %8d data2 = %8d\n", me, data1, data2);

MPI_Finalize();
return 0;
}

8.4 Compilación y Ejecución


sgonzalez@laurel:~/ibcast$ ls
ibcast.c ibcast.h ibctest.c
sgonzalez@laurel:~/ibcast$ mpicc -c -Wall -g -o ibcast.o ibcast.c
sgonzalez@laurel:~/ibcast$ mpicc -c -Wall -o ibctest.o ibctest.c
sgonzalez@laurel:~/ibcast$ mpicc -g -Wall -o ibctest ibctest.o
ibcast.o
sgonzalez@laurel:~/ibcast$ lamboot

LAM 7.1.1/MPI 2 C++/ROMIO - Indiana University

sgonzalez@laurel:~/ibcast$ mpirun -np 2 ibctest


me = 1, data1 = 42 data2 = 4711
me = 0, data1 = 42 data2 = 4711
sgonzalez@laurel:~/ibcast$ mpirun -np 5 ibctest
me = 0, data1 = 42 data2 = 4711
me = 2, data1 = 42 data2 = 4711
me = 3, data1 = 42 data2 = 4711
me = 4, data1 = 42 data2 = 4711

Procesamiento Vectorial y Paralelo Página 48 de 49


MPI y Librerías Paralelas Grupo 15

me = 1, data1 = 42 data2 = 4711


sgonzalez@laurel:~/ibcast$

Procesamiento Vectorial y Paralelo Página 49 de 49

Você também pode gostar