Laboratorio de Paralelismo 3 Programacin paralela MPI Introduccin
Laboratorio de Paralelismo 3. Programación paralela: MPI Introducción. Funciones MPI básicas. Otros modos de envío y recepción. Comunicación en grupo. Tipos de datos derivados, empaquetado. Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Laboratorio de Paralelismo IF – UPV/EHU
Introducción. Funciones MPI básicas. Otros modos de envío y recepción. Comunicación en grupo. Tipos de datos derivados, empaquetado. Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Informatika Fakultatea UPV/EHU
MPI Introducción 3 Paralelizar una aplicación para un sistema de memoria compartida SMP no es muy “complejo”, ya que la comunicación entre procesos es sencilla, aunque hay que • especificar bien el tipo de las variables: privada, shared… • sincronizar el uso de las variables compartidas. Sin embargo, el número de procesadores/cores de un sistema SMP no suele ser muy grande, por lo que no es fácil conseguir altos niveles de paralelismo. Informatika Fakultatea UPV/EHU 3
MPI Introducción 3 Pero es relativamente sencillo conseguir una máquina paralela tipo cluster con muchos procesadores, uniendo P máquinas independientes mediante una red de conexión estándar. Sin embargo, programar aplicaciones para sistemas de memoria privada es más complejo. Recuerda: • la memoria de cada procesador es de uso privado, por lo que todas las variables son, por definición, privadas. • la comunicación entre procesos debe hacerse a través de paso explícito de mensajes. • la red de comunicación juega un papel importante en el rendimiento del sistema. Informatika Fakultatea UPV/EHU 4
MPI Introducción 3 El estándar actual de programación mediante paso de mensajes es MPI (message-passing interface), una librería grande de funciones de comunicación entre procesos (para C y Fortran). PVM → MPI 1. 0 (94) → MPI 2. 0 (97) 2. 1 (08) 2. 2 (09) → MPI 3. 0 (13) El modelo básico de paralelismo que implementa MPI es SPMD (Single Program Multiple Data), diferenciando las tareas en base al identificador de cada proceso (pid) if (pid == 1) else if (pid == 2) Informatika Fakultatea UPV/EHU ENVIAR_a_pid_2 RECIBIR_de_pid_1 5
MPI Introducción 3 6 De manera general, MPI gestiona los procesos (número y asignación) de manera estática (aunque también permite gestión dinámica). La comunicación entre procesos puede hacerse de formas muy diferentes. Elegiremos una determinada estrategia en función de la longitud de los mensajes, de la estructura del programa. . . Ten en cuenta que la eficiencia en la comunicación va a ser determinante en el rendimiento del sistema, sobre todo en aplicaciones en las que la comunicación es intensa (paralelismo de grano medio/fino). Informatika Fakultatea UPV/EHU
MPI Introducción 3 nodo-0 -0 nodo-0 -63 … sids 14. si. ehu. es gigabit ethernet Recuerda: ethernet - switch / conmutación de paquetes - formato de paquetes check 4 bytes datos < 1, 5 Kbytes tipo 2 bytes - direcciones IP: 32 bits (4 × 8) - direcciones del NIC (MAC, 48 bits) Informatika Fakultatea UPV/EHU @orig 6 bytes @dest cabecera 6 bytes 8 bytes 7
Introducción. Funciones MPI básicas inicio y control de procesos envío y recepción de mensajes Otros modos de envío y recepción. Comunicación en grupo. Tipos de datos derivados, empaquetado. Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Informatika Fakultatea UPV/EHU
MPI Funciones básicas 3 9 Aunque MPI consta de bastantes más de 300 funciones, el núcleo básico lo forman solo 6: 2 de inicio y finalización del programa 2 de control del número de procesos 2 de comunicación Sintaxis: MPI_Funcion (…) constantes de MPI, en mayúsculas #include <mpi. h> [ un * en un parámetro de la función indica que se pasa por referencia (la dirección correspondiente). ] Informatika Fakultatea UPV/EHU
MPI Funciones básicas 3 Los parámetros de las funciones MPI se clasifican en tres categorías: IN la función lee el argumento OUT IN/OUT la función modifica el argumento la función lee y modifica el argumento Las funciones MPI (casi todas) devuelven un entero como código de error = MPI_Funcion (. . . ) Si no ha habido problemas, MPI_SUCCESS (0 en esta implementación); en general, el código devuelto, que indica el tipo de error, depende de la implementación. Informatika Fakultatea UPV/EHU 10
MPI Funciones básicas 3 1. Comienzo y final del programa MPI_Init (&argc, &argv) MPI_Finalize ( ) Estas dos funciones son la primera y última función MPI que deben ejecutarse en un programa. No se pueden utilizar funciones MPI antes de MPI_Init, y si un proceso no ejecuta MPI_Finalize el programa queda como “colgado”. Informatika Fakultatea UPV/EHU 11
MPI Funciones básicas 3 Los procesos se agrupan en conjuntos denominados comunicadores (grupo de procesos entre los cuales es posible el intercambio de mensajes). El comunicador MPI_COMM_WORLD (un objeto de tipo MPI_COMM) se crea al inicio y engloba a todos los procesos. Cada proceso se identifica mediante dos parámetros: el comunicador y su pid en ese comunicador. En algunos casos es útil definir otros grupos de procesos o comunicadores para facilitar la comunicación. Por tanto, un proceso puede tener más de un pid: uno por cada grupo del que forme parte. Informatika Fakultatea UPV/EHU 12
MPI Funciones básicas 2. Identificación de procesos MPI_Comm_rank (comm, *pid) Devuelve en pid (int) el identificador del proceso dentro del comunicador comm especificado. Recuerda que un proceso se identifica mediante dos parámetros: identificador y comunicador. MPI_Comm_size (comm, *npr) Devuelve en npr (int) el número de procesos del comunicador especificado. Informatika Fakultatea UPV/EHU 3 13
Funciones básicas MPI 3 Un ejemplo simple #include <stdio. h> #include <mpi. h> main (int argc, char *argv[]) { int pid, npr, A = 21; MPI_Init (&argc, &argv); MPI_Comm_size (MPI_COMM_WORLD, &npr); MPI_Comm_rank (MPI_COMM_WORLD, &pid); A = A + 1; printf (“Proceso P%d de %d activado. A = %d n”, pid, npr, A); MPI_Finalize (); } Informatika Fakultatea UPV/EHU 14
MPI Funciones básicas Otro ejemplo: planificación de un bucle. . . main (int argc, char *argv[]) {. . . MPI_Init (&argc, &argv); MPI_Comm_rank (MPI_COMM_WORLD, &pid); MPI_Comm_size (MPI_COMM_WORLD, &npr); for (i=pid; i<N; i+=npr) func (i); MPI_Finalize (); } Informatika Fakultatea UPV/EHU 3 15
MPI Funciones básicas: comunicación 3 3. Envío y recepción de mensajes MPI ofrece dos (tres) tipos de comunicación: punto a punto: del proceso i al j (participan ambos). en grupo (colectiva): entre un grupo de procesos, de uno a todos, de todos a uno, o de todos a todos. one-sided: del proceso i al j, pero sin la participación explícita de j. Además, hay múltiples variantes en función de cómo se implementa el proceso de envío y de espera. Informatika Fakultatea UPV/EHU 16
MPI Funciones básicas: comunicación 3 3. Envío y recepción de mensajes entre dos procesos La comunicación entre procesos requiere (al menos) de dos participantes: emisor y receptor. El emisor ejecuta una función de envío y el receptor otra de recepción. A enviar recibir B La comunicación es un proceso cooperativo: si una de las dos funciones no se ejecuta, no se produce la comunicación (y podría generarse un deadlock). Informatika Fakultatea UPV/EHU 17
MPI Funciones básicas: comunicación 3 18 MPI ofrece diferentes modos de comunicación. Veamos un resumen. Modos de comunicación (1) • síncrona: la comunicación no se produce hasta que emisor y receptor se ponen de acuerdo (sin búfer EMI intermedio). - petición de transmisión (espera) - aceptación de transmisión - envío de datos (de usuario a usuario) Informatika Fakultatea UPV/EHU REC RTS D RTR
MPI Funciones básicas: comunicación 3 Modos de comunicación (1) • con búfer (buffered): el emisor deja el mensaje en un búfer y retorna. La comunicación se produce cuando el receptor está dispuesto a ello. El búfer no se puede reutilizar hasta que se vacíe. REC EMI usuario s. o. ¡Ojo con el tamaño del búfer! Informatika Fakultatea UPV/EHU s. o. usuario 19
MPI Funciones básicas: comunicación 3 20 Modos de comunicación (2) • bloqueante Se espera a que la “comunicación” se produzca, antes de continuar con la ejecución del programa. La comunicación síncrona es bloqueante. La comunicación con búfer también, si el mensaje no cabe en el búfer. • no bloqueante Se retorna “inmediatamente” de la función de comunicación, y se continúa con la ejecución. Se comprueba más tarde si la comunicación se ha efectuado. Informatika Fakultatea UPV/EHU
MPI Funciones básicas: comunicación 3 Cada estrategia tiene ventajas e inconvenientes: • síncrona: es más rápida si el receptor está dispuesto a recibir; nos ahorramos la copia en el búfer. Además del intercambio de datos, sirve para sincronizar los procesos. Ojo: al ser bloqueante es posible un deadlock. • con búfer: el emisor no se bloquea si el receptor no está disponible, pero hay que hacer copia(s) del mensaje (más lento). Informatika Fakultatea UPV/EHU 21
MPI Funciones básicas: comunicación 3 Para enviar o recibir un mensaje es necesario especificar: • los datos a enviar (dirección de comienzo, cantidad y tipo) • a quién se envía (o de quién se recibe) • la etiqueta del mensaje (tag) Todo lo que no son los datos forma el “sobre” del mensaje (que se puede “procesar”). Las dos funciones estándar para enviar y recibir mensajes son: Informatika Fakultatea UPV/EHU 22
MPI Funciones básicas: send - recv 3 Función estándar para enviar un mensaje MPI_Send (*dat, count, type, dest, tag, comm) - mensaje a enviar: [dat (@comienzo), count (cantidad), type] - receptor: [dest (pid destino), - tag: 0. . 32767 (etiqueta asignada en el envío) - tipos de datos: MPI_CHAR, INT, LONG, FLOAT, DOUBLE, BYTE… comm (comunicador)] Send utiliza la capacidad de buffering del sistema; es decir, retorna una vez copiado en el búfer el mensaje a enviar… ¡siempre quepa! Informatika Fakultatea UPV/EHU 23
MPI Funciones básicas: send - recv 3 24 Función estándar para recibir un mensaje MPI_Recv (*dat, count, type, source, tag, comm, *stat) - mensaje a recibir: [dat, count, type] “reserva de espacio” para el mensaje - emisor: [source, comm] - tag: etiqueta del mensaje a recibir - stat: devuelve información sobre el mensaje recibido Recv se bloquea hasta que se recibe el mensaje. Informatika Fakultatea UPV/EHU
Funciones básicas: send - recv MPI 3 25 Algunas precisiones • source, dest, count y tag son enteros (int); comm y stat son de tipo MPI_Comm y MPI_Status. • La comunicación solo se efectúa si coinciden los parámetros de origen y destino con las direcciones de emisor y receptor, y las etiquetas(tag) son iguales. • El tamaño del mensaje (count) definido en la función Recv debe ser igual o mayor al definido en Send. • El origen de un mensaje en la función Recv puede ser MPI_ANY_SOURCE, y la etiqueta puede ser MPI_ANY_TAG. Origen/destino pueden ser MPI_PROC_NULL (no hace nada); stat puede ser MPI_STATUS_IGNORE; el mensaje (NULL) puede ser de tamaño 0. Informatika Fakultatea UPV/EHU
MPI Funciones básicas: send - recv 3 Algunas precisiones • stat es un struct con tres campos, en el que se devuelve información sobre el mensaje recibido: stat. MPI_SOURCE: indica el emisor del mensaje stat. MPI_TAG: devuelve el tag del mensaje recibido stat. MPI_ERROR: devuelve un código de error (aunque lo más habitual es abortar en caso de error) • También puede obtenerse el tamaño del mensaje recibido ejecutando: MPI_Get_count (*stat, type, *count) (en algunas implementaciones, stat. count, en bytes) Informatika Fakultatea UPV/EHU 26
MPI Funciones básicas: send - recv 3 Algunas precisiones • Si un proceso tiene varios mensajes para recibir, se reciben en el orden en que se ejecutan las funciones de recepción, de acuerdo a los parámetros de origen y tag, independientemente del orden en que se enviaron. • Si el tag del mensaje que se recibe puede ser cualquiera (MPI_ANY_TAG), los mensajes que provienen del mismo origen se reciben en el orden en que se enviaron. Informatika Fakultatea UPV/EHU 27
Funciones básicas: send - recv MPI 3 28 Un ejemplo. . . #define N 10 int main (int argc, char **argv) { int pid, npr, orig, dest, tag; int i, VA[N]; MPI_Status info; else if (pid == 1) { for (i=0; i<N; i++) printf (“%4 d”, VA[i]); orig = 0; tag = 0; MPI_Recv (&VA[0], N, MPI_INT, orig, tag, MPI_COMM_WORLD, &info); MPI_Init (&argc, &argv); MPI_Comm_rank (MPI_COMM_WORLD, &pid); printf (“Datos desde P%d; tag = %d, ndat = %d (bytes)n”, info. MPI_SOURCE, info. MPI_TAG, info. count); for (i=0; i<N; i++) VA[i] = 0; if (pid == 0) { for (i=0; i<N; i++) VA[i] = i; dest = 1; tag = 0; MPI_Send (&VA[0], N, MPI_INT, dest, tag, MPI_COMM_WORLD); } Informatika Fakultatea UPV/EHU for (i=0; i<N; i++) printf (“%4 d”, VA[i]); } MPI_Finalize (); }
MPI Funciones básicas: probe 3 ¿Y si se desconoce el tamaño del mensaje que hay que recibir (p. ej. , se calcula en ejecución)? Dos opciones: • Mandar primero un mensaje con la longitud del segundo mensaje. • “Mirar en el buzón”, pero sin recibir el mensaje: MPI_Probe (source, tag, comm, *stat) devuelve en stat información (origen, tamaño y tag) de un mensaje que está a la espera de ser recibido. Tras ello, podemos reservar espacio en memoria, decidir cómo recibir el mensaje en función de quién lo envía, o incluso no recibirlo si nos basta con el tag (p. ej. , es un mensaje vacío, un aviso). Informatika Fakultatea UPV/EHU 29
MPI Funciones básicas: medida de tiempos Dos funciones para obtener tiempos de ejecución MPI_Wtime ( ) devuelve el tiempo transcurrido desde algún instanterior (segundos, double) MPI_Wtick ( ) devuelve la precisión de la medida de tiempo (segundos, double, habitualmente 1 microsegundo) t 1 = MPI_Wtime (); . . . t 2 = MPI_Wtime (); printf (“T = %fn”, t 2 -t 1); Informatika Fakultatea UPV/EHU 3 30
MPI Funciones básicas 3 Una cuestión previa sobre las operaciones de entrada/salida. Lo habitual es que solo P 0 tenga acceso al teclado (stdin), aunque todos puedan escribir en pantalla (stdout), aunque de manera “desordenada”. P 0 se encarga de leer los datos y distribuirlos, y, en su caso, de recoger resultados e imprimirlos (o guardarlos en disco). if (pid == 0) { leer_datos (. . . ); distribuir_datos (. . . ); } else recibir_datos (. . . ); Informatika Fakultatea UPV/EHU 31
Introducción. Funciones MPI básicas. Otros modos de envío/recepción comunicación síncrona, inmediata. . . Comunicación en grupo. Tipos de datos derivados, empaquetado. Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Informatika Fakultatea UPV/EHU
MPI Otros modos de comunicación 3 Aparte de las funciones estándar de envío y recepción, MPI ofrece otras alternativas con el objetivo de lograr el máximo rendimiento. Aunque bastaría con un solo modo, cada alternativa está pensada para determinado tipo de situación. Las principales alternativas al modo estándar son: • comunicación síncrona • comunicación inmediata Informatika Fakultatea UPV/EHU 33
MPI Comunicación síncrona 3 1. Comunicación síncrona En modo síncrono, la función de envío no retorna hasta que se produce la comunicación. En principio, no se necesita un búfer intermedio. MPI_Ssend (*dat, count, type, dest, tag, comm) La función síncrona es bloqueante; si no se produce el correspondiente matching, habrá un deadlock. Informatika Fakultatea UPV/EHU 34
MPI Comunicación síncrona 3 Deadlock P 1 Ssend_to_2; Recv_from_2; P 2 Ssend_to_1; Recv_from_1; Obviamente, hay que cambiar el orden. ¿Y con el Send estándar? P 1 Send_to_2; Recv_from_2; Informatika Fakultatea UPV/EHU P 2 Send_to_1; Recv_from_1; 35
MPI Comunicación inmediata 3 2. Comunicación inmediata La función de comunicación retorna sin esperar a que se produzca una determinada acción. Es, por definición, no bloqueante, y se busca solapar las latencias de cálculo y comunicación. La comunicación se divide en dos fases: un aviso de envío/recepción, y una verificación de que se ha producido. Se utiliza un handle para obtener información sobre el estado de la comunicación. Informatika Fakultatea UPV/EHU 36
MPI Comunicación inmediata 3 37 Funciones de envío y recepción MPI_Isend (*dat, count, type, dest, tag, comm, *req) MPI_Irecv (*dat, count, type, source, tag, comm, *req) req es una variable de tipo MPI_Request, y se utiliza para preguntar sobre el estado de la función. No se puede modificar la memoria (dat) hasta que se produzca la comunicación; si modificamos antes dat, no sabremos qué se recibirá. Funciones de test (stat de tipo MPI_Status) MPI_Test (*req, *flag, *stat) devuelve en flag un 0 si la operación no se ha completado MPI_Wait (*req, *stat) espera hasta que la comunicación se haya efectuado Informatika Fakultatea UPV/EHU
MPI Comunicación inmediata Ejemplo 1 int MPI_Status int MPI_Request flag = 0; // solo parte del código info; bufer[grande]; req 1; . . . MPI_Isend (bufer, grande, MPI_INT, dest, tag, MPI_COMM_WORLD, &req 1); while (!flag && hay_tareas) {. . . /* realizar una tarea */ MPI_Test (&req 1, &flag, &info); } if (!flag) MPI_Wait (&req 1, &info); MPI_Isend (…); . . . Informatika Fakultatea UPV/EHU 3 38
MPI Comunicación inmediata 3 Se puede iniciar muchas comunicaciones en modo inmediato, y esperar mas tarde a que acaben todas, o una de ellas, o cualquiera… MPI_Waitall (num, *req_array, *stat_array) MPI_Waitany (num, *req_array, *index, *stat) MPI_Waitsome (…) Informatika Fakultatea UPV/EHU 39
Comunicación inmediata MPI 3 Ejemplo 2. . . if (pid == 0) { for (i=0; i<100; i++) { dat[i] = calcular (i); MPI_Isend (&dat[i], 1, MPI_INT, 1, 0, MPI_COMM_WORLD, req[i]); } MPI_Waitall (100, req, MPI_STATUSES_IGNORE) } else if (pid == 1) { for (i=0; i<100; i++) { MPI_Recv (&dat[i], 1, MPI_INT, 0, 0, MPI_COMM_WORLD, MPI_STATUS_IGNORE); procesar (dat[i]); } } … Informatika Fakultatea UPV/EHU 40
MPI Otros modos de comunicación 3 Hay más opciones (muchas) • Comunicación persistente Si se van a enviar repetidamente mensajes con los mismos argumentos (por ejemplo, dentro de un bucle), podemos dividir la comunicación en dos partes: (1) crear el contexto, una sola vez, y (2) enviar el mensaje (las veces que haga falta). MPI_Send_init (…, *req) MPI_Recv_init (…, *req) MPI_Start (*req) (Isend = Send_init + Start) Informatika Fakultatea UPV/EHU 41
MPI Otros modos de comunicación 3 Hay más opciones (muchas) • Enviar+ recibir, en una función MPI_Sendrecv (param. send + param. recv) Por ejemplo, en una comunicación en anillo, recibe del anterior y envía al siguiente, asegurando que no se produce deadlock. • Comunicación con buffering Obligamos a que la comunicación se haga mediante un búfer definido por el usuario. Hay un conjunto de funciones de este tipo (Bsend, …), más otras funciones para gestionar los búferes. • + Mezclas de todas ellas Informatika Fakultatea UPV/EHU 42
MPI Resumen 3 La comunicación es uno de los puntos clave en el rendimiento de un sistema paralelo de memoria distribuida. Por ello, MPI ofrece diferentes estrategias de comunicación punto a punto, que podemos resumir así: Modo Func. bloq. estándar síncrono MPI_Send MPI_Ssend MPI_Bsend MPI_Rsend buffered [ ready En todos los casos: Recv / Probe Informatika Fakultatea UPV/EHU Func. no bloq. MPI_Isend MPI_Issend MPI_Ibsend MPI_Irsend ] Irecv / Iprobe 43
Resumen MPI 3 44 El uso recomendado sería el siguiente: MPI_Ssend: cuando es posible, ofrece los mejores resultados, puesto que no se utilizan búferes intermedios (ojo con los deadlocks). MPI_Send: la alternativa más habitual. MPI_Isend: si, por cuestiones de rendimiento, se necesitan rutinas no bloqueantes. P. ej. , en clusters en los que la comunicación sea “cara”, para solapar cálculo y comunicación. El resto de opciones, para casos especiales. Informatika Fakultatea UPV/EHU
Introducción. Funciones MPI básicas. Otros modos de envío/recepción. Comunicación en grupo broadcast, gather, scatter reduce barrier Tipos de datos derivados, empaquetado. Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Informatika Fakultatea UPV/EHU
MPI Comunicación en grupo 3 46 Muchas aplicaciones requieren de operaciones de comunicación en las que participan muchos procesos. La comunicación es en grupo o colectiva si participan en ella todos los procesos del comunicador. Ejemplo: un broadcast, envío de datos desde un proceso a todos los demás. En general, podría ejecutarse mediante un bucle de funciones tipo send/receive, pero no sería muy eficiente (lineal con P). Informatika Fakultatea UPV/EHU
MPI Comunicación en grupo 3 Todos los procesos del comunicador deben ejecutar la función. Las funciones de comunicación en grupo son bloqueantes en el mismo sentido que la función Send. Tres tipos: 1 Movimiento de datos 2 Cálculo en grupo 3 Sincronización Las principales funciones de comunicación en grupo que ofrece MPI son las siguientes: Informatika Fakultatea UPV/EHU 47
MPI Movimiento de datos: broadcast 3 1 a BROADCAST: envío de datos desde un proceso (origen o root) a todos los demás. P 0 A P 2 P 1 P 0 A A P 1 P 3 P 2 A A P 3 MPI_Bcast (*dat, count, type, source, comm) (La implementación suele ser logarítmica, en árbol) Informatika Fakultatea UPV/EHU 48
Movimiento de datos: scatter MPI 3 49 1 b SCATTER: reparto de datos desde un proceso a todos los procesos del comunicador (incluido él mismo). P 0 ABCD P 2 Informatika Fakultatea UPV/EHU P 1 P 0 P 3 P 2 ABCD A C B P 1 D P 3
Movimiento de datos: scatter MPI 3 MPI_Scatter (*dats, counts, types, *datr, countr, typer, source, comm) - el proceso source distribuye dats en P trozos, uno por procesador, todos de tamaño counts. - los datos se reciben en datr (también en el nodo origen). - lo lógico es que el tamaño y tipo de los datos que se envían y se reciben sean iguales. Ejemplo: A = (0, 1, 2, 3, 4, 5, 6, 7) en P 0 MPI_Scatter (A, 2, MPI_INT, B, 2, MPI_INT, 0, comm) (P 0) B = 0, 1 Informatika Fakultatea UPV/EHU (P 1) B = 2, 3 (P 2) B = 4, 5 (P 3) B = 6, 7 50
Movimiento de datos: gather MPI 3 1 c GATHER: recolección de datos de todos los procesos en uno de ellos (orden estricto de pid). P 0 A B P 1 P 0 A ABCD B P 1 P 2 C D P 3 Informatika Fakultatea UPV/EHU 51
Movimiento de datos: gather MPI 3 MPI_Gather (*dats, counts, types, *datr, countr, typer, dest, comm) - el proceso dest recolecta en datr los datos enviados en dats por cada proceso del comunicador. - los datos se guardan en el orden marcado por el pid. - countr indica la cantidad de datos recibidos de cada proceso, no el total; lo lógico es que cantidad y tipo de los datos que se envían y se reciben sean iguales. Ejemplo: (P 0) B = 0, 1 (P 1) B = 2, 3 (P 2) B = 4, 5 (P 3) B = 6, 7 MPI_Gather (B, 2, MPI_INT, C, 2, MPI_INT, 0, comm) → en P 0: C = 0, 1, 2, 3, 4, 5, 6, 7 Informatika Fakultatea UPV/EHU 52
MPI Movimiento de datos 3 53 Otras versiones de estas funciones MPI_Gatherv (…) la información que se recolecta es de tamaño variable (vector de tamaños y dist. ). MPI_Allgather (…) los datos se recibe en todos los procesos (mismos parámetros, pero sin destino). MPI_Allgatherv (…) “suma” de las dos anteriores. MPI_Scatterv (…) la información que se distribuye es de tamaño variable (vector de tamaños y dist. ). MPI_Alltoall (…) todos los procesos distribuyen datos a todos (mismos parámetros, pero sin origen) MPI_Alltoallv (…) “suma” de las dos anteriores. Informatika Fakultatea UPV/EHU
MPI Movimiento de datos: scatterv 3 Ejemplo: Scatterv; reparto de trozos de un vector, de tamaños diferentes y no consecutivos: MPI_Scatterv (*vector, *counts, *dist, type, *buf, countr, type, source, comm) - counts[]: número de elementos del trozo a enviar a cada proceso (un int por cada proceso). - dist[]: distancia desde el comienzo del vector de datos al comienzo de cada trozo (un int por proceso). - countr: entero que indica el número de elementos a recibir en buf en el procesador local (= counts[pid]). Informatika Fakultatea UPV/EHU 54
Cálculo en grupo: reduce MPI 3 55 2 REDUCE: una operación de reducción con los datos de cada procesador, dejando el resultado en uno de ellos. P 0 A B P 1 P 0 A A+B+C+D B P 1 P 2 C D P 3 MPI_Reduce (*dat, *res, count, type, oper, dest, comm) Informatika Fakultatea UPV/EHU
MPI Cálculo en grupo: reduce 3 Algunos comentarios • Operación: res : = res oper dat • res es una variable del proceso destino, de nombre diferente a dat (no aliasing). • Funciones típicas de reducción: MPI_SUM _PROD MPI_MAX _MIN MPI_BAND _BOR _BXOR _MAXLOC _MINLOC [ver manual] _LAND _LOR _LXOR • Pueden definirse otras operaciones de reducción: MPI_Op_create (…) Informatika Fakultatea UPV/EHU MPI_Op_free (…) 56
MPI Cálculo en grupo: reduce 3 Otras funciones del mismo tipo: MPI_Allreduce (…) MPI_Reduce_scatter (…) MPI_Scan (…) todas las “sumas”: ALLREDUCE butterfly, 3 pasos (log P) Informatika Fakultatea UPV/EHU 0, 0+1+2, … 0+1+…+P-1 57
MPI Sincronización: barrier 3 BARRIER: sincronización global entre los procesos del comunicador. MPI_Barrier (comm) La función se bloquea hasta que todos los procesos del comunicador la ejecutan. Informatika Fakultatea UPV/EHU 3 58
MPI Comunicación en grupo Algunas precisiones • todos los procesos del comunicador deben ejecutar la función colectiva. • salvo la barrera, no implican sincronización entre los procesos. • el número de datos enviado y recibido debe ser igual. • los tipos pueden ser diferentes Informatika Fakultatea UPV/EHU 3 59
MPI Comunicación en grupo Ejemplo: V(i) = V(i) * V(j) (Leer N; sum = 0); for (j=0; j<N; j++) sum = sum + V[j]; for (i=0; i<N; i++) V[i] = V[i] * sum; 1. 2. 3. 4. 5. 6. 7. 8. Informatika Fakultatea UPV/EHU Leer N (el pid = 0) (tamaño del vector) Broadcast de N Scatter del vector V (trozo correspondiente) Cálculo local de la suma parcial Allreduce de sumas parciales (todos obtienen suma total) Cálculo local de V(i)*sum Gather de resultados Imprimir el resultado (el pid = 0) 3 60
Introducción. Funciones MPI básicas. Otros modos de envío/recepción. Comunicación en grupo. Tipos de datos derivados, empaquetado vector, indexed, struct / packed Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Informatika Fakultatea UPV/EHU
MPI Tipos de datos derivados La comunicación entre procesos es costosa (en función de la red). Por ello, es mejor enviar un paquete con tres datos que tres paquetes con un dato cada uno. Las funciones Send y Recv indican explícitamente la dirección de comienzo del mensaje y el número de elementos que lo componen, pero hay una restricción: deben ser elementos consecutivos y del mismo tipo. Informatika Fakultatea UPV/EHU 3 62
MPI Tipos de datos derivados 3 63 Ejemplo: enviar la fila 2 de la matriz A, de P 0 a P 1 int A[N][M]; . . . orig = 0; dest = 1; if (pid == orig) MPI_Send (&A[2][0], M, MPI_INT, dest, 0, MPI_COMM_WORLD); else if (pid == dest) MPI_Recv (&A[2][0], M, MPI_INT, orig, 0, MPI_COMM_WORLD, &info); Pero, ¿cómo se envía una columna en un solo mensaje? ¿cómo se envían en un mensaje datos de diferente tipo? Informatika Fakultatea UPV/EHU
MPI Tipos de datos derivados 3 Tenemos dos soluciones: 1. Definir nuevos “tipos de datos”, en los que se especifica una determinada “estructura” para los datos, agrupando datos de diferente tipo, tamaño, etc. 2. “Empaquetar” los datos que hay enviar (que en general serán de diferente tipo, tamaño y estructura) en un único paquete, que el receptor deberá desempaquetar de manera adecuada. Informatika Fakultatea UPV/EHU 64
MPI Tipos de datos derivados 3 1. Tipos derivados • Un TDD define una determinada “estructura” para los datos, indicando dónde están, cuántos son, de qué tipo, etc. El TDD se utiliza en las funciones de comunicación para especificar qué datos hay que enviar. • El proceso de generación de un TDD se efectúa en dos pasos: 1. definición del TDD 2. creación del tipo Informatika Fakultatea UPV/EHU (de tipo MPI_Datatype) (MPI_Type_commit) 65
MPI Tipos de datos derivados: vector 3 66 1 a Tipo “vector” Se define un vector de n elementos, del mismo tipo y tamaño, a distancia (stride) constante. (normalmente un subconjunto de un array mayor). x x x xx x x x … MPI_Type_vector (nelem, count, dist, type, *newtype) El nuevo tipo contiene nelementos (4), de tamaño count (2), a distancia dist (7) uno de otro, todos del mismo tipo. [ MPI_Type_contiguous (nelem, type, *newtype) ] Informatika Fakultatea UPV/EHU
Tipos de datos derivados: vector MPI 3 67 Ejemplo: enviar la columna 3 de la matriz A, de P 0 a P 1 int A[N][M]; MPI_Datatype Columna; . . . MPI_Type_vector (N, 1, M, MPI_INT, &Columna); MPI_Type_commit (&Columna); orig = 0; dest = 1; if (pid == orig) MPI_Send (&A[0][3], 1, Columna, dest, 0, MPI_COMM_WORLD); else if (pid == dest) MPI_Recv (&A[0][3], 1, Columna, orig, 0, MPI_COMM_WORLD, &info); OJO: 1 columna, pero no 2 (ver extensión del tipo) Informatika Fakultatea UPV/EHU
MPI Tipos de datos derivados: indexed 1 b Tipo “indexed” Se define un vector de n elementos, del mismo tipo, con tamaño y stride variable. xxx x x x xx xx x x x… MPI_Type_indexed (nelem, *count, *dist, type, *newtype) count[]: número de componentes de cada elemento que forma el nuevo tipo. (3, 2, 1, 2) dist[]: distancia desde un punto de comienzo dado a cada elemento. (3, 12, 16, 21) Informatika Fakultatea UPV/EHU 3 68
Tipos de datos derivados: indexed MPI 3 Ejemplo: enviar de P 0 a P 1 el triángulo superior de la matriz A. M int A[N][M], T[N][M]; MPI_Datatype Mtri; . . . N for(i=0; i<N; i++) { tam[i] = M - i; dist[i] = (M+1) * i; } MPI_Type_indexed (N, tam, dist, MPI_INT, &Mtri); MPI_Type_commit (&Mtri); MPI_Send (A, 1, Mtri, 1, 0, MPI_COMM_WORLD); else if (pid == 1) MPI_Recv (T, 1, Mtri, 0, 0, MPI_COMM_WORLD, &info); if (pid == 0) Informatika Fakultatea UPV/EHU 69
Tipos de datos derivados: struct MPI 3 1 c Tipo “struct” Es el caso más general, en el que se agrupan n elementos de tamaño y tipo diferente. Por ejemplo, P 0 tiene que enviar a todos los procesadores tres parámetros: A y B, dobles, y C, entero. Podemos agrupar los tres parámetros y efectuar un único envío (BC). Para ello construimos un struct con: - d número de elementos A tamaño de cada elemento tipo de cada elemento distancia a cada elemento desde el origen Informatika Fakultatea UPV/EHU d i B C 2 6 70
MPI Tipos de datos derivados: struct 3 71 1 c Tipo “struct” La función para definir el tipo es: MPI_Type_create_struct (nelem, *count, *dist, *type, *newtype) count[]: tamaño de cada elemento que forma el nuevo tipo. dist[]: distancias desde el comienzo del nuevo tipo a cada elemento (MPI_Aint). type[]: tipo de cada elemento (MPI_Datatype). MPI_Get_address (*A, *addr. A) Devuelve la dirección de A en addr. A (de tipo MPI_Aint). Para calcular la distancia en memoria entre datos, como diferencia entre sus direcciones. Informatika Fakultatea UPV/EHU
Tipos de datos derivados: struct MPI void Crear_Tipo 3 (double* A, B; int* C; MPI_Datatype* Mensaje) { int tam[3]; MPI_Aint dist[3], dir 1, dir 2; MPI_Dataype tipo[3]; // tipo de las dir. en MPI tam[0] = tam[1] = tam[2] = 1; tipo[0] = tipo[1] = MPI_DOUBLE; tipo[2] = MPI_INT; . . . (A, B, C, punteros a las variables) dist[0] = 0; Crear_Tipo (A, B, C, &Mensaje); MPI_Get_address (A, &dir 1); MPI_Bcast (A, 1, Mensaje, 0, MPI_COMM_WORLD ); MPI_Get_address (B, &dir 2); . . . dist[1] = dir 2 - dir 1; MPI_Get_address (C, &dir 2); dist[2] = dir 2 - dir 1; MPI_Type_create_struct (3, tam, dist, tipo, Mensaje); MPI_Type_commit (Mensaje); } Informatika Fakultatea UPV/EHU 72
MPI Tipos de datos derivados: subarray 3 1 d Subarrays Una submatriz de N dimensiones a partir de una de N dimensiones. Por ejemplo, un bloque 2 D de una matriz 2 D: MPI_Type_create_subarray (. . . ) MPI_Type_create_darray (. . . ) Informatika Fakultatea UPV/EHU 73
MPI Tipos de datos derivados: subarray 3 1 d Subarrays Una submatriz de N dimensiones a partir de una de N dimensiones. Por ejemplo, un bloque 2 D de una matriz 2 D: MPI_Type_create_subarray (ndim, *kdim_sa, *sa, order, type, *newtype) ndim: nº de dimensiones del array original. kdim[], kdim_sa[]: elementos en cada dimensión. sa[]: dirección de comienzo del subarray. order: MPI_ORDER_C, _FORTRAN. kdim, kdim_sa, dir_sa, arrays de ndim elementos. sa es normalmente 0. (>> también puede definirse como “vector”) Informatika Fakultatea UPV/EHU 74
MPI Tipos de datos derivados 3 75 Tamaño y extensión de un TDD: 1. Tamaño: número de bytes que se transmiten. MPI_Type_size (type, *bytecount) 2. Extensión: distancia en memoria entre el primer y el último byte que se transmiten. MPI_Type_get_extent (type, *start, *exten) (final = start + exten) Informatika Fakultatea UPV/EHU
MPI Tipos de datos derivados 3 Modificación de la extensión de un tipo Podemos modificar la extensión de un tipo (dónde acaba) MPI_Type_create_resized (type, start, exten, *newtype) P. ej. , si definimos el tipo col (columna), la extensión es la dirección final del último elemento. Podemos cambiarla para que apunte a la dirección de comienzo de la siguiente columna: > MPI_Type_create_resized (col, 0, sizeof(int), &col 2) Ahora podemos, por ejemplo, enviar varias columnas consecutivas usando el tipo col 2 sin problemas. (Nuevo tipo > ejecutar “commit”. ) Algo similar hay que hacer para un scatter de datos 2 D. Informatika Fakultatea UPV/EHU 76
MPI Tipos de datos derivados 3 Matching de tipos Send y Recv especifican el tipo de los datos que se envían y reciben. Se llama firma (signature) de tipos a la secuencia {t 0, t 1. . . , tn-1}, donde ti es el tipo del elemento i. El tipo “global” no tiene por qué ser el mismo en el envío y en la recepción, pero la firma de tipos del tipo de datos especificado en Recv tiene que tener al menos tantos elementos como la de Send, y los tipos básicos han de ser iguales: es decir, se deben recibir al menos tantos datos como se envían y del mismo tipo. Ojo: en las funciones colectivas el tipo “global” debe ser el mismo. Informatika Fakultatea UPV/EHU 77
MPI Tipos de datos derivados 3 Matching de tipos El mensaje lleva los datos sin “estructura”. La estructura se “recompone”, si se desea, al recibirlo. Por ejemplo, enviamos N elementos de una columna de una matriz utilizando el tipo columna. Si recibimos los datos con el tipo columna, se cargarán en la columna de la matriz que indiquemos. Si los recibimos como N enteros, se cargarán uno tras otro a partir de la dirección que indiquemos; si los recibimos con tipo “diagonal”, se cargarán en la diagonal; etc. Informatika Fakultatea UPV/EHU 78
MPI Empaquetado 3 2. Empaquetado Se trata de empaquetar en posiciones consecutivas de memoria los datos que hay que enviar. La comunicación se efectúa en tres fases: 1 Antes de enviar el mensaje, se van añadiendo los datos a un búfer mediante la función de empaquetado. 2 Se envía el búfer (para el envío, de tipo MPI_PACKED) 3 El receptor desempaqueta los datos recibidos mediante la función MPI de desempaquetado. Informatika Fakultatea UPV/EHU 79
MPI Empaquetado 3 80 2. Empaquetado La función para empaquetar datos es: MPI_Pack (*dat, count, type, *buf, bufsize, *pos, comm) Añade a buf (char, de tamaño bufsize) la variable dat, a partir de la posición marcada por el puntero pos (int). El puntero queda apuntando a la primera posición libre de buf. Al final de las operaciones de empaquetado, indica el tamaño del paquete. La función MPI_Pack_size (ver manual) devuelve el tamaño mínimo necesario para empaquetar un objeto. Se puede usar para calcular el tamaño final del paquete y reservar memoria para el mismo. Informatika Fakultatea UPV/EHU
MPI Empaquetado 3 81 2. Empaquetado La función para desempaquetar datos es: MPI_Unpack (*buf, bufsize, *pos, *dat, count, type, comm) Recupera de buf (apuntado por pos) la variable dat del tamaño y tipo indicado (pos queda apuntando a la siguiente variable). bufsize debe ser suficiente para quepa el mensaje recibido. Informatika Fakultatea UPV/EHU
Empaquetado MPI 3 void Leer_Datos (double* A, B; int* C; int pid) { #define tambuf 20; char bufer[tambuf]; int pos; if (pid == 0) { printf (“-> A, B y Cn”); scanf (“%f %f %d”, A, B, C); pos = 0; // A, B, C, punteros a las vbles. MPI_Pack (A, 1, MPI_DOUBLE, bufer, tambuf, &pos, MPI_Pack (B, 1, MPI_DOUBLE, bufer, tambuf, &pos, MPI_Pack (C, 1, MPI_INT, bufer, tambuf, &pos, MPI_COMM_WORLD ); } MPI_Bcast (bufer, tambuf, MPI_PACKED, 0, MPI_COMM_WORLD); if (pid > 0) { pos = 0; MPI_Unpack (bufer, tambuf, &pos, A, 1, MPI_DOUBLE, MPI_COMM_WORLD ); MPI_Unpack (bufer, tambuf, &pos, B, 1, MPI_DOUBLE, MPI_COMM_WORLD ); MPI_Unpack (bufer, tambuf, &pos, C, 1, MPI_INT, MPI_COMM_WORLD ); } } Informatika Fakultatea UPV/EHU 82
MPI Resumen 3 83 Para reducir el overhead de la comunicación necesitamos agrupar en un solo mensaje datos que pudieran estar repartidos por la memoria (no consecutivos). Una alternativa es definir un tipo derivado, una estructura concreta para indicar dónde están los datos (un struct que se crea en ejecución y que puede pasarse como argumento a una función de comunicación). MPI_Type_vector, _indexed, _create_struct La otra es empaquetar los datos antes de enviarlos y desempaquetarlos tras recibirlos. MPI_Pack, MPI_Unpack Informatika Fakultatea UPV/EHU
MPI Resumen 3 Lo más habitual es enviar datos consecutivos utilizando el mecanismo estándar de transmisión. Si se envían datos heterogéneos una sola vez (o pocas), lo más adecuado sería empaquetar. Si los datos a enviar son muchos y no consecutivos, y se envían repetidamente, lo mejor sería definir un tipo específico: - datos homogéneos: contiguous, vector, indexed - datos heterogéneos: struct Memoria: empaquetar implica más memoria (búfer datos), mientras que un TDD solo necesita memoria para definir la estructura de los datos. Informatika Fakultatea UPV/EHU 84
Introducción. Funciones MPI básicas. Otros modos de envío/recepción. Comunicación en grupo. Tipos de datos derivados, empaquetado. Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Informatika Fakultatea UPV/EHU
MPI Comunicadores 3 Un comunicador define un grupo de procesos entre los que es posible el intercambio de mensajes. Cada uno se identifica en el comunicador mediante su pid. El comunicador predefinido MPI_COMM_WORLD engloba a todos los procesos de la aplicación paralela. En muchos casos es útil/necesario definir subgrupos de procesos para • comunicaciones globales entre subconjuntos de procesos • distribuir los procesos entre tareas independientes (librerías) • … Así, un proceso puede tener más de un pid: uno por cada comunicador o grupo del que forme parte. Informatika Fakultatea UPV/EHU 86
MPI Comunicadores 3 Un comunicador está formado, al menos, por un grupo de procesos y un contexto. El contexto define un espacio propio e identificado de comunicación. Un comunicador puede incluir más datos asociados, tales como una topología, características específicas de los procesadores que lo componen, entrada/salida, etc. Para ver la utilidad de definir grupos de procesos, analicemos un ejemplo sencillo: producto de matrices. Informatika Fakultatea UPV/EHU 87
MPI Motivación 3 Ejemplo: C = A × B C B A x Cij = Ai, 0 B 0, j + Ai, 1 B 1, j + = . . . + Ai, i Bi, j + . . . + Ai, n-1 Bn-1, j En el caso de una matriz “densa”, hay que efectuar O(N 3) operaciones. ¿Cómo paralelizar el cálculo de manera eficiente entre P procesadores? ¿Cuál es la mejor manera de distribuir los datos entre los procesos? Objetivo: minimizar el tiempo asociado a la comunicación. Informatika Fakultatea UPV/EHU 88
MPI Motivación 3 1. Descomposición unidimensional (filas) (P procesadores, N/P filas por procesador) P 0 x = • Computación por procesador (* y +) : O(N 3/P) • Pero hay que añadir comunicación: N 2/P datos desde cada uno de los P-1 procesadores restantes → O(N 2). Podría ser un allgather de N 2/P datos, que se ejecuta en log P pasos. Informatika Fakultatea UPV/EHU C B A 89
Motivación MPI • Si Tcom = ti + tw N 3 ti tiempo de inicio tw tiempo por palabra • Tejec = Tcalc + Tcom Eficiente solo si: top >> tw o P es pequeño (<<N) Informatika Fakultatea UPV/EHU 90
MPI Motivación 3 N = 100 ti = 100 tw tw = 100 top speed-up = Ts / Tp 100 eficiencia = Ts / (Tp*P) 1 0. 8 80 0. 6 60 0. 4 40 0. 2 20 0 20 40 60 P Informatika Fakultatea UPV/EHU 80 0 100 91
MPI Motivación 3 2. Descomposición bidimensional: P 0 C B A x = • Computación por procesador: O(N 3/P) • Pero ahora la necesidad de comunicación es menor. Cada procesador necesita los datos de “los bloques de su fila y su columna”. Se necesita recibir 2(P 1/2 -1)(N 2/P) datos. ¿Cuál es la manera más eficiente de procesar/enviar esos datos? Informatika Fakultatea UPV/EHU 92
Motivación MPI A 00 B 00+ A 01 B 10+ A 02 B 20 bc 3 A 00 B 01+ A 01 B 11+ A 02 B 21 bc A 10 B 00+ A 11 B 10+ A 12 B 20 A 10 B 01+ A 11 B 11+ A 12 B 21 bc A 20 B 00+ A 21 B 10+ A 22 B 20 bc Informatika Fakultatea UPV/EHU A 00 B 02+ A 01 B 12+ A 02 B 22 A 20 B 01+ A 21 B 11+ A 22 B 21 A 10 B 02+ A 11 B 12+ A 12 B 22 bc A 20 B 02+ A 21 B 12+ A 22 B 22 bc 93
MPI Motivación 3 Algoritmo de multiplicación (Fox): desde k=0 hasta P 1/2 – 1 • en cada fila i, el proceso (i+k) mod N hace un broadcast de A (su trozo) al resto de procesos de la fila • operaciones locales de cálculo: Ci, j += Ai, i+k Bi+k, j • envío del trozo procesado de B al vecino de arriba Comunicación por procesador: P 1/2 veces • broadcast de N 2/P datos a P 1/2 - 1 procesos (se puede optimizar en log P 1/2 pasos) • send de N 2/P datos Informatika Fakultatea UPV/EHU 94
MPI Motivación Coste global (cálculo + comunicación) [ ¿y el coste de reorganizar las matrices? ] Informatika Fakultatea UPV/EHU 3 95
MPI Motivación 3 N = 100 ti = 100 tw tw = 100 top speed-up = Ts / Tp 100 eficiencia = Ts / (Tp*P) 1 0. 8 80 0. 6 60 0. 4 40 0. 2 20 0 20 40 60 P Informatika Fakultatea UPV/EHU 80 0 100 96
Motivación MPI 3 Resumen (Nx. N datos, P procesadores) N Com. : N N 2(P-1)/P N 2/P P 1/2 bloques N 2 - N 2/P = Com. : 2 N 2/P (P 1/2 -1) = N 2 2(P 1/2 -1)/P Conclusión: división 2 D es más “escalable”, pero el algoritmo de multiplicación requiere comunicaciones entre subconjuntos de procesos. Informatika Fakultatea UPV/EHU 97
MPI Creación de comunicadores 3 A 1. Para crear un determinado comunicador con un subconjunto de procesos hay que seguir los siguientes pasos: 1. Extraer de un comunicador inicial, comm, el grupo de procesos asociados, grp: MPI_Comm_group (comm, *grp) Si com es MPI_COMM_WORLD, grp contendrá a todos los procesos. Informatika Fakultatea UPV/EHU 98
MPI 2. Creación de comunicadores Crear un nuevo grupo de procesos, newgrp, eligiendo del grupo inicial grp aquellos queremos que formen parte del nuevo comunicador: MPI_Group_incl (grp, npr, *pid, *newgrp) pid[] es un array de npr elementos que indica el pid de los procesos de grp que se eligen para formar el nuevo grupo. (también se puede hacer por exclusión, con la función MPI_Group_excl) 3. Finalmente, se crea el comunicador newcomm con los procesos del grupo newgrp extraídos del comunicador comm: MPI_Comm_create (comm, newgrp, *newcomm) Informatika Fakultatea UPV/EHU 3 99
MPI Creación de comunicadores 3 Ejemplo: una matriz está repartida por bloques en n × n procesos. Así definiríamos el comunicador “fila 0” de procesos y haríamos un broadcast en esa fila: MPI_Group MPI_Comm gr 1, grf 0; CF 0; for(i=0; i<n; i++) pids[i] = i; /* lista de proc. */ MPI_Comm_group (MPI_COMM_WORLD, &gr 1); MPI_Group_incl (gr 1, n, pids, &grf 0); MPI_Comm_create (MPI_COMM_WORLD, grf 0, &CF 0); . . . if (pid < n) { MPI_Comm_rank (CF 0, &pid_f 0); MPI_Broadcast (&A[0][0], tam, MPI_FLOAT, 0, CF 0); } Informatika Fakultatea UPV/EHU 100
MPI Creación de comunicadores 3 Algunas precisiones • Los grupos de procesos son variables de tipo MPI_Group y los comunicadores son de tipo MPI_Comm. • MPI_Comm_create es una operación colectiva, que debe ser llamada por todos los procesos del comunicador original (aunque no vayan a formar parte del nuevo comunicador; se les devuelve el valor MPI_COMM_NULL). • Al finalizar su uso hay que “deshacer” el comunicador, ejecutando MPI_Comm_free. • Otras funciones para trabajar con grupos: MPI_Group_rank, size, free, union, intersection. . . Informatika Fakultatea UPV/EHU 101
MPI Creación de comunicadores 3 102 A 2. Crear muchos comunicadores “similares” (p. ej. , filas o columnas de una estructura 2 D). Podemos generarlos todos a la vez mediante la función: MPI_Comm_split (comm, color, key, *newcomm) Los procesos de comm se van a agrupar en nuevos comunicadores en función del “color” (entero positivo) con el que llamen a la función, y se reordenan según el parámetro key (a igual valor, se usa el pid original). Se crean tantos comunicadores como colores diferentes se utilicen, todos con el mismo nombre newcomm. Informatika Fakultatea UPV/EHU
MPI Creación de comunicadores 3 Ejemplo: crear comunicadores para todas las filas de un grupo de n × n procesos: // pid = identificador en el comunicador global mi_fila = pid / n; MPI_Comm_split (MPI_COMM_WORLD, mi_fila, pid, &CFILA); Si había 3× 3 procesos, ahora tendremos un nuevo comunicador en el que el grupo de procesos es diferente en función de quién lo use: {0, 1, 2} en P 0, P 1, P 2 / {3, 4, 5} en P 3, P 4, P 5 / {6, 7, 8} en P 6, P 7, P 8. Es una función colectiva; todos los procesos del comunicador original tienen que ejecutarla (si un proceso no va a formar parte de los nuevos comunicadores, llama a la función color MPI_UNDEFINED y se le devuelve MPI_COMM_NULL). Informatika Fakultatea UPV/EHU 103
MPI Topologías 3 Un comunicador puede incluir, además del grupo y el contexto, otro tipo de información o atributos; por ejemplo, una topología (virtual). No se refiere a la red de comunicación, sino a una manera alternativa de identificar a los procesos de un comunicador. Se genera un nuevo comunicador (que contiene todos los procesos del original). Dos tipos: cartesian y graph. Informatika Fakultatea UPV/EHU 104
MPI Topologías 3 B 1. Cartesian: se define una “malla” de procesos de n dimensiones (p. ej. , de 4 x 4 para 16 procesos). Para definir un comunicador con una “malla” global tenemos que indicar: • • número de dimensiones (2) elementos por dimensión (4, 4) cadenas (0) o anillos (1) en cada dimensión optimización (ajuste a la red física) MPI_Cart_create (comm, ndim, *kdim, *rings, opt, *newcomm) Informatika Fakultatea UPV/EHU 105
MPI Topologías 3 Ejemplo: definir un comunicador con topología toro MPI_Comm CTORO. . . opt = 1; ndim = 2; kdim[0] = kdim[1] = 4; anillos[0] = anillos[1] = 1; MPI_Cart_create (MPI_COMM_WORLD, ndim, *kdim, *anillos, op, &CTORO); kdim[]: número de procesos en cada dimensión. anillos[]: 0 → cadena 1 → anillo; CTORO es un nuevo comunicador que engloba a los mismos procesos que MPI_COMM_WORLD, pero que añade coordenadas cartesianas a los procesos. Informatika Fakultatea UPV/EHU 106
MPI Topologías 3 Ahora podemos trabajar con una doble identificación de los procesos: el pid en el nuevo comunicador, y las coordenadas cartesianas de la topología asociada. Dos funciones permiten pasar de una a otra: MPI_Cart_coords (comm, pid, dim, *coord) comm: pid: dim: coord[]: comunicador con topología identificador del proceso en el nuevo comunicador número de dimensiones coordenadas del proceso en la topología MPI_Cart_rank (comm, *coord, *pid) Informatika Fakultatea UPV/EHU 107
MPI Topologías 3 108 Se pueden obtener las direcciones de los “vecinos” en cada dimensión de la topología (- / +) MPI_Cart_shift (comm, dist, *source, *dest) dim: dimensión en la queremos buscar los vecinos dist: distancia del vecino source, dest: dirección de los vecinos a “izquierda” y “derecha” a la distancia indicada (- / +) ( usada habitualmente junto con sendrec para efectuar envíos/ recepciones en anillo en una determinada dimensión de la malla, donde uno de los vecinos es el origen y el otro el destino ) Informatika Fakultatea UPV/EHU
MPI Topologías 3 B 2. Definición de submallas Partiendo de una malla, generamos comunicadores para submallas (p. ej. , las columnas o filas de una malla 2 D) MPI_Cart_sub (comm, *dim, *newcomm) A partir del comunicador que tiene asociada la topología, se crean nuevos comunicadores con un subconjunto de las dimensiones originales, las indicadas en el array booleano dim: 1 → se mantiene la dimensión 0 → no se mantiene P. ej. , de una malla de 3 x 5 (3 filas, 5 columnas) se crean 3 comunicadores fila si dim = [0, 1], o 5 columna si dim = [1, 0] (Otro ejemplo: 4 x 6 x 8 y dim [1, 1, 0] > 8 comunicadores de 4 x 6 procesos) Informatika Fakultatea UPV/EHU 109
MPI Topologías 3 Un conjunto de funciones similares permite añadir un “grafo” subyacente a un determinado grupo de procesos. En resumen Un comunicador representa a un grupo de procesos que se reconocen entre sí como posible origen y destino de sus comunicaciones. Una topología es un mecanismo alternativo de identificación “lógica” de procesos dentro de un grupo, que genera un nuevo comunicador y, en su caso, subgrupos. Informatika Fakultatea UPV/EHU 110
MPI Intercomunicadores 3 Procesos de comunicadores diferentes no pueden comunicarse entre sí, a no ser que se cree un “intercomunicador”: MPI_Intercomm_create (localcomm, *local_leader, globalcom, *pid_lider_remoto, tag, *intercom) localcomm, local_leader: comunicador local, y pid del líder del comunicador local (normalmente 0) globalcomm, remote_leader: comunicador global que engloba a los comunicadores a interconectar (MPI_COMM_WORLD), y pid del líder en él (normalmente 0) tag: etiqueta usada la genera el intrac. (no utilizar para otros propósitos) Es una función colectiva para todos los procesos en la unión de los dos grupos. Al finalizar su uso, MPI_Comm_free. Informatika Fakultatea UPV/EHU 111
MPI Resumen de las funciones principales (hay más funciones, mirar manual) que permiten crear comunicadores y topologías MPI_Comm_group/create/split/free MPI_Group_incl/excl/rank/size/free MPI_Cart_create/sub/rank/coords/shift Informatika Fakultatea UPV/EHU 3 112
Introducción. Funciones MPI básicas. Otros modos de envío/recepción. Comunicación en grupo. Tipos de datos derivados, empaquetado. Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Informatika Fakultatea UPV/EHU
MPI Entrada/salida paralela 3 114 Algunas aplicaciones utilizan y generan una gran cantidad de datos, por lo que el tiempo necesario para las operaciones de E/S puede ser muy elevado (comparable o mayor que el tiempo estricto de ejecución). Algunos campos en los que se procesan datos de manera intensiva: • minería de datos: grano fino / patrones de acceso no predecibles • multimedia: • cálculo científico: Informatika Fakultatea UPV/EHU grano grueso / predecibles grano fino - grueso / predecibles o no
MPI Entrada/salida paralela 3 El número de datos procesados por unidad de tiempo es mayor en los sistemas paralelos. Si el sistema de almacenamiento de datos (discos) no responde adecuadamente, el rendimiento del sistema global decaerá (Amdahl de nuevo). Estructura típica serie: • un disco conectado al procesador (servidores, sistema RAID) • datos en diferentes formatos • acceso secuencial / aleatorio • llamadas estándar (fprintf, fscanf. . . ) • sistema de ficheros NFS Informatika Fakultatea UPV/EHU 115
MPI Entrada/salida paralela 3 Resumen RAID (redundant array independent disks) • objetivos: capacidad (+ disc. ), rendimiento (entrelaz. ), fiabilidad (paridad, hamming. . . ) • estructura: RAID 0: entrelazado de los datos (nivel de sub-bloque) entre un conjunto de discos (igual que en la MP) + capacidad / + rendimiento / no fiabilidad RAID 1/10 (mirroring): replicación de datos / entrelazado - capacidad / rendimiento / fiabilidad RAID 5: entrelazado de los datos / paridad + capacidad / + rendimiento / tolerancia a fallos Informatika Fakultatea UPV/EHU 116
MPI Entrada/salida paralela La entrada/salida en paralelo hace referencia a dos cuestiones • estructura “física” de los ficheros > un disco local en cada nodo E/S local -- almacenamiento temporal los datos se pueden juntar al final funciones estándar de E/S en C > nodos específicos de E/S mejor rendimiento funciones paralelas de E/S Informatika Fakultatea UPV/EHU 3 117
MPI Entrada/salida paralela 3 La entrada/salida en paralelo hace referencia a dos cuestiones • estructura “lógica” de los ficheros > procesadores escribiendo en el mismo fichero - los datos de cada proceso se intercalan en el fichero - MPI parallel I/O > en ficheros diferentes, que se pueden juntar al final -- en general, un sistema de ficheros tipo PVFS Informatika Fakultatea UPV/EHU 118
MPI Entrada/salida paralela 3 MPI añade un interfaz para el uso compartido de ficheros en operaciones de entrada/salida. La lectura y escritura de datos se realiza de forma similar al envío y recepción de datos. Los modos de acceso pueden ser bloqueantes (lo más habitual) y no bloqueantes, así como individuales (lo más habitual) y colectivos. Informatika Fakultatea UPV/EHU 119
MPI Entrada/salida paralela 3 Cada proceso tiene una “vista” del fichero, compuesta por tres elementos: - desplazamiento: punto base de los accesos. - tipo elemental (etype): tipo de las unidades que forman el fichero, y que representa la unidad de acceso y desplazamiento, normalmente un byte. - ftype: “filetype, ” una plantilla de repeticiones de “etype” que indica los datos que pueden ser accedidos por cada proceso (p. ej. , los elementos pares/impares de un vector). La “vista” por defecto tiene desplazamiento 0, y etype y ftype MPI_BYTE. Informatika Fakultatea UPV/EHU 120
MPI Entrada/salida paralela Operaciones básicas con ficheros: 1. Apertura y cierre del fichero MPI_File_open (comm, *fname, amode, info, *fh) MPI_File_close (*fh) Informatika Fakultatea UPV/EHU comm: fname: amode: info: comunicador fh: handle para trabajar con el fichero (MPI_File) nombre del fichero (char) modo de acceso (int) información sobre el fichero (MPI_Info) (MPI_INFO_NULL) 3 121
MPI Entrada/salida paralela 3 122 Operaciones básicas con ficheros: 1. Apertura y cierre del fichero MPI_File_open (comm, *fname, amode, info, *fh) MPI_File_close (*fh) Modos de acceso: MPI_MODE_RDONLY _RDWR _WRONLY Se pueden añadir otras opciones (or, |): MPI_MODE_CREATE _EXCL _DELETE_ON_CLOSE _UNIQUE_OPEN _SEQUENTIAL _APPEND Informatika Fakultatea UPV/EHU
MPI Entrada/salida paralela 3 Operaciones básicas con ficheros: 1. Apertura y cierre del fichero MPI_File_open (comm, *fname, amode, info, *fh) MPI_File_close (*fh) Son funciones colectivas; para acceso a ficheros locales (un solo proceso) se usa el comunicador MPI_COMM_SELF. MPI_File_delete (*fname, info) Permite borrar un fichero que no esté abierto (solo un proceso). Informatika Fakultatea UPV/EHU 123
MPI Entrada/salida paralela 3 124 2. Manejo de punteros MPI_File_seek (fh, offset, pos) Desplaza el puntero offset unidades (bytes) en relación a: la posición actual en el fichero (pos = MPI_SEEK_CUR), al comienzo del fichero (MPI_SEEK_SET), o al final (MPI_SEEK_END). MPI_File_set_view (fh, disp, etype, ftype, drepr, info) Crea una “vista” del fichero fh para cada proceso (op. colectiva); coloca los punteros privados de cada proceso en disp (p. ej. , función del pid). drepr indica el modo de representación de datos (ver manual). MPI_File_get_position (fh, *offset) Devuelve en offset la posición actual del puntero privado. Informatika Fakultatea UPV/EHU
MPI Entrada/salida paralela 3 3. Lectura de datos MPI_File_read (fh, *dat, count, type, *stat) Lee de fh los datos indicados por *dat, count y type. Utiliza el puntero privado de acceso al fichero (que se actualiza automáticamente). MPI_File_read_at (fh, offset, *dat, count, type, *stat) Lee comenzando en la dirección marcada por offset, sin utilizar ni modificar ningún puntero privado. Informatika Fakultatea UPV/EHU 125
MPI Entrada/salida paralela 3 4. Escritura de datos MPI_File_write (fh, *dat, count, type, *stat) Escribe en fh los datos indicados por *dat, count y type. Utiliza el puntero privado de acceso al fichero (que se actualiza automáticamente). MPI_File_write_at (fh, offset, *dat, count, type, *stat); Escribe en fh a partir de la dirección marcada por offset, sin utilizar ni modificar ningún puntero privado. MPI_File_sync (fh) (flush) Obliga a escribir en fh datos que todavía puedan estar sin escribir. Es una operación colectiva de todos los procesos del comunicador. Informatika Fakultatea UPV/EHU 126
MPI Entrada/salida paralela 3 127 Existen muchas más funciones para trabajar con ficheros (ver manual MPI), con el objetivo de conseguir el mayor rendimiento posible en aplicaciones en las que el punto clave sean las operaciones de entrada/salida. Por ejemplo, funciones colectivas de lectura/escritura, también ordenadas, etc. Informatika Fakultatea UPV/EHU
Introducción. Funciones MPI básicas. Otros modos de envío/recepción. Comunicación en grupo. Tipos de datos derivados, empaquetado. Comunicadores, topologías. Entrada/salida paralela. Rendimiento, depurado, perfiles de ejecución… Informatika Fakultatea UPV/EHU
MPI Rendimiento 3 El objetivo de un programa paralelo es resolver problemas más grandes y/o en menor tiempo. Los parámetros típicos que miden el rendimiento de una determinada ejecución en paralelo son: (N = tam. probl. , P = núm. proc. ): speed-up S(N, P) = Ts(N) / Tp(N, P) eficiencia E(N, P) = S(N, P) / P La comparación de tiempos hay que hacerla con el mejor programa serie, y no con el programa paralelo ejecutado sobre un procesador. Informatika Fakultatea UPV/EHU 129
MPI Rendimiento S debería estar entre 0 y P (E, entre 0 y 1) S = P es el caso ideal. En todo caso, lo mejor es que S sea una función lineal de P (escalabilidad). ¡pero puede ser que S < 1! (peor que el caso serie) S > P? ? Influyen otros factores, por ejemplo el hecho de que el sistema de P procesadores tenga P veces más memoria que un solo procesador. Factores que limitan el rendimiento > parte del código se ejecutará en serie (Amdahl) - a tamaño constante S tiende a 1 / (1 -f) - a tiempo constante S tiende a f p Informatika Fakultatea UPV/EHU 3 130
MPI Rendimiento 3 Factores que limitan el rendimiento Cuando se ejecuta el código en paralelo, hay que hacer más cosas que simplemente ejecutar cálculo. Lo principal, la comunicación entre procesos (y las operaciones de entrada/salida). Tp(N, P) = Tcalc(N, P) + Tcom(N, P) + (Te/s) ti + N/B Informatika Fakultatea UPV/EHU ti = tiempo de inicio B = ancho de banda N = datos a transmitir 131
MPI Rendimiento 3 Comunicación Tcom = ti + N/B ABreal = N / Tcom = = B / (1 + B ti/N) 5000 Tcom 4000 1 3000 0. 6 1000 0. 4 N 0 1000 2000 3000 4000 5000 0. 2 0 Informatika Fakultatea UPV/EHU ( 1/B) 0. 8 2000 0 ABreal N 1 10 1000 104 105 132
MPI Rendimiento 3 133 Para reducir el efecto de la latencia de las comunicaciones • Agrupar datos en los envíos. • Aumentar el tamaño de grano de las tareas. • Solapar cálculo y comunicación, usando comunicaciones no bloqueantes. Atención a las limitaciones de buffering del sistema (dependiente de la máquina!). Otros factores que limitan el rendimiento: > cálculo extra (replicado) > tiempos muertos (load balancing) (+ Tespera) Tp(N, P) = Tcal(N, P) + Tcom(N, P) + Tesp + (Te/s) Informatika Fakultatea UPV/EHU
MPI Rendimiento Otras cuestiones a considerar • Elegir el algoritmo adecuado al problema en su versión paralela (no cambiar un buen algoritmo secuencial por un algoritmo paralelo desastroso). • Analizar con cuidado el reparto de datos a los procesadores (localidad de los accesos a la cache). Informatika Fakultatea UPV/EHU 3 134
MPI Depuración de errores 3 Algunas ideas sobre depurado de errores en programas paralelos • es más difícil; un error puede no corresponder a un proceso, sino a una secuencia de acciones en diferentes procesos. • es imposible predecir el comportamiento de un programa erróneo, ya que los resultados cambian de ejecución a ejecución y de sistema a sistema. • la gran mayoría de los errores no tienen nada que ver con el paralelismo. Informatika Fakultatea UPV/EHU 135
MPI Depuración de errores Procedimiento a seguir 1. Hacer una versión serie del programa. Diseñar previamente el programa antes de escribir código, seguir un proceso incremental de pruebas, mejor ser claro que “original”. . . Utilizar las herramientas de debug de programas serie (por ejemplo, gdb). 2. Tras diseñar el programa paralelo, probarlo en un solo procesador. 3. Si es correcto, probarlo con más procesadores. Informatika Fakultatea UPV/EHU 3 136
MPI Depuración de errores Los problemas específicos están relacionados casi siempre con la “sincronización” de los procesos • Carreras (races) Los programas paralelos pueden ofrecer resultados no deterministas, que cambian de ejecución a ejecución. • Bloqueos (deadlocks) Los procesos se bloquean en operaciones de comunicación que no se completan. Por ejemplo: + intentar recibir datos con Recv pero no enviarlos. + parámetros no correctos en las funciones de comunicación: direcciones de origen/destino, tags. . . Informatika Fakultatea UPV/EHU 3 137
MPI Depuración de errores 3 Ante un determinado problema, tres líneas a seguir 1. Leer el código detenidamente, e intentar deducir cómo se comporta el programa paralelo (si es sencillo y sabemos más o menos dónde puede estar el error). 2. Rastrear el programa mediante printf; etiquetar los mensajes con el nodo y hacer flush(stdout); . . . Ojo: la introducción de trazas puede modificar el comportamiento del programa. Es útil imprimir los parámetros de comunicación antes de enviar los mensajes y cuando se reciben. 3. Usar un debugger simbólico: Total. View, gdb. . . Informatika Fakultatea UPV/EHU 138
MPI Depuración de errores 3 Casi todas las funciones MPI devuelven un código que puede ayudar a identificar problemas. char mens_err[MPI_MAX_ERROR_STRING]; int long_mens; cod_err = MPI_Bcast (&x, 1, MPI_INT, 0, MPI_COMM_WORLD); if (cod_err != MPI_SUCCESS) { MPI_Error_string (cod_err, mens_err, &long_mens); fprintf (stderr, “Error BC = %sn”, mens_err); fprintf (stderr, “Agur Benhurn”); MPI_Abort (MPI_COMM_WORLD, -1); // -1: error_code } Ante un error, se puede abortar o devolver el código de error. Informatika Fakultatea UPV/EHU 139
MPI Perfiles de ejecución 3 De cara a mejorar el rendimiento del programa, hay que identificar qué partes son las responsables de la mayor parte del tiempo de ejecución: hay que obtener un “perfil”. Obviamente, para obtener un buen rendimiento debemos partir de un buen algoritmo. Hay que medir tiempos: - gettimeofday, MPI_Wtime… - barreras (? ) - modificar las funciones MPI, para incluir toma de tiempos (PMPI) Informatika Fakultatea UPV/EHU 140
MPI Perfiles de ejecución Usar alguna herramienta específica de profiling: • gprof (serie) • Total. View, Paragraph, Pablo, Vampir, Paradyne… (INTEL, KAI, Porland, PALLAS…) • jumpshot (MPICH) Se puede analizar el tiempo de ejecución de cada función MPI, los patrones de comunicación. . . Informatika Fakultatea UPV/EHU 3 141
MPI ++ Hemos analizado las funciones principales de MPI, pero hay más. • Por ejemplo, es posible generar procesos de manera dinámica, y no solamente de manera estática, tal como hemos hecho a lo largo del curso. • También existen operaciones de comunicación unilateral one-sided (put, get) que permiten a un proceso acceder a la memoria local de otro, que nos permite utilizar un modelo de memoria compartida distribuida (DSM). Informatika Fakultatea UPV/EHU
MPI + Open. MP En un cluster típico, cada nodo es un “multiprocesador” de memoria compartida, bien por disponer de varios procesadores y/o porque son multicore (multithreading). Puede ser útil utilizar simultáneamente los dos modelos de programación paralela: MPI (paso de mensajes entre nodos de memoria privada) y Open. MP (hilos de memoria compartida). En una primera opción, basta con añadir a los procesos MPI las correspondientes directivas Open. MP, de tal manera que la tarea asignada a cada nodo se reparta entre los procesadores locales. Atención al modelo de memoria (variables privadas y compartidas), que es diferente en cada caso. Informatika Fakultatea UPV/EHU
Referencias > MPI • P. S. Pacheco: Parallel Programming with MPI. M. Kaufmann, 1997. • G. Barlas: Multicore and GPU programming. M. Kaufmann, 2015. • W. Gropp et al. : Using MPI. Portable Parallel Programming with the Message Passing Interface. Using MPI-2. Advanced Features of the Message Passing Interface. The MIT Press, 1999. • M. Snir et al. : MPI - The complete reference (vol. 1 /2). The MIT Press, 1998. • www. mpi-forum. org www. mpich. org www. open-mpi. org > Gestión de un cluster • T. Sterling: Beowulf Cluster Computing with Linux. The MIT Press, 2002. • J. D. Sloan: High Performance Linux Clusters with OSCAR, Rocks, open. Mosix & MPI. O’Reilly, 2005. Informatika Fakultatea UPV/EHU
- Slides: 144