You are on page 1of 12

27/05/14 El Rincon del Cluster

montatucluster.blogspot.mx 1/12
El Rincon del Cluster
ma r t e s , 1 5 d e f e b r e r o d e 2 0 1 1
Cluster Beowulf + OpenMPI + NFS
Instalacin y configuracin
Condiciones previas
Antes de comenzar tenemos que tener por lo menos dos maquinas con Ubuntu 9.10 instalado y
conectadas en red mediante el protocolo TCP/IP, para este caso todas las maquinas tienen que
ser de la misma arquitectura para poder usar el binario compilado en el nodo maestro en cada
uno de los nodos esclavos, caso sontrario sera necesario compilar la aplicacion segun cada
plataforma. De aca en adelante me referire a los nodos del cluster como nodo maestro o nodo
esclavo segun corresponda.
Desde el nodo maestro podrs controlar el cluster y ejecutar los programas de administracion
del cluster, en los nodos esclavos se ejecutaran las aplicaciones distribuidas desde el nodo
maestro.
Instalacin de los paquetes bsicos
En Ubuntu 9.10 es necesario instalar los siguientes paquetes openmpi-bin, openmpi-common,
libopenmpi1.3, libopenmpi-dev en todos los nodos del cluster.
Esto se puede hacer como root, en Ubuntu 9.10, con:
sudo apt-get install openmpi-bin openmpi-common libopenmpi1.3 libopenmpi-dev
Los nombres pueden variar en su distribucin, pero lo importante es tener en cuenta que debe tener la
misma versin de Open MPI en todas las mquinas para que funcione correctamente.
Para el control y administracin remoto instalaremos tambin en cada integrante del cluster el servidor
ssh.
sudo apt-get install ssh
Para que el maestro sea capaz de ejecutar comandos en cada uno de los nodos necesitamos que estos
ltimos permitan el acceso ssh sin clave para lograr esto haremos lo siguiente.
Primero vamos a generar nuestra clave publica en el nodo meastro, esta sera la que nos permitir acceder
remotamente a cada nodo sin necesidad de proveer una clave por linea de comandos. Es importante que
este proceso se haga en el maestro y los nodos esclavos usando el usuario erluco o el que usted halla
elegido.
erluco@master:~$ ssh-keygen
Cuando se ejecute ese comando aceptar el directorio por defecto para almacenar las claves y cuando
solicita la frase de paso pulsar enter sin ingresar nada.
Tenemos ahora que copiar la llave generada a cada uno de los nodos del cluster, para ello usaremos el
comando de copia remota scp.
Primero en cada nodo esclavo creamos el directorio .ssh que es donde pondremos las llaves.
erluco@nodo1:~$ mkdir .ssh
erluco@nodo1:~$ chmod 700 .ssh
Ahora procedemos a la copia.
erluco@master:~$ scp .ssh/id_rsa.pub erluco@192.168.0.101:
Accedemos ahora al nodo donde copiamos la llave y lo ubicamos en el archivo correcto.
erluco@nodo1:~$ mv id_rsa.pub .ssh/authorized_keys
Los dos pasos anteriores, es decir el proceso de copia y mover el archivo a la ubicacin correcta se tiene
que repetir para cada nodo del cluster.
Ya estamos en condiciones de probar si el nodo maestro es capaz de ejecutar comandos sin necesidad de
proveer la clave de cada nodo. Para hacer esta prueba, desde el maestros y como usuario erluco
ejecutamos el siguiente comando:
erluco@master:~$ ssh erluco@192.168.0.101 hostname
y nos dara una respuesta :
nodo1
Si responde con el nombre de la maquina remota es que funciona correctamente. Ese comando lo que
hace es usar el acceso ssh para a la maquina con el hostname (nodo1) y ejecutar en ella el comando
Acceso remoto a los nodos
Participar en este sitio
Google Friend Connect
Todava no hay miembros.
S el primero!
Ya eres miembro? Iniciar sesin
Seguidores
2011 (2)
febrero (2)
Cluster Beowulf + OpenMPI +
NFS
DIRECTIVAS DE
PARALELIZACION FORTRAN
MP
Archivo del blog
ErLuCo
Ver todo mi perfil
Datos personales
Compart ir
0 Ms

