next up previous contents
Siguiente: Instalación PVM. Subir: PVM Y XPVM. Anterior: PVM Y XPVM.   Índice General

Introducción PVM.

PVM (Paralel Virtual Machine) es una herramienta diseñada para solucionarnos una gran cantidad de problemas asociados con la programación paralela. Sobre todo, el monetario. Para ello, nos va a crear una nueva abstracción, que es la máquina paralela virtual, empleando los recursos computacionales libres de todas las máquinas de la red que pongamos a disposición de la biblioteca. Es decir, disponemos de todas las ventajas económicas asociadas a la programación distribuida, ya que empleamos los recursos hardware de dicho paradigma; pero programando el conjunto de máquinas como si se tratara de una sola máquina paralela, que es mucho más cómodo.

La PVM es el estándar de facto del mundo científico. De hecho, en el área de la Física Computacional, la PVM es una biblioteca ampliamente usada.

La máquina paralela virtual es una máquina que no existe, pero un API apropiado nos permite programar como si existiese. El modelo abstracto que nos permite usar el API de la PVM consiste en una máquina multiprocesador completamente escalable (es decir, que podemos aumentar y disminuir el número de procesadores en caliente). Para ello, nos va a ocultar la red que estemos empleando para conectar nuestras máquinas, así como las máquinas de la red y sus características específicas. Este planteamiento tiene numerosas ventajas respecto a emplear un supercomputador, de las cuales, las más destacadas son:

El uso de la PVM tiene muchas ventajas, pero también tiene una gran desventaja: nos podemos olvidar del paralelismo fuertemente acoplado. Si disponemos de una red Ethernet, simplemente la red va a dejar de funcionar para todas las aplicaciones (incluida PVM) de la cantidad de colisiones que se van a producir en caso de que intentemos paralelismo fuertemente acoplado. Si disponemos de una red de tecnología más avanzada; es decir, más cara (como ATM) el problema es menor, pero sigue existiendo.

La segunda desventaja es que la abstracción de la máquina virtual, la independencia del hardware y la independencia de la codificación tienen un coste. La PVM no va a ser tan rápida como son los Sockets. Sin embargo, si el grado de acoplamiento se mantiene lo suficientemente bajo, no es observable esta diferencia.

La arquitectura de la pvm se compone de dos partes. La primera parte es el daemon, llamado pvmd. En la versión actual de la PVM -la 3-, el nombre es pvmd3. El daemon ha de estar funcionando en todas las máquinas que vayan a compartir sus recursos computacionales con la máquina paralela virtual. A diferencia de otros daemons y programas del sistema, el daemon de la PVM puede ser instalado por el usuario en su directorio particular (de hecho, la instalación por defecto es así). Esto nos va a permitir hacer supercomputación como usuarios, sin tener que discutir con el administrador de la red que programas vamos a poder ejecutar (aunque suele ser una buena idea comentar que vamos a instalar la PVM en el sistema, por la carga que puede llegar a producir en las comunicaciones globales en algunos casos). Una vez que un usuario (o superusuario) instaló en un directorio la PVM, todos los usuarios pueden hacer uso de esa instalación con el requisito de que el directorio donde esté instalada la PVM sea de lectura al usuario que quiera hacer uso de ella.

En muchos centros de computación, el administrador prefiere instalar él mismo la PVM; con lo que, además de evitar que un usuario pueda borrarla sin consultar a los demás, va a permitir que todos los usuarios tengan la PVM instalada por defecto; y, lo que es más importante, nosotros como administradores podremos determinar el valor de nice (prioridad del daemon) con el que va a ser lanzado el daemon pvmd3 y así, si este valor de nice es lo suficientemente alto, permite que la máquina ejecute la PVM solamente en los momentos ociosos.

