



(5 opiniones)
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
El paquete rpm de PVM que se va ha instalar es el que nos provee el cd de Red Hat Linux 6.2, para llevar a cabo su instalación se realizará lo siguiente:
$mount /mnt/cdrom
$cd /mnt/cdrom/RedHat/RPMS/
$rpm -ivh pvm-3.4.3-4.i386.rpm
En el sitio web http://www.epm.ornl.gov/pvm/pvm_home.html se podrá obtener la última versión de dicho software además de abundante información y sitios relacionados.
En el directorio del usuario se tendrá que crear la siguiente estructura de directorios $HOME/pvm3/bin/LINUX. A continuación se modificará el archivo .bashrc quedando de la siguiente forma:
# .bashrc
# User specific aliases and functions
# Source global definitions
if [ -f /etc/bashrc ]; then
. /etc/bashrc
fi
# append this file to your .profile to set path according to machine
# type. you may wish to use this for your own programs (edit the last
# part to point to a different directory f.e./bin/_$PVM_ARCH.
export PVM_ROOT=/usr/share/pvm3
export XPVM_ROOT=$PVM_ROOT/xpvm
if [ -z $PVM_ROOT ]; then
if [ -d/pvm3 ]; then
export PVM_ROOT=/pvm3
else print "Warning - PVM_ROOT not defined"
print "To use PVM, define PVM_ROOT and rerun your .profile" fi fi
if [ -n $PVM_ROOT ]; then
export PVM_ARCH=`$PVM_ROOT/lib/pvmgetarch`
# uncomment one of the following lines if you want the PVM commands
# directory to be added to your shell path.
export PATH=$PATH:$PVM_ROOT/lib # generic
# export PATH=$PATH:$PVM_ROOT/lib/$PVM_ARCH # arch-specific
# uncomment the following line if you want the PVM executable directory
# to be added to your shell path.
export PATH=$PATH:$PVM_ROOT/bin/$PVM_ARCH
export PATH=$PATH:$HOME/pvm3/bin/$PVM_ARCH
fi
A continuación se creará el archivo .pvmrc, en el cual se incluirán el nombre de los nodos que van a formar la PVM. Dicho archivo tendrá la siguiente estructura:
# example PVM console startup script
# copy this file to $HOME/.pvmrc
# command aliases
alias ? help
alias print_environment spawn -> /bin/env
alias h help
alias j jobs
alias t ps
alias tm trace
alias v version
# important for debugging
#
setenv PVM_EXPORT DISPLAY
# want to see these trace events by default
tm addhosts delhosts halt
tm pvm_mytid pvm_exit pvm_parent
tm send recv nrecv probe mcast trecv sendsig recvf
#
# inscripcion de los nodos que forman parte del cluster
#
add pc1
add pc2
version # print PVM release version
id # print console TID
conf
Seguidamente se modificará el fichero .rhosts incluyendo el nombre de los nodos que van a trabajar con la PVM.
pc0
front end
pc1
pc2
Antes de compilar se tendrá que comprobar que la PVM esta activa de la siguiente forma:
$pvm
Una vez activada la PVM utilizaremos el comando quit para salir de esta.
Seguidamente se creará un archivo llamado Makefile.aimk, que tendrá la siguiente estructura:
DEBUG =
SDIR = ..
BDIR = $(HOME)/pvm3/bin
#BDIR = $(SDIR)/../bin
XDIR = $(BDIR)/$(PVM_ARCH)
CC = gcc
OPTIONS = -g
CFLAGS= $(OPTIONS) -I$(PVM_ROOT)/include $(ARCHCFLAGS)
LIBS = -lpvm3 $(ARCHLIB)
GLIBS = -lgpvm3
LFLAGS= $(LOPT) -L$(PVM_ROOT)/lib/$(PVM_ARCH)
default: nombre_programa -master nombre_programa-slave
nombre_programa-master : $(SDIR)/ejer5-master.c $(XDIR)newli $(CC) $(DEBUG) $(CFLAGS) -o $@ $(SDIR)/ejer5-master.c![]()
$(LFLAGS) $(LIBS) -lm
cp $@ $(XDIR)
nombre_programa-slave : $(SDIR)/nombre_programa-slave.c $(XDIR)
$(CC) $(DEBUG) $(CFLAGS) -o $@ $(SDIR)/nombre_programa-slave.c![]()
$(LFLAGS) $(LIBS) -lm
cp $@ $(XDIR)
$(XDIR):
- mkdir $(BDIR)
- mkdir $(XDIR)
clean:
rm -f *.o nombre_programa-master nombre_programa-slave $(XDIR)/nombre_programa-master $(XDIR)/ nombre_programa -slave
Para compilar los programas fuentes únicamente se tendrá que hacer: $
aimk
En el caso de que se quiera borrar los código objeto:
$aimk clean
Una vez que tenemos los programas ya compilados para ejecutarlos se realizará lo siguiente:
$
programa-master Numero de procesos
En muchas ocasiones, es muy útil tener una representación gráfica de la configuración de la máquina virtual que se está utilizando, así como una codificación visual de la actividad llevada a cabo en cada host de la máquina virtual, qué mensajes se están enviando, quién los envía y a dónde. La interfaz gráfica de usuario de PVM (XPVM) permite realizar todas estas funciones.
XPVM combina las funciones de la consola básica PVM con un monitor de seguimiento de actividades y un debugger en una interfaz tipo X-Windows. XPVM está escrito en C, usando el toolkit TCL/TK.
Para ejecutar XPVM, hay que asegurarse de que el daemon no está ya corriendo y que no haya ficheros temporales relacionados con PVM.
El menú Hosts nos permite añadir un nuevo host a la máquina virtual, seleccionado de entre todos los hosts listados en el fichero .xpvm_hosts.
En este caso vamos a añadir varios hosts. Cada vez que añadimos uno aparece un nuevo símbolo de host conectado a los símbolos existentes.
A través del menú Tasks-SPAWN pueden lanzarse tareas en cualquiera de los hosts que compone la máquina.
La vista de Representación de Tareas muestra el estado de todas las tareas que se están ejecutando en la máquina virtual en un momento dado. Para que las tareas se muestren, el botón PLAY que se ve en la parte superior de la ventana de visualización de tareas. La visualización puede ser interrumpida o terminada en cualquier momento, utilizando los botones PAUSE y STOP. Una vez detenida la visualización se mover hacia el pasado o el futuro de las tareas utilizando los botones REWIND y FORWARD.
La vista de Representación de Tareas se compone de dos ventanas. La ventana izquierda contiene el nombre del host y el de la tarea ejecutada en el mismo. Las tareas aparecen ordenadas alfabéticamente. El número de tareas mostradas en una ventana puede aumentarse utilizando el botón de compresión de tareas que aparece a la izquierda de los botones anteriormente mencionados.
La ventana derecha muestra, para cada proceso, el estado de dicha tarea en cada momento, así como líneas rojas que emanan de cada proceso y que corresponden a envíos de mensajes entre procesos. El código de colores muestra el estado del proceso, que puede estar ejecutando tareas propias (verde), rutinas PVM (amarillo) o esperando mensajes (blanco).
La representación de tareas en la ventana derecha puede ampliarse o reducirse (zooming) utilizando simultáneamente los dos botones del ratón (simula el botón central de un ratón de tres botones) y el botón derecho, respectivamente.
Existe una ventana de salida de tareas, accesible a través del menú VIEWS, que actúa como salida standard para los procesos.
Finalmente, existe una ventana de utilización de recursos, accesible también a través del menú VIEWS. Esta ventana, que está sincronizada con la ventana de representación de tareas, muestra el número de tareas que están ejecutándose, ejecutando funciones PVM o en espera en cada momento.
|