Siguiente blog Crear un blog

Acceder
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 2/12
hostname.
Ahora vamos a corregir nuestros archivos hosts para lograr la identificacin de los nodos del cluster
mediante el nombre. Editamos en cada componente del nodo el archivo /etc/hosts
sudo nano /etc/hosts
y agregamos las siguientes lineas al contenido preexistente
192.168.0.100 master master
192.168.0.101 nodo1 nodo1
192.168.0.102 nodo2 nodo2
Necesitamos en cada nodo de nuestro cluster los programas distribuidos que vamos a ejecutar. Para ello
vamos a instalar y configurar un servidor de archivos NFS en el nodo maestro y los clientes NFS en los
nodos esclavos de manera que trabajemos nicamente en el maestro con nuestros programas y luego
mediante la exportacin del directorio y el montaje remoto desde los nodos podamos tener los mismos
archivos en cada uno de los componentes, existen otras formas de hacer esto, por ejemplo rsync, pero
para mi gusto esta me resulto mas cmoda y transparente.
En el nodo maestro:
erluco@master:~# sudo apt-get install nfs-kernel-server nfs-common portmap
Ahora a configurar un recurso compartido, es nuestro caso crearemos un nuevo directorio dentro del
home de erluco al que llamaremos clusterdir.
erluco@master:~$ mkdir clusterdir
Ahora nuevamente como root vamos a exportar este directorio mediante NFS para que los nodos puedan
montarlo remotamente, editamos el archivo /etc/exports
sudo nano /etc/exports
y agregamos la siguiente linea.
/home/erluco/clusterdir 192.168.0.0/24(rw,no_subtree_check,async,no_root_squash)
Luego reiniciamos el resvicio de NFS con el comando:
erluco@master:~# /etc/init.d/nfs-kernel-server restart
Ahora vamos a instalar lo necesario en cada nodo.
erluco@nodo1:~# sudo apt-get install nfs-common portmap
Una vez que termino la instalacion probaremos si el directorio compartido es accesible desde el nodo:
erluco@nodo1:~# showmount -e 192.168.0.100
y respondera
Export list for 192.168.0.100:
/home/erluco/clusterdir 192.168.0.0/24
Bien ahora vamos a montar el recurso desde linea de comando para luego agregarlo a fstab para el
montado automatico cada vez que arranque el nodo. Creamos en la home de la cuenta erluco un
directorio llamado clusterdir al igual que el que compartimos en el nodo maestro, este sera el directorio
donde montaremos el recursos compartido mediante NFS en el maestro.
erluco@nodo1:~$ mkdir clusterdir
Ahora montamos el directorio remoto, esto lo hacemos como root:
erluco@nodo1:~# sudo mount -t nfs 192.168.0.100:/home/erluco/clusterdir /home/erluco/clusterdir
Podemos comprobar mediante el siguiente comando si todo salio como corresponde:
nodo1:~# mount
y respondera
/dev/xvda2 on / type ext3 (rw,noatime,nodiratime,errors=remount-ro)
tmpfs on /lib/init/rw type tmpfs (rw,nosuid,mode=0755)
proc on /proc type proc (rw,noexec,nosuid,nodev)
sysfs on /sys type sysfs (rw,noexec,nosuid,nodev)
udev on /dev type tmpfs (rw,mode=0755)
none on /dev/pts type devpts (rw)
192.168.0.100:/home/erluco/clusterdir on /home/erluco/clusterdir type nfs (rw,addr=192.168.0.100)
Observe que el ultimo montaje corresponde al directorio compartido por NFS.
Para automatizar el proceso de montaje del recurso exportado en cada nodo esclavo del cluster
modificamos el archivo /etc/fstab
sudo nano /etc/fstab
agregando la siguiente linea:
192.168.0.100:/home/erluco/clusterdir /home/erluco/clusterdir nfs rw,sync,hard,intr 0 0
Para probar en caliente si funciona correctamente procedemos primero a desmontar el recurso con el
comando:
erluco@nodo1:~# umount /home/erluco/clusterdir/
Y luego le decimos como al sistema que monte los sistemas declarado en fstab:
erluco@nodo1:~# mount a
Ya esta todo listo en el NFS para continuar.
En el nodo maestro es necesario instalar las herramientas de desarrollo o por lo menos los compiladores y
libreras bsicas para poder compilar nuestros programas, para ello ejecutamos el siguiente comando:
erluco@master:~# apt-get install build-essential
Configuramos MPI para decirle los nodos que componen el cluster, esto se hace creando un archivo de
configuracin llamado .mpi_hostfile en el home del usuario erluco del nodo maestros
Servidor de archivos NFS
Entorno de desarrollo
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 3/12
nano .mpi_hostfile
con el siguiente contenido:
# Nodo maestro
localhost slots=2
# Nodos esclavos
nodo1 slots=2
nodo2 slots=1
Antes que nada vamos a ver un programa de ejemplo que es el que utilizaremos para probar el cluster, a
continuacion el codigo fuente en C++ de un programa que suma numeros primos.
nano primos.c++
y pegamos esto
# include <cstdlib>
# include <iostream>
# include <iomanip>
# include <cmath>
# include <ctime>
# include "mpi.h"
using namespace std;
int main ( int argc, char *argv[] );
int prime_number ( int n, int id, int p );
void timestamp ( );
int main ( int argc, char *argv[] )
{
int i;
int id;
int master = 0;
int n;
int n_factor;
int n_hi;
int n_lo;
int p;
int primes;
int primes_part;
double wtime;
n_lo = 1;
n_hi = 131072;
n_factor = 2;
MPI::Init ( argc, argv );
p = MPI::COMM_WORLD.Get_size ( );
id = MPI::COMM_WORLD.Get_rank ( );
if ( id == master )
{
cout << "\n";
cout << "'Cuenta primos\n";
cout << " C++/MPI version\n";
cout << "\n";
cout << " Programa para contar cantidad de primos para un N dado.\n";
cout << " Corriendo en " << p << " procesos\n";
cout << "\n";
cout << " N S Tiempo\n";
cout << "\n";
}
n = n_lo;
while ( n <= n_hi )
{
if ( id == master )
{
wtime = MPI::Wtime ( );
Prueba del cluster
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 4/12
}
MPI::COMM_WORLD.Bcast ( &n, 1, MPI::INT, master );
primes_part = prime_number ( n, id, p );
MPI::COMM_WORLD.Reduce ( &primes_part, &primes, 1, MPI::INT, MPI::SUM,
master );
if ( id == master )
{
wtime = MPI::Wtime ( ) - wtime;
cout << " " << setw(8) << n
<< " " << setw(8) << primes
<< " " << setw(14) << wtime << "\n";
}
n = n * n_factor;
}
MPI::Finalize ( );
if ( id == master )
{
cout << "\n";
cout << "PRIME_MPI - Procesos maestro:\n";
cout << " Finalizacion del calculo normal.\n";
}
return 0;
}
int prime_number ( int n, int id, int p )
{
int i;
int j;
int prime;
int total;
total = 0;
for ( i = 2 + id; i <= n; i = i + p )
{
prime = 1;
for ( j = 2; j < i; j++ )
{
if ( ( i % j ) == 0 )
{
prime = 0;
break;
}
}
total = total + prime;
}
return total;
}
void timestamp ( )
{
# define TIME_SIZE 40
static char time_buffer[TIME_SIZE];
const struct tm *tm;
size_t len;
time_t now;
now = time ( NULL );
tm = localtime ( &now );
len = strftime ( time_buffer, TIME_SIZE, "%d %B %Y %I:%M:%S %p", tm );
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 5/12
Publicado por ErLuCo en 6:59 1 comentario:
cout << time_buffer << "\n";
return;
# undef TIME_SIZE
}
Lo compilamos con el comando:
erluco@master:~/clusterdir$ mpic++ primos.c++ -o primos
Esto nos dara como resultado el binario ejecutable del programa de prueba, podremos verificar que
gracias a la comparticion NFS este binaro se encuentra disponible tambien en cada uno de los nodos que
hemos agregado a nuestro cluster.
Ahora lo vamos a ejecutar y verificar su funcionamiento primero solo en el nodo maestro y luego en todos
los nodos del cluster.
Primero en el nodo maestro unicamente:
erluco@master:~/clusterdir$ ./primos
Ahora distribuido en todos los nodos del cluster, para ello vamos a usar la herramienta mpirun con el
parametro -np 5 con el que le decimos la cantidad de procesos a ejecutar.
erluco@master:~/clusterdir$ mpirun -np 5 --hostfile ../.mpi_hostfile ./primos
o
erluco@master:~/clusterdir$ mpirun -np 5./primos
y se obtendra este resultado :
Cuenta primos
C++/MPI version
Programa para contar cantidad de primos para un N dado.
Corriendo en 3 procesos
N S Tiempo
1 0 0.33965
2 1 0.0965161
4 2 0.0619619
8 4 0.125543
16 6 0.179036
32 11 0.122198
64 18 0.152973
128 31 0.117323
256 54 0.17525
512 97 0.182207
1024 172 0.123595
2048 309 0.0812861
4096 564 0.162669
8192 1028 0.207587
16384 1900 0.494075
32768 3512 1.62275
65536 6542 3.71538
131072 12251 1.0127
PRIME_MPI - Procesos maestro:
Finalizacion del calculo normal.
Recomendar esto en Google
DIRECTIVAS DE PARALELIZACION FORTRAN MP
Introduccin
Este curso pretende dar una visin general sobre las directivas de paralelizacin Fortran MP, que
permiten ejercer un mayor control sobre el cdigo de programa del que tiene la paralelizacin
automtica. Esto da lugar a un mejor aprovechamiento de los recursos de la mquina Seymour, al
especificarle como debe trabajar con cada regin de nuestro programa. Las directivas Fortran MP se
caracterizan por:
Son faciles de usar, permitiendo una paralelizacin muy efectiva de bucles con la
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 6/12
introduccin de unos pocos comandos. Estos comandos no son compatibles con
compiladores de otros vendedores, aunque si con la paralelizacin automtica PFA.
Se activan con la opcin de compilacin -mp. Esta opcion debe introducirse entre las que
acompaan al comando f77 para permitir la paralelizacin del cdigo.
Se inician siempre con la clave c$. Por tanto, sin la opcin -mp se leen como comentarios
en el cdigo, y el programa se ejecuta normalmente en serie. (Esto facilita la portabilidad
del cdigo, pudiendo ser ejecutado tanto en serie como en paralelo.)
Los contenidos del curso se muestran en el siguiente ndice:
1. Directiva c$doacross.
Definicin de variables.
Modificador if.
mp_schedtype y chunk.
2. Control del nmero de procesos.
3. Elementos de control del paralelismo.
4. Dependencia de datos.
5. Algunos consejos sobre como trabajar con MP.
6. Algunos ejemplos prcticos.
7. Documentacin sobre Fortran MP.
Directiva c$doacross
c$doacrosses la directiva fundamental, que permite la paralelizacion de un bucle en distintos
procesos. Su estructura general es:
c$doacross
c$& share (variable11,variable12,...),
c$& local (variable21,variable22,...),
c$& lastlocal (variable31,variable32,...),
c$& reduction (variable41,variable42,...),
c$& if (expresion logica),
c$& mp_schedtype = type,
c$& chunk = chunksize