Este daemon pvmd3 es el responsable de la máquina virtual de por sí, es decir, de que se ejecuten nuestros programas para la PVM y de gerenciar los mecanismos de comunicación entre máquinas, la conversión automática de datos y de ocultar la red al programador. Por ello, una vez que la PVM esté en marcha, el paralelismo es independiente de la arquitectura de la máquina, y sólo depende de la arquitectura de la máquina virtual creada por la PVM. Esto nos va a evitar el problema que teníamos con los Sockets ya que teníamos que hacer una rutina de codificación y otra de decodificación, al menos, por cada arquitectura distinta del sistema.

Cada usuario, arrancará el daemon como si de un programa normal se tratase, para ejecutar el código de PVM. Este programa se queda residente, realizando las funciones anteriores.

La segunda parte es la biblioteca de desarrollo. Contiene las rutinas para operar con los procesos, transmitir mensajes entre procesadores y alterar las propiedades de la máquina virtual. Toda aplicación se ha de enlazar a la biblioteca para poderse ejecutar después. Tendremos tres ficheros de bibliotecas, la libpvm3.a (biblioteca básica en C), la libgpvm3.a (biblioteca de tratamiento de grupos) y la libfpvm3.a (biblioteca para Fortran).

Un programa para PVM va a ser un conjunto de tareas que cooperan entre si. Las tareas se van a intercambiar información empleando paso de mensajes. La PVM, de forma transparente al programador, nos va a ocultar las transformaciones de tipos asociadas al paso de mensajes entre máquinas heterogéneas. Toda tarea de la PVM puede incluir o eliminar máquinas, arrancar o parar otras tareas, mandar datos a otras tareas o sincronizarse con ellas.

Cada tarea en la PVM tiene un número que la identifica unívocamente, denominado TID (Task Identification Number). Es el número al que se mandan los mensajes habitualmente. Sin embargo, no es el único método de referenciar una tarea en la PVM. Muchas aplicaciones paralelas necesitan hacer el mismo conjunto de acciones sobre un conjunto de tareas. Por ello, la PVM incluye una abstracción nueva, el grupo. Un grupo es un conjunto de tareas a las que nos podemos referir con el mismo código, el identificador de grupo. Para que una tarea entre o salga de un grupo, basta con avisar de la salida o entrada al grupo. Esto nos va a dotar de un mecanismo muy cómodo y potente para realizar programas empleando modelos SIMD (Single Instruction, Multiple Data), en el que vamos a dividir nuestros datos en muchos datos pequeños que sean fáciles de tratar, y después vamos a codificar la operación simple y replicarla tantas veces como datos unitarios tengamos de dividir el problema. Para trabajar con grupos, además de enlazar la biblioteca de la PVM (libpvm3.a) tenemos que enlazar también la de grupos (libgpvm3.a).

Habitualmente para arrancar un programa para la PVM, se lanzará manualmente desde un ordenador contenido en el conjunto de máquinas una tarea madre. La tarea se lanzará con el comando spawn desde un monitor de la máquina virtual, que a su vez se activará con el comando pvm. Esta tarea se encargará de iniciar todas las demás tareas, bien desde su función main (que va a ser la primera en ejecutarse), bien desde alguna subrutina invocada por ella. Para lanzar nuevas tareas se emplea la función pvm_spawn, que devolverá un código de error, asociado a si pudo o no crearla, y el TID de la nueva tarea.

Para evitar el engorro de andar realizando transformaciones continuas de datos, la PVM define clases de arquitecturas. Antes de mandar un dato a otra máquina comprueba su clase de arquitectura. Si es la misma, no necesita convertir los datos, con lo que se tiene un gran incremento en el rendimiento. En caso que sean distintas las clases de arquitectura se emplea el protocolo XDR para codificar el mensaje.

Las clases de arquitectura están mapeadas en números de codificación de datos, que son los que realmente se transmiten y, por lo tanto, los que realmente determinan la necesariedad de la conversión.

