La interfaz de paso de mensajes o MPI (sigla del inglés message passing interface) es un estándar que define la sintaxis y la semántica de las funciones contenidas en una biblioteca de paso de mensajes diseñada para ser usada en programas que exploten la existencia de múltiples procesadores, escritas generalmente en C, C++, Fortran y Ada. Siendo más específicos, en realidad MPI se trata de un "estándar por consenso", el cual fue diseñado originalmente en un foro abierto que incluía proveedores de hardware, investigadores, académicos, desarrolladores de bibliotecas de software y usuarios, que representaban a más de 40 organizaciones. Es gracias a esta amplia participación que tuvo su desarrollo que surgió rápidamente MPI, en forma de un estándar ampliamente usado para escribir programas de paso de mensajes. Con esto dicho, cabe aclarar que MPI no es un verdadero estándar, debido a que no fue emitido por una organización de estándares como ISO o la IEEE, sin embargo, este es ampliamente usado debido a que es de código libre.
La ventaja de MPI sobre otras bibliotecas de paso de mensajes, es que los programas que utilizan la biblioteca son portables (dado que MPI ha sido implementado para casi toda arquitectura de memoria distribuida), y rápidos, (porque cada implementación de la biblioteca ha sido optimizada para el hardware en la cual se ejecuta).
Historia
Al diseñarse la interfaz de paso de mensajes se tuvieron en cuenta las características más atractivas de los sistemas existentes para el paso de mensajes, en vez de seleccionar uno solo de ellos y adoptarlo como el estándar, resultando así, en una fuerte influencia para MPI los trabajos hechos por IBM, INTEL, NX/, Express, nCUBE's Vernex, p4 y PARMACS. Otras contribuciones importantes provienen de Zipcode, Chimp, PVM, Chameleon y PICL.
El esfuerzo para estandarizar MPI involucró a cerca de 60 personas de 40 organizaciones diferentes principalmente de EE. UU. y Europa.
La mayoría de los vendedores de computadoras concurrentes estaban involucrados con MPI, así como con investigadores de diferentes universidades, laboratorios del gobierno e industrias.
El proceso de estandarización comenzó en el taller de estándares para el paso de mensajes en un ambiente con memoria distribuida, patrocinado por el Centro de Investigación en Computación Paralela en Williamsburg, Virginia, Estados Unidos (abril 29-30 de 1992).
Se llegó a una propuesta preliminar conocida como MPI1, enfocada principalmente en comunicaciones punto a punto sin incluir rutinas para comunicación colectiva y no presentaba tareas seguras.
El estándar final por el MPI fue presentado en la conferencia de supercómputo en noviembre de 1993, constituyéndose así el foro para el MPI.
En un ambiente de comunicación con memoria distribuida en la cual las rutinas de paso de mensajes de nivel bajo, los beneficios de la estandarización son muy notorios. La principal ventaja al establecer un estándar para el paso de mensajes es la portabilidad y el ser fácil de utilizar. MPI-1 apareció en 1994, el estándar MPI-2 fue lanzado en 1997
Fundamentos de MPI Con MPI el número de procesos requeridos se asigna antes de la ejecución del programa, y no se crean procesos adicionales mientras la aplicación se ejecuta. A cada proceso se le asigna una variable que se denomina rango, la cual identifica a cada proceso, en el rango de 0 a p-1, donde p es el número total de procesos. El control de la ejecución del programa se realiza mediante la variable de rango. Esta permite determinar qué proceso ejecuta determinada porción de código. En MPI se define un comunicador como una colección de procesos, los cuales pueden enviar mensajes el uno al otro; el comunicador básico se denomina MPI_COMM_WORLD y se define mediante un macro del lenguaje C. MPI_COMM_WORLD agrupa a todos los procesos activos durante la ejecución de una aplicación. Las llamadas de MPI se dividen en cuatro clases: Llamadas utilizadas para inicializar, administrar y finalizar comunicaciones. Llamadas utilizadas para transferir datos entre un par de procesos. Llamadas para transferir datos entre varios procesos. Llamadas utilizadas para crear tipos de datos definidos por el usuario.
La primera clase de llamadas permiten inicializar la biblioteca de paso de mensajes, identificar el número de procesos y el rango de los procesos. La segunda clase de llamadas incluye operaciones de comunicación punto a punto, para diferentes tipos de actividades de envío y recepción. La tercera clase de llamadas son conocidas como operaciones grupales, que proveen operaciones de comunicaciones entre grupos de procesos. La última clase de llamadas provee flexibilidad en la construcción de estructuras de datos complejos.
En MPI, un mensaje está conformado por el cuerpo del mensaje, el cual contiene los datos a ser enviados, y su envoltura, que indica el proceso fuente y el destino. El cuerpo del mensaje en MPI se conforma por tres piezas de información: búfer, tipo de dato y contador. El búfer, es la localidad de memoria donde se encuentran los datos de salida o donde se almacenan los datos de entrada. El tipo de dato, indica el tipo de los datos que se envían en el mensaje. En casos simples, éste es un tipo básico o primitivo, por ejemplo, un número entero, y que en aplicaciones más avanzadas puede ser un tipo de dato construido a través de datos primitivos. Los tipos de datos derivados son análogos a las estructuras de C. El contador es un número de secuencia que junto al tipo de datos permiten al usuario agrupar ítems de datos de un mismo tipo en un solo mensaje. MPI estandariza los tipos de datos primitivos, evitando que el programador se preocupe de las diferencias que existen entre ellos, cuando se encuentran en distintas plataformas.
La envoltura de un mensaje en MPI típicamente contiene la dirección destino, la dirección de la fuente, y cualquier otra información que se necesite para transmitir y entregar el mensaje. La envoltura de un mensaje en MPI, consta de cuatro partes: la fuente, el destino, el comunicador y una etiqueta. La fuente identifica al proceso transmisor. El destino identifica al proceso receptor. El comunicador especifica el grupo de procesos a los cuales pertenecen la fuente y el destino. La etiqueta permite clasificar el mensaje.
El campo etiqueta es un entero definido por el usuario que puede ser utilizado para distinguir los mensajes que recibe un proceso. Por ejemplo, se tienen dos procesos A y B. El proceso A envía dos mensajes al proceso B, ambos mensajes contienen un dato. Uno de los datos es utilizado para realizar un cálculo, mientras el otro es utilizado para imprimirlo en pantalla. El proceso A utiliza diferentes etiquetas para los mensajes. El proceso B utiliza los valores de etiquetas definidos en el proceso A e identifica que operación deberá realizar con el dato de cada mensaje.
Llamadas utilizadas para inicializar, administrar y finalizar comunicaciones MPI dispone de cuatro funciones primordiales que se utilizan en todo programa con MPI: MPI_Init, MPI_Finalize, MPI_Comm_size y MPI_Comm_rank.
MPI_Init
Esta función inicializa todas las estructuras de datos necesarias para permitir la comunicación entre procesos basados en el envío de mensajes MPI y debe ser utilizada antes de llamar a cualquier otra función de MPI. Reserva el canal de comunicación, asigna un valor a la constante MPI_COMM_WORLD Esta rutina, o MPI_Init_thead, debe ser llamado antes que cualquier otra rutina de MPI. Debe ser llamada una única vez, en caso de no hacerlo así sería erróneo. De esta forma, un programa normal MPI debería quedar de la siguiente forma:int main(int argc, char* argv[]){ /* Declaracion de variables */ MPI_Init(&argc, &argv); /* Reparto de contenido */ /* Bucle principal del programa */ MPI_Finalize(); }Todos los programas MPI deben tener siempre una llamada a esta función. Los argumentos que recibe no son ni modificados, ni interpretados ni distribuidos.
Sintaxis de MPI_Init
En C#include "mpi.h" int MPI_Init (int *argc, char ***argv)En C++#include <mpi.h> void MPI::Init(int& argc, char**& argv) void MPI::Init()Parámetros de entrada de MPI_Init
argc: puntero al número de argumentos
argv: puntero al vector de argumentos
MPI_Finalize
Permite terminar una sesión MPI. Esta función debe ser la última llamada a MPI que un programa realice. Permite liberar la memoria usada por MPI.
MPI_Comm_size
Permite determinar el número total de procesos que pertenecen a un comunicador.
MPI_Comm_rank
Permite determinar el identificador o rango del proceso actual.
Llamadas utilizadas para transferir datos entre dos procesos La transferencia de datos entre dos procesos se consigue mediante las llamadas MPI_Send y MPI_Recv. Estas llamadas devuelven un código que indica su éxito o fracaso.
MPI_Send
Permite enviar información desde un proceso a otro.
MPI_Recv
Permite recibir información desde otro proceso.
Ambas funciones son bloqueantes, es decir que el proceso que realiza la llamada se bloquea hasta que la operación de comunicación se complete.
Las versiones no bloqueantes de MPI_Send y MPI_Recv son MPI_Isend y MPI_Irecv, respectivamente. Estas llamadas inician la operación de transferencia pero su finalización debe ser realizada de forma explícita mediante llamadas como MPI_Test y MPI_Wait.
MPI_Wait
Es una llamada bloqueante y retorna cuando la operación de envío o recepción se completa.
MPI_Test
Permite verificar si la operación de envío o recepción ha finalizado, esta función primero chequea el estado de la operación de envío o recepción y luego retorna.
Llamadas utilizadas para transferir datos entre varios procesos MPI posee llamadas para comunicaciones grupales que incluyen operaciones tipo difusión (broadcast), recolección (gather), distribución (scatter) y reducción. Algunas de las funciones que permiten realizar transferencia entre varios procesos se presentan a continuación.
MPI_Barrier permite realizar operaciones de sincronización. En estas operaciones no existe ninguna clase de intercambio de información. Suele emplearse para dar por finalizada una etapa del programa, asegurándose de que todos los procesos han terminado antes de dar comienzo a la siguiente.
MPI_Bcast permite a un proceso enviar una copia de sus datos a otros procesos dentro de un grupo definido por un comunicator.
MPI_Scatter establece una operación de distribución, en la cual un dato (arreglo de algún tipo de datos) se distribuye en diferentes procesos.
MPI_Gather establece una operación de recolección, en la cual los datos son recolectados en un solo proceso.
MPI_Reduce permite que el proceso raíz recolecte datos desde otros procesos en un grupo, y los combine en un solo ítem de datos. Por ejemplo, se podría utilizar una operación reducción, para calcular la suma de los elementos de un arreglo que se distribuyó en algunos procesos.
Llamadas utilizadas para crear tipos de datos definidos por el usuario Para definir nuevos tipos de datos se puede utilizar la llamada MPI_Type_struct para crear un nuevo tipo o se puede utilizar la llamada MPI_Pack para empaquetar los datos.
Características de MPI
Estandarización. Portabilidad: multiprocesadores, multicomputadores, redes, heterogéneos, ... Buenas prestaciones. Amplia funcionalidad. Existencia de implementaciones libres (mpich, LAM-MPI, ...)
La especificación detalla las funciones que se pueden utilizar, no el modo como se compilan y lanzan-ejecutan los programas, lo cual puede variar de una implementación a otra.
Siguiendo el modelo SPMD, el usuario escribirá su aplicación como un proceso secuencial del que se lanzarán varias instancias que cooperan entre sí.
Los procesos invocan diferentes funciones MPI que permiten
iniciar, gestionar y finalizar procesos MPI comunicar datos entre dos procesos realizar operaciones de comunicación entre grupos de procesos crear tipos arbitrarios de datos