c$& son extensiones de linea; continuan con la descripcin de c$doacross y cualquier
otra directiva iniciada en la linea superior.
Las salidas y entradas de datos que puedan darse en el bucle afectado, deben ejecutarse
en serie. Si no, el bucle no puede funcionar. Para ello se emplea una serie de directivas,
con algunos ejemplos ilustrados ms adelante.
No esta permitido el anidamiento de c$doacross; al hacerlo el programa no puede
funcionar.
A continuacin se hace una explicacin de los modificadores que pueden acompaar a este
comando.
Definicin de Variables
Se facilita el manejo de las variables optimizando el tiempo de ejecucin.
share: variables accesibles portodos los procesos.
local: variables propias de cada regin paralela, no accesibles por el resto.
lastlocal: escalares de los que solo se salva el valor tras la ltima iteracin.
reduction: escalares empleadas en operaciones con eliminacion de otras
variables:
Ej: A = A + B; A = A*B; A = min(A,B); A = max(A,B) (En estos casos se puede definir
c$doacross reduction(A); consultense los ejemplos).
Por defecto los ndices de bucle son lastlocal; las variables nuevas dentro del bucle
c$doacross son local; el resto de variables share. Nota importante: hay que tener en
cuenta que estas variables se definen partiendo de los tres tipos principales de variables en
serie de Fortran:
Globales: corresponden a variables share.
Automaticas (creadas en la llamada a los procesos en los que se encuentran, y
destruidas a la salida de los mismos): corresponden a variables local.
Estaticas (inicializadas a cero al inicio del programa, existen a lo largo de todo el
mismo): no se permiten en paralelizacin MP. Para ello, no esta permitido emplear
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 7/12
la opcion de compilacion -static (que convierte las variables locales en
estticas), y variables definidas con save y con data (tambin estticas).
Un problema grave se da con las variables definidas dentro de un common, que por defecto
son siempre de tipo share. Para definir un common con variables local son necesarias dos
opciones de compilacin, que indican al compilador que hacer con tales variables: -Wl,-
Xlocal,***_, donde *** es el nombre del common con unicamente variables locales. Ejemplo:
Suma de matrices
subroutine suma(A,B,C,M,N)
dimension A(M,N), B(M,N), C(M,N)

