Paradigmas de Computación Paralela, Concurrente y Distribuida Dra. Veronica Gil-Costa e-mail: gvcosta@unsl.edu.ar ggvcosta@gmail.com 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