El modelo de paso de mensajes es transparente a la arquitectura para el programador, por la comprobación de las clases de arquitectura y la posterior codificación con XDR de no coincidir las arquitecturas. Los mensajes son etiquetados al ser enviados con un número entero definido por el usuario, y pueden ser seleccionados por el receptor tanto por dirección de origen como por el valor de la etiqueta.

El envío de mensajes no es bloqueante. Esto quiere decir que el que envía el mensaje no tiene que esperar a que el mensaje llegue, sino que solamente espera a que el mensaje sea puesto en la cola de mensajes. La cola de mensajes, además, asegura que los mensajes de una misma tarea llegarán en orden entre si. Esto no es trivial, ya que empleando UDP puede que enviemos dos mensajes y que lleguen fuera de orden (UDP es un protocolo no orientado a conexión). TCP, por ser un protocolo orientado a la conexión, realiza una reordenación de los mensajes antes de pasarlos a la capa superior, sin embargo, tiene el inconveniente que establecer las conexiones entre nodos empleando TCP supone, si tenemos n nodos, tendremos un mínimo de ($n)(n$-1) conexiones TCP activas. Provocando esto que hasta para números ridículos de $n$ nos quedamos sin puertos por éste planteamiento. Establecer conexiones TCP entre procesos en lugar de entre nodos es peor todavía, por las mismas razones que en el caso de los nodos.

La comunicación de las tareas con el daemon se hace empleando TCP. Esto se debe a que, al ser comunicaciones locales, la carga derivada de la apertura y cierre de un canal es muy pequeño. Además, no vamos a tener tantas conexiones como en el caso de la conexión entre daemons, ya que las tareas no se conectan entre sí ni con nada fuera del nodo, por lo que sólo hablan directamente con su daemon. Esto determina que serán n conexiones TCP, que sí es una cifra razonable.

La recepción de los mensajes podemos hacerla mediante primitivas bloqueantes, no bloqueantes o con un tiempo máximo de espera. La PVM nos dotará de primitivas para realizar los tres tipos de recepción. En principio nos serán más cómodas las bloqueantes, ya que nos darán un mecanismo de sincronización bastante cómodo. Las de tiempo máximo de espera nos serán útiles para trabajar con ellas como si fuesen bloqueantes, mas dando soporte al hecho de que puede que el que tiene que mandarnos el mensaje se haya colgado. Por último, la recepción de mensajes mediante primitivas no bloqueantes hace de la sincronización un dolor de cabeza. De cualquier forma, en los tres casos anteriormente citados la misma PVM se encargará de decirnos cuándo una tarea acabó. Para informarnos de lo que pasa, emplea un mecanismo de eventos asíncronos.

La PVM puede ser empleada de forma nativa como funciones en C y en C++, y como procedimientos en Fortran. Basta para ello con tomar las cabeceras necesarias (si trabajamos con C o C++); y, para los tres, enlazar con la biblioteca adecuada, que viene con la distribución estándar. En el caso de C es libpvm3.a y en el del Fortran libfpvm3.a.

Si deseamos trabajar en otros lenguajes puede ser un poco más complejo. Si el lenguaje permite incorporar funciones nativas en lenguaje C (como es el caso, por ejemplo, de Java) no hay ningún problema; ya que podemos invocar la función; bien directamente si el lenguaje lo permite, bien haciendo alguna pequeña rutina para adaptar el tipo de los datos, el formato de llamada a función o cualquiera de las restricciones que nos imponga el lenguaje que empleemos para invocar funciones en C.

Hemos de destacar que toda función en C pvm_alguna cosa tiene como equivalente en Fortran pvmfalgunacosa, y viceversa.

El programa PVM corresponde al interprete de comandos de nuestra máquina virtual. Algunos de los comandos más importantes son:

Podemos obtener la PVM vía ftp anónimo: ftp://netlib2.cs.utk.edu


next up previous contents
Siguiente: Instalación PVM. Subir: PVM Y XPVM. Anterior: PVM Y XPVM.   Índice General
Ismael Olea 2004-03-22