c$doacross local(i,j), share(A,B,C,M,N)
do 100 i = 1, M
do 100 j = 1, N
C(i,j) = A(i,j) + B(i,j)
100 continue
return
end
Modificador if
Solo se efectua la paralelizacion si se cumple la condicion impuesta. Se emplea:
Para paralelizar solo cuando los bucles se hacen grandes:
Ej:
c$doacross local(i,j)
c$& if(imax.gt.1000.and.jmax.gt.1000)
do i = 1,imax
do j = 1,jmax
...
enddo
enddo
Para evitar dependencia de datos:
Ej:
c$doacross if(jmax.gt.imax), local(i)
do i = 1, imax
a(i) = b(i) + b(i+jmax)
enddo
Para equilibrar trabajo entre procesos:
Ej:
num = valor
c$doacross local(i), if (num.eq.2)
do i = 1, 2
if(i.eq.1) call work1
if(i.eq.2) call work2
enddo

mp_schedtype y chunk.
mp_schedtype define el modo de distribucin del trabajo en los distintos procesos. Junto a ella se
define el valor de chunk (nmero de iteraciones de bucle incluidas en cada envio). Ambas permiten
un mejor equilibrio de carga entre procesos, mejorando el rendimiento del programa (limitado por
el proceso ms lento). Las opciones de mp_schedtype son:
simple (sin chunk): el total de iteraciones se divide entre el nmero de procesos: la
primera divisin se envia al primer proceso, la segunda al segundo, ..., y la ltima al
ltimo proceso.
Ej: Dadas 13 iteraciones en 4 procesos, los envios son: 1111 222 333 444.
interleave (chunk=1 por defecto): cada envio (de tantas iteraciones como define chunk)
es llevado a cada proceso de manera correlativa: el primero a la primera, el segundo a la
segunda,...).
Ej: Dadas 13 iteraciones en 4 procesos, con chunk=1, los envios son: 1234 1234 1234 1
dynamic (chunk=1 por defecto): los envios se realizan a los procesos que van quedando
libres.
gss (sin chunk): modo dinmico, que se inicia con envios grandes y termina con envios de
un tamao mas pequeo. Es el mejor modo de conseguir que todos los procesos acaben
al mismo tiempo.
runtime: mp_schedtype y chunk quedan definidas por medio de variables de entorno:
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 8/12
setenv MP_SCHEDTYPE type setenv CHUNK n .
Ejemplo del uso de mp_schedtype y chunk: conjunto de Mandelbrot.
El siguiente programa genera un conjunto de Mandelbrot de dimension 128x128, en el cada
columna de puntos es determinada por un proceso distinto (paralelizacion de los bucles 'j'). Todas
las variables se definen como locales, ya que son independientes en cada punto (i,j), excepto la
variable data que es compartida en todos los casos.
Las iteraciones (i,j) tienen una duracin bastante variable, ya que la condicin de finalizacin del do
while determina valores muy diferentes de 'k' dependiendo del punto al que se aplica.
integer i,j
integer k, data(0:127,0:127)
complex*8 c,m
c$doacross local(i,j,c,m,k), share(data),
c$& mp_schedtype = {simple | interleave | dynamic | gss}
do j = 0,127
do i = 0,127
c = cmplx (-2.+(4./127.)*j,-2.+(4./127.)*i)
m = cmplx (0.,0.)
k = 0
do while (abs(m).le.4..and.k.lt.5000)
m = m**2 + c
k = k + 1
enddo
data(i,j) = k
enddo
enddo
stop
end
La distribucin de los valores de 'k' es bastante aleatoria. De ese modo, empleando mp_schedtype =
simple o mp_schedtype = interleave el rendimiento conseguido puede no ser ptimo por una
carga no equilibrada de los procesos. mp_schedtype = dynamic, y sobre todo mp_schedtype = gss
reparten mejor el trabajo y posibilitan que todos los procesos puedan terminar a la vez, reduciendo
el tiempo de ejecucin.
Control del nmero de procesos.
El mejor modo de definir el nmero de procesos en que se debe ejecutar un programa es por medio
de la variable de entorno:
%setenv MP_SET_NUMTHREADS N
Por defecto, esta es igual al nmero de CPUs en el sistema.
Elementos de de control del paralelismo.
Existe una serie de funciones externas que, aadidas a nuestro codigo Fortran, nos permiten un
mejor control sobre la ejecucion paralela de nuestro programa. Informacion completa sobre ellas se
puede obtener a traves de las man pages, por medio de:
man mp
Lo que hacemos aqui es unicamente una enumeracion de las mas importantes:
1. Control de procesos:
subroutine mp_set_numthreads()
Determina el numero de procesos que deben ejecutar un programa.
integer function mp_numthreads()
Indica el numero de procesos que estan ejecutando un programa.
integer function mp_my_threadnum()
Indica que proceso esta determinando aquella parte de programa en la que se incluye.
logical function mp_in_doacross_loop()
Responde a la pregunta: Estoy dentro de un bucle paralelo?
2. Sincronizacin de procesos:
subroutine mp_setlock()
subroutine mp_unsetlock()
La parte de codigo situada entre estas dos subrutinas se ejecuta una sola vez por cada
proceso y de manera no simultanea (acceso limitado a un unico proceso a la vez).
subroutine mp_barrier()
Ningun proceso puede seguir adelante mientras no hayan llegado todos a este punto.
3. Bloqueo de procesos 'esclavos'.
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 9/12
subroutine mp_block()
subroutine mp_unblock()
Estas dos subrutinas rodean a una parte del cdigo que no precisa de los procesos
'esclavos' para su ejecucin. Entre ellas se dejan libres estos procesos, para que se
puedan usar en otros trabajos.
subroutine mp_blocktime(integer iters)
Proceso automtico de bloqueo o liberacin de procesos esclavos, cuando estos dejan
de formar parte del trabajo activo, y entran en el 'bucle de espera' un numero de veces
definido por 'iters'.
4. Creacin y destruccin de procesos.
subroutine mp_create(integer num)
Subrutina de creacion e inicializacion de procesos, de modo que el nmero total de
procesos activos sea el indicado por 'num'.
subroutine mp_destroy()
Subrutina que elimina todos los procesos 'esclavos'.
subroutine mp_setup()
Subrutina que crea e inicializa procesos, en un nmero que si no se especifica en
cualquier otra rutina de definicin de procesos, adopta como valor el nmero total de
CPUs de la maquina. No es necesaria; es llamada automaticamente en la entrada a la
primera regin paralela.

