Paradigmas de Computación Paralela, Concurrente y Distribuida

Anuncio
Paradigmas de
Computación Paralela,
Concurrente y Distribuida
Dra. Veronica Gil-Costa
e-mail: [email protected]
[email protected]
Librerías
Librerías de Paso de Mensajes:
PVM
MPI
BSPonMPI
Paso de Mensajes
Fundamentalmente, los procesos se
componen de código secuencial al que se le
han incluido funciones o rutinas para:
Enviar mensajes
Recibir mensajes
Las operaciones de envío se han de
corresponder con las de recepción.
Posibilidad de crear “deadlocks”.
Librerías
Librerías de Paso de Mensajes:
PVM
Configuración
Gestión de procesos
Envío y Recepción de Mensajes
Grupos de procesos
QUE ES LO NUEVO ?
PVM es un programa que permite
conectar máquinas
heterogéneas, tanto paralelas
como secuenciales, para que
trabajen como un recurso
computacional único.
5
PVM: Parallel Virtual Machine
Software que permite que un conjunto
heterogéneo de computadores aparezca
como un único computador paralelo de
memoria distribuida.
Proporciona las funciones necesarias para
crear, comunicar y sincronizar procesos
sobre la máquina virtual.
PVM
Se compone de dos partes:
pvmd3 (daemon):Reside en todos los
computadores que componen la máquina virtual.
libpvm3.a (librería):Contiene las rutinas de
paso de mensajes, gestión de procesos, gestión
de la máquina virtual, etc. Los programas deben
“linkarse” con esta librería.
EL DEMONIO
‘pvmd’
»
debe estar corriendo en todas las
computadoras que constituyan la máquina virtual.
»
Un usuario que desea ejecutar una aplicación PVM
ejecuta primero el demonio en una única máquina, y
éste se encarga de activar el resto de demonios de las
máquinas que forman la PVM
8
MAQUINA VIRTUAL PARALELA
Estación de trabajo
pvmd
Prog
(.exe
)
pvmd
pvmd
Prog
(.exe
)
Estación de trabajo
Prog
(.exe
)
Estación de trabajo
9
PVM
Los programas se componen de un programa
“master” y otros programas “esclavos”.
Tanto el “master” como los “esclavos” son
programas ejecutables, compilados y linkados
independientemente.
Cuando se pone a ejecutar el programa “master”
creará las activaciones de los “esclavos”.
Cada proceso recibe un identificador (“tid”).
Configuración
La Máquina virtual puede configurarse
desde la consola de PVM o directamente
en tiempo de ejecución con llamadas a las
funciones adecuadas.
Configurar la máquina virtual significa
poner en marcha los “daemons” en todas
aquellas máquinas que vayamos a utilizar
Configuración
Al arrancar la consola de PVM (pvm) se pone en
marcha (si no lo estaba) el daemon en la
máquina correspondiente.
$> pvm
Una vez dentro de la consola (pvm>) se puede:
Añadir máquinas: add host
Eliminar máquinas: delete host
Consultar configuración: conf
$> pvm hostfile
Funciones de la consola
quit: Permite salir de la consola y volver al
sistema operativo, dejando los “daemons” en
funcionamineto.
halt: Cierra PVM, matando todos los procesos
PVM, incluidos los “daemons”.
ps -a: Lista los procesos que están ejecutándose
en la máquina virtual.
spawn: Inicia una aplicación PVM.
Ejecución de una aplicación
Cualquier aplicación PVM puede ejecutarse
desde la consola con el comando spawn
pvm> spawn master
Ejemplo
master.c
worker.c
#include <stdio.h>
#include “pvm3.h”
#include<stdio.h>
#include“pvm3.h”
main()
{
int mytid, tids, a=3, resul;
mytid= pvm_mytid();
pvm_spawn(“worker”,NULL,1,
”lidic01”,1, &tids);
pvm_initsend (PvmDataRaw);
pvm_pkint (&a, 1, 1);
pvm_send(tids, 0);
pvm_recv (tids, 0);
pvm_unpkint (&resul, 1, 1);
pvm_exit();
}
main()
{
int mytid, master, x;
mytid= pvm_mytid();
master = pvm_parent();
pvm_recv(master, 0);
pvm_unpkint(&x, 1, 1);
x=x*2;
pvm_initsend(PvmDataRaw);
pvm_pkint(&x, 1, 1);
pvm_send(master, 0);
pvm_exit();
}
Ejemplo
main ( )
{ struct pvmhostinfo *hostp ;
int result , check , i , nhost , narch , stid ;
char buf [ 64 ] ;
PVMcommunMaster.c
gethostname ( buf , 20) ; / / get master ’ s name
printf ("The master process runs on %s \n" , buf ) ;
//get & display parallel machine configuration
pvm_config ( &nhost , &narch , &hostp ) ; // getconfiguration
printf("I found following hosts in your virtual machine\n") ;
for( i = 0 ; i < nhost ; i ++)
printf ("\t%s\n" , hostp[ i ].hi_name ) ;
for ( i =0; i<nhost ; i++) / / spawn processes
{ check=pvm_spawn ("answer" , NULL , 1 , hostp[ i ].hi_name , 1 , &stid );
if ( ! check ) printf ("Couldn’t start on %s\n" , hostp[ i ].hi_name ) ;
}
result =0;
while ( result <nhost )
{ pvm_recv (-1 , 2) ; /¤ wait for reply message ¤/
pvm_upkstr ( buf ) ; /¤ unpack message ¤/
printf ("%s\n" , buf ) ; /¤ print contents ¤/
result ++;
}
pvm_exit ; /¤ we are done ¤/
}
Ejemplo
#include<stdio.h>
#include <pvm3.h>
PVMcommunSlave.c
#include <time.h>
main ( )
{
time_t now ;
char name [ 12 ] , buf [ 60 ] ;
int ptid ;
ptid = pvm_parent ( ) ; /¤ the ID of the master process ¤/
gethostname ( name , 64) ; /¤ find name of machine ¤/
now= time (NULL) ; /¤ get time ¤/
strcpy ( buf , name ) ; /¤ put name into string ¤/
strcat ( buf , "time is ") ;
strcat ( buf , ctime(&now) ) ; /¤ add time to string ¤/
pvm_initsend ( PvmDataDefault ) ; /¤ allocate message buffer ¤/
pvm_pkstr ( buf ) ; /¤ pack string into buffer ¤/
pvm_send ( ptid , 2) ; /¤ send buffer to master ¤/
pvm_exit ; /¤ slave is done and exits ¤/
}
Compilar
cc -o answer PVMcommunSlave.c -lpvm3
cc -o master PVMcommunMaster.c -lpvm3
Para ejecutar $> master
Primitivas de control de procesos
pvm_mytid(): Esta función retorna un valor
entero que corresponde al identificador del
proceso que la llama.
Primitivas de control de procesos
pvm_spawn(char *task, char**arg,int flag, char
*where, int ntasks, int *tids)
task= nombre del ejecutable esclavo
arg = argumentos que se le pasan al esclavo
flag =
PvmTaskDefault (0) se elije cualquier máquina para ejecutar
el esclavo
PvmTaskHost (1) el parámetro where especifica el host
donde ubicar el proceso esclavo
ntasks = número de copias del ejecutable task
where = nombre del host donde se ejecuta el slave
tids = arreglo de enteros de longitud ntask con los tids de los
procesos PVM creados.
Devuelve un valor entero con el número de procesos creados.
Primitivas de control de procesos
pvm_initsend( int encoding ):
Codificación:
PvmDataDefault Utiliza un tipo de codificación que se
denomina XDR
PvmDataRaw No utiliza codificación
pvm_pktipo(tipo*np,int nitem,int stride): Empaqueta
Crea un buffer vacío que pasa a ser el buffer activo y especifica el
tipo de codificación
nitem componentes del array de elementos apuntado por np,
tomando los elementos en un paso stride.
pvm_pkbyte
pvm_pkint
pvm_pkdouble
pvm_pkstr (no necesita los argumentos nitem o stride)
Primitivas de control de procesos
pvm_send (int tid, int msgtag)
Envía el contenido del buffer activo al proceso
identificado por tid. El mensaje es etiquetado con un
entero especificado en msgtag.
pvm_mcast (int *tids, int task, int tag)
Envía el mismo mensaje a ntask procesos
identificados por los identificadores tids.
Si tid = -1 recibe desde cualquier proceso.
Primitivas de control de procesos
pvm_recv (int tid, int msgtag)
El proceso queda bloqueado hasta que recibe un
mensaje procedente del proceso tid con etiqueta
msgtag(-1 wildcard).
pvm_nrecv (int tid, int msgtag)
El proceso no se bloquea. Si hay un mensaje lo toma,
sino sigue adelante.
Primitivas de control de procesos
pvm_upktipo(tipo*np, int nitem, int stride)
Desempaqueta el mensaje del buffer activo sobre el
array especificado.
El formato debe encajar con el formato de los datos
enviados.
Se pueden empaquetar varios datos en un mismo
mensaje, siempre y cuando se desempaqueten en el
mismo orden.
Primitivas de control de procesos
pvm_addhosts (char**hosts,int nhosts, int*infos)
Añade nhosts a la máquina virtual indicados por el
array de cadenas hosts.
pvm_delhosts(char**hosts,int nhosts, int*infos)
Elimina nhosts de la máquina virtual.
Grupos de procesos
Sobre el núcleo de PVM se han introducido unas
primitivas que permiten gestionar grupos
dinámicos de procesos.
Estas primitivas permiten enviar mensajes a un
grupo de tareas, distribuir datos, sincronizar, etc.
Para utilizar estas primitivas se debe añadir una
librería separada (libgpvm3.a) que debe linkarse
con cada uno de los procesos.
Grupos de procesos
pvm_joingroup (char *group)
Añade el proceso que llama a la función al grupo
especificado. Cada proceso que se incorpora a un grupo
recibe un número de instancia, que representa el número
del proceso dentro del grupo.
pvm_lvgroup (char *group)
El proceso que llama a la función abandona el grupo en
cuestión.
pvm_gsize (char *group)
La función devuelve el número de procesos que hay en el
grupo.
Grupos de procesos
pvm_barrier (char *group, int count)
Se bloquean todos los procesos del grupo group que llegan
a la barrera hasta que count-procesos han llegado a la
barrera. Una vez que count-procesos han llegado a la
barrera, todos siguen ejecutándose normalmente.
pvm_bcast (char *group, int msgtag)
Envía el contenido del buffer activo a todos los procesos
pertenecientes al grupo especificado.
Grupos de procesos
pvm_scatter (void *result, void*data, int count,
int datatype, int tag, char*group, int rootginst)
El miembro del grupo rootginst distribuye una porción
de count-elementos del array data entre todos los
miembros del grupo, quedándose él mismo su parte
proporcional.
Los datos se reciben en los arrays result de cada
miembro del grupo.
Todos los procesos del grupo
deben
llamar
a laenfunción
Los tipos
de datos
datatype
PVM
scatter.
pueden ser PVM_BYTE , PVM_SHORT
PVM_INT, PVM_FLOAT PVM_DOUBLE,
PVM_STR
Grupos de procesos
pvm_gather(void *result, void*data, int count,
int datatype, int tag, char*group, int rootginst)
El miembro del grupo rootginst recoge en el array
result todas las componentes de los array data que le
son enviadas por los restantes miembros del grupo.
Cada miembro envía count-datos, incluido él mismo.
Todos los procesos del grupo deben llamar a la
función.
Grupo de procesos
pvm_reduce (void (*func)(), void *data, int
count, int datatype, int msgtag, char *group, int
root)
Todos los miembros del grupo llaman a pvm_reduce
con sus datos locales en “data” y el resultado de la
El resultado de la
operación reduce aparece en el resultado
del proceso
operación
se
root.
almacena en el dato
Funciones de reducción provistas por
PvmMax, PvmSum, PvmProduct.
local del proceso
root. Es decir el dato
“data”PvmMin,
del proceso
MPI:
root se sobre
escribe con el
resultado de la
operación result.
Ejemplo
Ejemplo
Rutina de
sincronización que
debe ser llamada por
todos los miembros
del grupo. La
información del
grupo es cacheada
por todos los
miembros.
MPI
MPI es una especificación de una librería de paso
de mensajes pensada para computadores
paralelos, clusters y redes heterogéneas.
Los mensajes son datos que se pasan entre
procesos en un entorno de memoria distribuida.
Memoria distribuida: Cada procesador tiene su
memoria local y no puede acceder directamente a
la memoria de los demás procesadores
MPI
Edición de un programa
Compilar y Linkar
Ejecución de programas MPI
PROCESOS (SPMD)
#include “mpi.h”
Definición de
Funciones MPI
main(int argc, char,**argv)
{
int nproc, mytid;
Comunicador
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);
MPI_Comm_Rank(MPI_COMM_WORLD, &mytid);
/* cuerpo de programa */
MPI_Finalize();
}
0<=mytid<nproc
36
PROCESOS (Master/Slave)
#include “mpi.h”
main(int argc, char,**argv)
{
int nproc, mytid;
MPI_Init(&argc,&argv);
MPI_Comm_size(MPI_COMM_WORLD, &nproc);
MPI_Comm_Rank(MPI_COMM_WORLD, &mytid);
if (mytid ==0)
master( );
else
MIMD: Debe hacerse
slave( );
MPI_Finalize();
}
explícitamente
37
MPI
mpicc–o hola hola.c
mpif77 –o holaf holaf.f
Makefile
mpirun–np 4 hola
MPI
Un “Communicator” es un grupo de procesos que
puede comunicarse entre sí.
Todos las funciones de comunicación tienen un
parámetro que es el comunicador.
El comunicador más común es
MPI_COMM_WORLD
Se define cuando se llama a MPI_Init.
Contiene a todos los procesos de la aplicación.
MPI: Comunicadores
Un comunicador: es una colección de procesos, los cuales se
comunican a través del envío y recepción de mensajes.
40
MPI
#include “mpi.h”
#include <stdio.h>
int main (int argc, char** argv)
{
int rank, size;
MPI_Init (&argc, &argv);
MPI_Comm_rank (MPI_COMM_WORLD, &rank);
MPI_Comm_size (MPI_COMM_WORLD, &size);
printf(“Hola mundo, Yo soy %d de %d\n”, rank, size);
MPI_Finalize ();return 0;
}
MPI: Tipos de Datos
MPI: Comunicación punto a punto
Comunicación entre dos procesos.
El proceso origen envía un mensaje al proceso
destino.
El proceso destino recibe el mensaje.
La comunicación se realiza dentro de un
comunicador.
El proceso destino se identifica por su rango
dentro del comunicador.
MPI
MPI_Send (void *buf, int count,
MPI_Datatype datatype, int dest, int tag,
MPI_Comm comm)
MPI_Send (data, 500, MPI_FLOAT, 6, 33, MPI_COMM_WORLD)
Envía 500 datos en punto flotante almacenados a partir de la
dirección data al proceso 6 dentro del comunicador
MPI_COMM_WORLD con una etiqueta 33.
MPI
MPI_Recv (void *buf, int count,
MPI_Datatype datatype, int source, int tag,
MPI_Comm comm, MPI_Status *status)
MPI_Recv (value, 500, MPI_FLOAT, MPI_ANY_SOURCE,
MPI_ANY_TAG, MPI_COMM_WORLD, &status)
Recibe 500 datos en punto flotante y los almacena a
partir de la dirección value de cualquier proceso
dentro del comunicador MPI_COMM_WORLD con
cualquier etiqueta.
MPI
WARNING! Ambos send y recv son
bloqueantes
MPI_Recv: retorna el control luego que el
buffer tiene los datos requeridos
MPI_Send: se bloquea hasta que los datos
son recibidos
Ejemplo Deadlocking
#include <mpi.h>
#include <stdio.h>
int main(int argc, char **argv) {
int me, np, q, sendto;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &np);
MPI_Comm_rank(MPI_COMM_WORLD, &me);
if (np%2==1) return 0;
if (me%2==1) {sendto = me-1;}
else {sendto = me+1;}
MPI_Recv(&q, 1, MPI_INT, sendto, sendto, MPI_COMM_WORLD, &status);
MPI_Send(&me, 1, MPI_INT, sendto, me, MPI_COMM_WORLD);
printf(“Sent %d to proc %d, received %d from proc %d\n”, me,
sendto, q, sendto);
MPI_Finalize();
return 0;
}
Ejemplo Seguro
#include <mpi.h>
#include <stdio.h>
int main(int argc, char **argv) {
int me, np, q, sendto;
MPI_Status status;
MPI_Init(&argc, &argv);
MPI_Comm_size(MPI_COMM_WORLD, &np);
MPI_Comm_rank(MPI_COMM_WORLD, &me);
if (np%2==1) return 0;
if (me%2==1) {sendto = me-1;}
else {sendto = me+1;}
if (me%2 == 0) {
MPI_Send(&me, 1, MPI_INT, sendto, me, MPI_COMM_WORLD);
MPI_Recv(&q, 1, MPI_INT, sendto, sendto, MPI_COMM_WORLD, &status);
} else {
MPI_Recv(&q, 1, MPI_INT, sendto, sendto, MPI_COMM_WORLD, &status);
MPI_Send(&me, 1, MPI_INT, sendto, me, MPI_COMM_WORLD);
}
printf(“Sent %d to proc %d, received %d from proc %d\n”, me, sendto,
q, sendto);
MPI_Finalize();
return 0;
}
MPI
Modos
de comunicación dependiendo
de:
Bloqueante o no bloqueante
Modo Básicos
Bloqueante
MPI_Send MPI_Recv
No bloqueante MPI_Isend MPI_Irecv
Condición
de finalización
Síncrono MPI_Ssend
Buffered MPI_Bsend
Ejemplo
se bloquea hasta que un numero especifico de send y
recv no bloqueantes se completan
MPI
Comunicaciones colectivas
Barrier synchonization
Broadcast
Scatter
Gather
Operaciones de reducción
MPI
Todos los procesos se detienen cuando
van llegando a la barrera hasta que llega
el último de ellos.
MPI_Barrier (MPI_Comm comm)
MPI: Bcast
Es una comunicación desde un origen a todos los
miembros del comunicador.
Los mismos datos se envían a todos los destinos.
Todos los procesos ejecutan el broadcast, pero
sólo el proceso que se indica como origen realiza
el envío.
MPI_Bcast (void *buffer, int count, MPI_Datatype
datatype, int root, MPI_Comm comm)
MPI: Bcast
MPI: Scatter
Es una distribución de datos desde un origen a
todos los miembros del comunicador.
datos a ser enviados
MPI_Scatter (void *sendbuf,
int sendcnt,
datos a ser enviados
MPI_Datatype sendtype, Tipo de elem. a enviar
void* recvbuf,
Datos a recibir
int recvcnt,
MPI_Datatype recvtype,
int root,
MPI_Comm comm)
MPI: Gather
Es una recolección de datos desde todos los
miembros del comunicador a un destino.
La recolección se realiza según el orden de rango
en el comunicador.
MPI_Gather (void *sendbuf,
int sendcnt,
MPI_Datatype sendtype,
void* recvbuf,
Datos que se
int recvcnt,
reciben
MPI_Datatype recvtype,
int root,
MPI_Comm comm)
Datos que se
envían
MPI: Reduce
Se realiza una operación determinada con las
variables x de todos los procesos del
comunicador y se almacena el resultado en el
proceso 0.
MPI_Reduce(void* sendbuf,
void* recvbuf,
int count,
MPI_Datatype datatype,
MPI_Op op,
int root,
MPI_Comm comm);
MPI: Operaciones Reduce
MPI: Allreduce
Aplica la operación reduce sobre todos los
procesos involucrados
MPI_Allreduce(void* sendbuf,
void* recvbuf,
int count,
MPI_Datatype datatype,
MPI_Op op,
MPI_Comm comm);
MPI
MPI: AlltoAll
Envía un mensaje diferente de cada
proceso a cada otro proceso
MPI_Alltoall(void* sendbuf,
int sendcount,
MPI_Datatype sendtype,
void* recvbuf,
Datos que se
int recvcount,
reciben
MPI_Datatype recvtype,
MPI_Comm comm)
Datos que se
envían
Cada proceso recibe del proceso
k un elemento y lo almacena en
el buffer recvbuf en la posición k
MPI: Wait
Modo Básico
Espera a que una operación no bloqueante se
complete.
MPI_Wait(MPI_Request
*request,MPI_Status *status);
Toma como entrada un “recibo” (tipo de
operación que quiere que se complete) y se
bloquea hasta que la operación haya finalizado.
Hacer un Isend + Wait = send bloqueante. Sin
embargo entre la llamada al Isend y la llamada al
Wait puede hacer otras cosas útiles, logrando
solapar computo y comunicación
MPI: Test
Modo Básico
Verifica si la operación no bloqueante ha
finalizado
MPI_Test(MPI_Request *request,
int *flag,
MPI_Status *status);
Cuando no interesa bloquearse, sino simplemente
saber si la operación terminado, se usa
MPI_test().
Esta función actualiza un flag que se le pasa como
segundo parametro. Si la operación ha finalizado
flag =1, sino flag =0
MPI: Cancel
Modo Básico
Cancela una operación de comunicación
pendiente, siempre que aún esta no se
haya completado
MPI_Cancel(MPI_Request *request)
MPI
Modo Buffer
Uno de los problemas que tiene el modo
básico de comunicación es que el
programados no sabe cuanto va a tardar
en completarse la operación.
Para evitar el riesgo de un bloqueo no
deseado se puede solicitar que el mensaje
se copie en un buffer y que se de por
finalizada la operación.
MPI
Modo Buffer
Soluciona los problemas de bloqueo en la
comunicación modo básica
El programador debe asignar un buffer de
salida de forma estática o con malloc.
MPI_Buffer_attach(void* buffer,int size);
MPI_Buffer_detach(void* buffer,int *size);
MPI: Recepción por encuesta
Cuando estamos a la espera de mensajes
de diferente clase: A, B y C. Cada una
asociada a un tipo de dato diferente y la
clase nos viene dado por el valor de la
etiqueta. Por lo tanto nos gustaría saber el
valor de la etiqueta antes de procesarlo.
Cuando llega un mensaje de longitud
desconocida y necesitamos averiguar el
tamaño para asignar memoria
dinámicamente
MPI: Recepción por encuesta
MPI_Iprobe(int source,
int tag,
MPI_Comm comm,
int *flag,
MPI_Status *status);
MPI_Probe(int source,
int tag,
MPI_Comm comm,
MPI_Status *status);
BSPonMPI
Implementa las rutinas estándar de BSPlib
y las ejecuta sobre una plataforma con
MPI
Actualmente existen dos
implementaciones de BSPlib:
BSPPUB
BSP Oxford
Modelo Bulk Synchronous Parallel
Propuesto por L. Valiant - 1990.
Sus principales objetivos son:
Simplicidad
Portabilidad
Predictibilidad
Sus características son:
Red de comunicación Abstracta
Modelo de costo de comunicación y sincronización independiente.
Aplicable por cualquier software o herramienta paralela(MPI, PVM..)
BSP
Red de Comunicación
Memoria0
Procesador0
Memoriap
...
Procesadorp
Un conjunto de pares (procesador-memoria)
Una red de comunicación (mensajes punto a
punto)
Un mecanismo de sincronización de procesadores
BSP
Características Generales
Una computadora BSP está caracterizada por:
Ancho de banda de la red de Interconexión
Número de procesadores
Velocidad de procesadores
Tiempo de sincronización
Una computadora BSP es un modelo de memoria de dos niveles
¿ Qué computadoras reales son computadoras BSP ?
Computadoras con un único procesador
Redes de estaciones de trabajo (PC conectadas con una librería de pasaje de mensaje: MPI, PVM..)
Computadoras paralelas con Memoria Compartida o Memoria Distribuida.
BSP
Un Superpaso implica:
Fase 1: Computación Local Independiente
Fase 2: Fase Global de Comunicaciones
Fase 3: Sincronización
¿Qué ocurre con las comunicaciones realizadas en la Fase 1?
Toda comunicación debe ser No Bloqueante.
Toda solicitud de datos remotos es resuelta en la Fase 2.
Todo dato remoto solicitado está disponibles después de la Fase 3.
BSP: Superpaso
Superpaso
P1
P2
P3
P4
Fase 1
Tiempo
Superpaso
P0
Fase 2
Fase 3
Sincronización
BSP
El Modelo de análisis de Costo es Muy Sencillo
Para predecir el comportamiento de los algoritmos es necesario Normalizar los
parámetros respecto a la velocidad de los procesadores y en consecuencia:
Predecir el comportamiento sobre distintos sistemas
Un Step: Una unidad de computación
Generalmente: El tiempo requerido en realizar una operación de punto flotante
Entonces:
Velocidad de Procesador = K steps /seg
BSP
Propiedad Bulk:
Para el análisis de un programa paralelo es Imposible considerar las distintas
interacciones de comunicación que tienen lugar:
Comunicaciones Send o Receive
Acceso directo a Memoria Remota (DRMA)
La solución es:
Comunicaciones en Masa
BSP lo logra y considera toda acción que involucra una comunicación como una unidad
Modelo BSP
Una computadora BSP queda caracterizada por la 4-upla:
(P, s, L, g)
Donde:
P es el número de procesadores
s la velocidad de los procesadores
L el costo, en pasos, de realizar una sincronización
g el costo, en palabras, de entregar un mensaje
Como s es el factor de normalización, el modelo
queda caracterizado por P, L y g.
Modelo BSP
BSP
L representa el costo de la sincronización por barrera requerido al final de todo
superpaso.
L es tiempo transcurrido entre dos sincronizaciones continuas.
Toda sincronización por barrera es costosa debido a:
Distintas duraciones del superpaso en cada procesador.
El costo de lograr el estado de consistencia global en todos los
procesos.
BSP
L
y g depende de:
L
Bandwidth de la topología de la red de
comunicación.
El protocolo usado por la red.
La administración de los buffer (por los
procesadores y la red)
Routing en la red.
depende, además
Hardware de sincronización por barrera
BSP
El costo BSP de un superpaso s es:
Máximo Costo de computación local + Costo de comunicaciones
Ws + CSs
donde
Ws = max{ wsi / i∈ { 0, . . ., p-1}
CSs = el tiempo insumido en las comunicaciones, C, y en la sincronización, S, por el
superpaso s.
¿ Cómo se determina el costo de las comunicaciones?
BSP
El
costo de las comunicaciones está ligado a la cantidad
de mensajes que se comunican.
¿Cómo se mide la masa csi comunicada por el procesador i?
En un superpaso, cada procesador pi
Envía un número de mensajes outsi
Recibe un número de mensajes insi
Si las entradas y salidas son en secuencia ⇒
csi = insi + outsi
Si las entradas y salidas son en paralelo ⇒
csi = max { insi, outsi}
BSP
El procesador ds que mas comunica es aquel que:
hs = max { insi @ outsi / i∈ { 0, . . ., p-1}}
Todo superpaso tiene asociada una h-relación (h= 0, 1, 2, ...) si el patrón de
comunicación en el superpaso s es tal que hs = h.
BSP: Costo
Considerando una computadora BSP y una arquitectura H existen constantes gH y LH tal que
el tiempo insumido en las comunicaciones de un superpaso s es una función lineal del
tamaño hs de la h-relación asociada al superpaso
CSH,s (hs) = gH * hs + LH
El tiempo de un superpaso s puede aproximarse por
ts ≈ Ws +gH * hs + LH
y el costo total de un programa BSP con R superpasos es
TR = Σs = 0,..,R ( ts)
BSP
Para una buena performance de algoritmos BSP se debe:
Equilibrar la computación w entre los distintos procesadores.
Equilibrar las comunicaciones entre los diferentes procesadores.
Minimizar el número de Superpasos.
Librerías
La filosofía BSP puede ser expresada por una amplia variedad
de lenguajes de programación y de librerías.
Un programa BSP puede ser escrito utilizando alguna librería de
comunicación existente tal como PVM o MPI.
Condición: La librería debe proveer mecanismo de
comunicación no bloqueantes y sincronizaciones por barreras.
Un programa BSP es desarrollado en algún lenguaje
secuencial (Fortran o C) e incluye la librería que provee la
funcionalidad de BSP.
El enfoque mas común para la programación BSP es SPMD.
Librerías mas comunes:
BSPLib
Oxford BSP
BSPpub
BSPonMPI
BSPonMPI
Pocas instrucciones
bsp_sync()
bsp_put()
bsp_get()
bsp_send()
mpicc -o example example.c -lbsponmpi
mpirun -np 4 example
BSPonMPI
Funciones de Inicializacion
Funciones de Informacion
bsp_init()
bsp_begin()
bsp_end()
bsp_pid()
bsp_nprocs()
bsp_time()
Funciones de Sync
bsp_sync()
BSPonMPI: bsp_init
Inicialización de los procesos paralelos.
Realiza la operación spawn
bsp_init(void (*startproc)(void),
int argc,
char ** argv);
BSPonMPI: bsp_begin bsp_end
Se crean a lo más maxprocs procesos
bsp_begin(maxprocs)
/*…código …*/
bsp_end()
Ejemplo
Ejemplo
BSPonMPI:
bsp_pid(): determina el identificador del
proceso
bsp_nprocs(): determina el número total
de procesos BSP.
bsp_time(): entrega el tiempo de
ejecución de un proceso.
bsp_sync(): sincroniza los procesos.
Ejemplo
BSPonMPI
Funciones
DRMA
bsp_pushregister()
bsp_popregister()
bsp_put()
bsp_get()
BSPonMPI
Delcarar que una dirección de memoria
puede ser accedida por otros procesos
bsp_push_reg(buff, size)
Declarar que una dirección de memoria no
puede ser accedida por otros procesos
bsp_pop_reg(buff)
BSPonMPI
Escribir en la memoria de otro proceso
bsp_put(int pid, const void *src,
void *dst, int offset,
int nbytes);
Ejemplo
BSPonMPI
Buscar datos desde una memoria remota
de otro proceso
bsp_get(int pid, const void *src,
int offset, void *dst, int nbytes);
Ejemplo
Ejemplo
P0
P1
P2
P3
1
4
2
3
1
5
6
5
1
4
7
10
left
right
x
Ejemplo 2
BSPonMPI: Paso de Mensajes
Funciones BSMP
bsp_set_tag_size()
bsp_send()
fetch from queue
bsp_qsize()
match tag with message
bsp_move()
send to remote queue
bsp_get_tag()
choose tag size
Number of messages
Funciones Halt
bsp_abort()
one process halts all
BSPonMPI
Enviar un mensaje por la red
bsp_send(int pid,
const void *tag,
const void * payload,
int payload_bytes);
BSPonMPI
Permitir al programador asignar un tamaño
específico al tag
bsp_set_tag_size(int *tag_bytes)
Para recibir un mensaje se debería utilizar
bsp_get_tag + bsp_move
bsp_get_tag(int *status, void *tag);
El argumento status tiene valor -1 si no se ha
recibido ningún mensaje. En caso contrario tiene
el tamaño del primer mensaje en el buffer
BSPonMPI
Copiar el primer mensaje del buffer y sacarlo
del buffer
bsp_move(void *payload, int reception_bytes)
Para verificar cuántos paquetes han llegado
bsp_qsize(int *packets, int *accum_nbytes);
Ejemplo
Ver Ejercicios BSP
Benchmark
Benchmark
Descargar