Dependencia de datos.
Cuando se paralelice un bucle se debe uno asegurar de que no exista dependencia de
datos (dependencia de los datos que se computan en una iteracion de los obtenidos
en las iteraciones anteriores). Esto no se debe pasar por alto en paralelizacin MP,
pues toda la responsabilidad de programacin recae sobre el usuario (por lo que este
debe estar siempre al tanto de no dar lugar a clculos erroneos). En comparacin, en
paralelizacin automtica PFA esto no es necesario, ya que el compilador se encarga
de paralelizar solo los bucles en los que esta seguro de no encontrar dependencias de
datos. Un modo fcil de saber si un bucle no tiene dependencia de datos, es que su
ejecucin sea reversible (la ltima iteracin se pueda ejecutar la primera). Por otro
lado, muchas veces es fcil eliminar la dependencia de datos en un bucle. Por ejemplo:
integer a(20,20)
k = 0
do i = 1,20
do j = 1,20
k = k + 1
a(i,j) = k
enddo
enddo
Este bucle se puede paralelizar por medio de:
integer a(20,20)
c$doacross local(i,j) share(a)
do i = 1,20
do j = 1,20
a(i,j) = 20*(i-1) + j
enddo
enddo
En general, los casos mas dificiles de paralelizar son aquellos en los que los datos se
van uniendo en una cadena de dependencias hasta el dato inicial. Sin embargo, en un
bucle anidado estas dependencias pueden deberse a un unico indice, pudiendo
paralelizar en otros indices del bucle. Por ejemplo:
c$doacross local(i,j) share(imax,jmax,a,b)
do j = 1,jmax
a(1,j) = b(i)
do i = 2,imax
a(i,j) = a(i-1,j) + b(i)
enddo
enddo
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 10/12
Finalmente, hay que tener especial cuidado con las llamadas a subrutinas externas
desde un bucle que queramos paralelizar, ya que a veces las subrutinas hace llamadas
a datos hallados en llamadas anteriores a la misma.

Algunos consejos sobre como trabajar con Fortran MP.
Una vez escrito el codigo de programa con todas las directivas MP, lo primero que hay que hacer es
correrlo en serie para asegurarnos de que no hay errores de lenguaje y los resultados del programa
son correctos. Para ello basta con compilar sin la opcin -mp, con lo que 'olvida' las directivas de
paralelizacin y crea un ejecutable que corre sin problemas sobre un procesador en serie.
Una vez que el programa en serie funciona sin errores, se debe hacer un estudio de dependencias
de datos en los bucles, para saber cuando es posible paralelizar. Aqui nos puede ser muy util la
paralelizacion automatica PFA; en los archivos de ayuda que genera el compilador (programa.w2f.f,
programa.l), se nos indican los problemas por los que no se paralelizan muchos bucles, lo que nos
puede ayudar a la hora de solventarlos. (Sin embargo hay que recordar que la paralelizacion
automatica tiene un criterio muy conservador y que muchas veces se puede llegar mas lejos de los
que esta indica; se recomienda consultar las preguntas ms frecuentes para saber mas sobre
paralelizacin automtica).
Este proceso es iterativo: se debe hacer un estudio de cada bucle y comprobar con cada
transformacion que los resultados son correctos. Este es un proceso bastante lento, por lo que se
recomienda concentrarse en las partes del programa que tardan mas tiempo, y que en realidad son
las unicas que nos van a permitir un mejor rendimiento del programa (en lo que se llama una
paralelizacion de grano grueso). Por medio de la herramienta SpeedShop y sus diversas aplicaciones
(ssusage, ssrun,...) podremos orientarnos sobre donde merece la pena paralelizar (consultense
tambien las preguntas ms frecuentes para saber ms del tema).
Se debe tener tambien en cuenta que, a veces, los resultados erroneos son debidos a una mala
definicion de las variables en nuestro programa (variables share que debian ser local, etc...). Esto
se puede comprobar comparando la version serie del programa, con la version paralela corrida
sobre un solo procesador: si los resultados no son iguales hay un problema de defincin de
variables.
Algunos Ejemplos Prcticos.
1. Paralelizacion de bucles anidados:
Paralelizacion de las unidades de trabajos mas largas, buscando el equilibrio entre procesos.
c Paralelizacion del bucle
c interno (bucle mas largo).
c No vale la pena paralelizar
c el bucle externo: la ganancia
c de tiempo se consigue con el
c interno, y se pueden dar
c problemas de equilibrio.
do 10 j = 1,5
c$doacross local(i),share(j)
do 10 i = 1,1000
b(i,j) = a(i,j)
10 continue
2. Paralelizacion de operaciones con reduccion de variables:
Una operacion de reduccion es del tipo:
A = A+B, A = A*B, A = max(A,B), A = min(A,B).
Como ya se indico, el modo mas simple de paralelizacion se limita a definir la variable A como
reduction.
c Por medio de 'reduction',
c uno se asegura de sumar una
c vez todos los elementos de 'A'
c a la variable 'red', aunque
c estemos trabajando en distintos
c procesos.
red = 0.0
c$doacross local(i,j),
c$& share(A, imax, jmax),
c$& reduction(red)
do j = 1,jmax
do i = 1,imax
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 11/12
red = red + A(i,j)
enddo
enddo
Otro modo consiste en definir una variable auxiliar de reduccion para cada uno de los
procesos en que estemos paralelizando. Este metodo esta especialmente recomendado en
bucles grandes, ya que evita un grave problema de comparticion innecesaria o false sharing.
Las variables reduction son en realidad un caso especial de variables share; se comparten en
todos los procesos, y cada vez que son modificadas deben ser actualizadas en todos ellos,
algo que no es necesario y que ralentiza enormemente el tiempo de ejecucin.
c En este programa se establece
c una variable de suma 'redj', que
c es local en cada proceso y que
c no sufre 'false sharing'. Una vez
c obtenida la suma parcial en cada
c proceso, se obtiene la suma total,
c por medio de:
c c$call mp_setlock()
c red = red + redj
c c$call mp_setunlock()
c Las funciones de sincronizacion
c 'mp_setlock' y 'mp_setunlock'
c garantizan que a la sentencia de
c suma se accede una sola vez desde
c todos los procesos, contribuyendo
c cada uno con su suma parcial 'redj'.
c Estas sentencias deben
c incluirse en el bucle que ha sido
c paralelizado, teniendo en cuenta
c que en cada proceso 'redj' es una
c variable distinta !!!!
red = 0.0
c$doacross local(i,j,redj)
do j = 1,jmax
redj = 0.0
do i = 1,imax
redj = redj + A(i,j)
enddo
c$call mp_setlock()
red = red + redj
c$call mp_setunlock()
enddo
3. Paralelizacin de bucles con entrada y salida de datos.
c$doacross solo permiten entrada y salida de datos en serie. Para ello los comandos I/O
deben estar dentro de llamadas a mp_setlock y mp_setunlock, que permiten ejecutarlos en
serie.
Empleando de nuevo el ejemplo anterior:
red = 0.0
c$doacross local(i,j,redj)
do j = 1,3
redj = 0.0
do i = 1,3
redj = redj + A(i,j)
enddo
c$call mp_setlock()
write (*,*) redj
red = red + redj
c$call mp_setunlock()
enddo
write(*,*) red
4. Paralelizacin de bucles con llamadas a funciones externas.
as llamadas a funciones externas no plantean problemas a la hora de emplearlas en paralelo
dentro de c$doacross.
27/05/14 El Rincon del Cluster
montatucluster.blogspot.mx 12/12
Pgina principal
Suscribirse a: Entradas (Atom)
Publicado por ErLuCo en 6:43 1 comentario:
Uno debe preocuparse solo de definir correctamente de que tipo son las variables incluidas
dentro de la funcion externa, para que estas sean interpretadas correctamente: (local,
share...)
La estructura general del bucle seria por tanto:
c$doacross local(i)
do i = i,imax
call work(i)
enddo
...
subroutine work(i)
...

5. Problemas con un vector o matriz local.
Un grave problema que presenta un programa Fortran es que el tamao de un vector o
matriz debe ser especificado de manera explicita en el cdigo, al inicio del programa o
subprograma en que se aplica. No se puede emplear para definir el tamao una variable no
especificada o un valor definido en un bloque common.
Esto obliga a hacer vectores y matrices de gran tamao, que en genera pueden quedar
semivacios, pero que garantizan la validez del programa cuando el numero de elementos que
se precisan se hace muy grande.
c Suma de vectores
subroutine vector(A,B,C)
dimension A(100), B(100), C(100)
c$doacross local(i), share(A,B,C)
do i = 1,100
C(i) = A(i) + B(i)
enddo
return
end
c No es valido hacer lo mismo con
c el siguiente codigo, cuando el
c valor de 'n' no esta especificado
c en el codigo.
subroutine vector(A,B,C,n)
dimension A(n), B(n), C(n)
c$doacross local(i), share(A,B,C,n)
do i = 1,n
C(i) = A(i) + B(i)
enddo
return
end

+1 Recomendar esto en Google
Plantilla Simple. Imgenes de plantillas de enot-poloskun. Con la tecnologa de Blogger.

You might also like