1 / 69

. 5 . O TRAS A LTERNATIVAS: U nified P arallel C (resumen ).

. 5 . O TRAS A LTERNATIVAS: U nified P arallel C (resumen ). Índice. 1. Introducción. 2. Variables y punteros. Movimiento de datos. Memoria dinámica. 3. Reparto de tareas ( work sharing ). 4. Sincronización y consistencia. 5. Librería de operaciones colectivas. 6. Conclusiones.

haile
Download Presentation

. 5 . O TRAS A LTERNATIVAS: U nified P arallel C (resumen ).

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. . 5 . • OTRAS ALTERNATIVAS: • Unified Parallel C • (resumen).

  2. Índice 1. Introducción. 2. Variables y punteros. Movimiento de datos. Memoria dinámica. 3. Reparto de tareas (work sharing). 4. Sincronización y consistencia. 5. Librería de operaciones colectivas. 6. Conclusiones.

  3. Introducción Las dos herramientas habituales para desarrollar aplicaciones paralelas son OpenMP, para el caso de sistemas SMP de memoria compartida, y MPI, para las arquitecturas de memoria distribuida (también para memoria compartida). En general, es más fácil programar aplicaciones paralelas de memoria compartida que de memoria distribuida; sin embargo, es mas fácil (barato) disponer de un sistema tipo cluster (memoria distribuida) con muchos procesadores que de una máquina SMP (MPP) de muchos procesadores.

  4. Introducción  Otra alternativa: UPC (Unified Parallel C) Herramienta para desarrollar aplicaciones paralelas de memoria compartida (como OpenMP), pero para sistemas de memoriadistribuida (p.e., una red de PCs) y sin tener que explicitar la comunicación. Una extensión de C que permite efectuar operaciones rd/wr sobre memoria local y sobre memoria remota. MPI también permite efectuar operaciones de “comunicación” one-sided, put y get, para poder leer o escribir unilateralmente en la memoria “privada” de otro nodo.

  5. Introducción  Cuando es necesario acceder a una porción de memoria no local, en otro nodo del sistema, la comunicación entre nodos se efectúa de manera implícita. En nuestro cluster, para ejecutar programas UPC, igual que en el caso MPI, necesitamos que estén en ejecución en cada nodo del sistema los daemons mpd. Versión 2.14, octubre 2011 (Berkeley)

  6. P C M R red general Introducción Modelo de memoria distribuida: MPI • Procesos con su memoria local • Comunicación explícita (send / receive) Pros / contras + Control del reparto de carga y de datos + Fácil de escalar – Sobrecarga de comunicación (pocos datos) – Más difícil de programar

  7. P C bus M Introducción Modelo de memoria compartida: OpenMP • Conjunto de threads/procesos, único espacio de direccionamiento • Comunicación mediante rd y wr Pros / contras + Programación más sencilla + Comunicación sencilla entre procesos – Datos compartidos → Sincronización – “No aprovecha la localidad” – Más difícil de escalar

  8. P C M R red general Introducción Modelo de mem. compartida distribuida (PGAS): UPC • Similar al modelo de memoria compartida • Los threads tienen afinidad con partes de memoria Pros / contras + Programación y comunicación entre procesos más sencilla + Aprovechamiento de la localidad (affinity) + Más fácil de escalar – Sincronización

  9. Introducción UPC:reparto de la memoria entre los threads • Un programa UPC define un grupo de threads que se ejecutan en un espacio de direccionamientoúnico, pero que está distribuido entre ellos. • La memoria del sistema se divide en dos partes: una zona compartiday una zona privada. Cada thread tiene acceso a su parte de la memoria privada y a la memoria compartida por todos los threads. • Las variables que se encuentran en la memoria asignada a cada thread (privada o compartida) tienen afinidad (affinity) con dicho thread.

  10. T1 T2 T3 T4 privada compartida Introducción UPC:reparto de la memoria entre los threads

  11. Introducción UPC:reparto de la memoria entre los threads • Todos los threads pueden acceder a cualquier posición de la memoria compartida, por lo que la comunicación entre procesos es sencilla. • Pero, ojo, el tiempodeacceso a las diferentes zonas de memoria va a ser muy diferente, en función de dónde se encuentre físicamente dicha memoria: en el propio nodo (porción de memoria con la que tiene afinidad) o en otro nodo de la red.

  12. Introducción  UPCincluye(entre otras cosas) • un par de variables reservadas que representan el número total de threads en ejecución, THREADS, y el identificador de cada thread, MYTHREAD. • un método para repartir los datos entre los procesos, más un mecanismo para reparto estático de las iteraciones de bucles (work sharing): upc_forall. • funciones para sincronizar threads (barreras y locks). • funciones para mover bloques de datos entre threads. • funciones de comunicación global:broadcast, scatter, gather...

  13. Introducción Para compilar y ejecutar programas UPC Diferentes opciones, dependiendo de la implementación; el programa fuente puede ser .upc o .c. Añadir al comienzo #include <upc.h> • Definir el número de threads al compilar (más optimizado) > upcc –T=4 –o pr1pr1.upc > upcrunpr1 • Definir el númerode threadsal ejecutar (más “flexible”) > upcc–o pr1pr1.upc > upcrun –n4pr1 Ojo! en este caso, el reparto de las variables a los threads no debe depender del número de threads.

  14. Introducción Ejemplo: hola.upc #include <upc.h> #include <stdio.h> int main() { int X = 0; if (MYTHREAD == 1) X = 1; printf("\n Thread %d (de %d) X = %d \n\n", MYTHREAD, THREADS, X); return(0); }

  15. Índice 1. Introducción. 2. Variables y punteros. Movimiento de datos. Memoria dinámica. 3. Reparto de tareas (work sharing). 4. Sincronización y consistencia. 5. Librería de operaciones colectivas. 6. Conclusiones.

  16. Variables y punteros Dado el modelo de memoria, las variables de un programa UPC pueden ser privadas o compartidas. • Por defecto las variables son privadas, y se crea una copia de cada una de ellas en la zona de memoria privada de cada thread. •Las variables compartidas (static) se declaran como shared, y se definen en la zona de memoria común. En la propia declaración de la variable se indica la afinidad de la misma; es decir, en la memoria asignada a qué thread hay que cargarla.

  17. Variables y punteros • Si la variable shared es un escalar, se asigna a la memoria afín al thread 0. Si es un array, se va repartiendo entre los threads, con el nivel de entrelazado (block size) que se indica en la propia definición. • La definición general de un vector (matriz...) compartido es: shared [tam_bloq] tipovector[tam]; Si no se indica el tamaño de bloque, el reparto es elemento a elemento. Si se indica [], todo el vector va al thread 0. [*] hace que cada thread tenga un trozo del mismo tamaño (salvo quizás el último, que puede ser más pequeño).

  18. Variables y punteros • El elemento ide un vector shared tiene afinidad con el thread (es decir, está cargado en la memoria asignada al thread): (i/tam_bloq) mod THREADS donde tam_bloq es el tamaño del “entrelazado” de los elementos del vector entre los threads.

  19. Variables y punteros Ejemplos int X;variable privada, cada thread una copia shared int Y;variable compartida, en el thread 0 shared[]float V1[N];todo el vector V1 en el thread 0 shared float V2[N];reparto 1 a 1 = shared [1] float V2[N] shared[N/THREADS]float V3[N];reparto por trozos de tamaño N/THREADS shared[N]int A[N][N];reparto de la matriz por filas

  20. T0 T1 T2 T3 zona de memoria privada --------------------- zona de memoria común Variables y punteros Ejemplos (4 threads) X X X X int X; MIN shared int MIN; A0 A2 A4 A6 A1 A3 A5 A7 shared [2] int A[8]; shared [] int B[2]; B0 B1

  21. Variables y punteros En UPC se definen cuatro tipos de punteros, en función de dónde estén y adónde apunten (zona compartida / zona privada). ¿Dónde está el puntero? ¿Dónde está la variable apuntada?

  22. Variables y punteros Ejemplos de punteros int*P1;Puntero en zona privada apuntando a zona privada (PP). Acceso a datos privados. shared int*P2;Puntero en zona privada apuntando a zona compartida (SP). Acceso del thread local a un dato shared. int*shared P3;Puntero en zona compartida apuntando a zona privada. (PS, no utilizar). sharedint*sharedP4;Puntero en zona compartida apun-tando a zona compartida (SS). Acceso de cualquier thread a un dato shared.

  23. P4 P1 P1 P1 P2 P2 P2 P3 Variables y punteros Ejemplos de punteros Threadi private shared

  24. thread dirección bloque fase Variables y punteros Formato de un puntero (una dirección de memoria) Dos (tres) campos definen un puntero UPC a shared: - la dirección a la que apunta, en dos partes: dirección del bloque de memoria, y dirección de la palabra apuntada dentro del bloque (fase). - el thread al que apunta (aquel que tiene afinidad con la dirección apuntada).

  25. Variables y punteros Formato de un puntero (una dirección de memoria) Tres funciones permiten obtener los tres paráme-tros que definen el puntero: > void *upc_addrfield(shared void *P) dirección del bloque apuntado por P > intupc_phaseof(shared void *P) fase (posición dentro del bloque) > int upc_threadof(shared void *P) thread con afinidad con el objeto apuntado

  26. Variables y punteros •De cara a mejorar la eficiencia en los accesos a memoria, se puede convertir (hacer un casting) un puntero sharedque apunta a un objeto shared en un puntero privado, siempre que el objeto compartido esté en el mismo thread. •La aritmética de punteros tiene en cuenta cómo se han distribuido los datos entre los threads (es decir, se hace dato a dato, no thread a thread), para lo que el puntero tiene que declararse con el mismo tamaño de bloque que los datos.

  27. p1 p2 T0 T1 T2 T3 p1 p1 p1 p1 p2 p2 p2 p2 P S Variables y punteros #define N 16 shared int A[N] shared int *p1, *p2 p1 = &A[5]; p2 = p1 + 9; A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15

  28. T0 T1 T2 T3 P S Variables y punteros #define N 16 shared int A[N] shared int *shared p1 shared int *p2 p1 = &A[5]; p2 = &A[MYTHREAD]; p2 p2 p2 p2 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 A10 A11 A12 A13 A14 A15 p1

  29. T0 T1 T2 T3 p1 p2 p3 Variables y punteros #define N 16 shared[3]intA[N],*p1,*p2; shared int *p3 p1 = &A[5]; p2 = p1 + 4; p3 = p1 + 3; p1 p1 p1 p1 p2 p2 p2 p2 p3 p3 p3 p3 P A0 A3 A6 A9 A1 A4 A7 A10 A2 A5 A8 A11 A12 A15 A13 A14 S

  30. Movimiento de datos  La gestión de la memoria, distribuida entre los procesos en zonas privadas y zonas compartidas, es uno de los puntos complejos de UPC. Por ello, UPC proporciona unas cuantas funciones para mover bloques de datos entre diferentes zonas de la memoria, similares a las correspondientes funciones de C, de manera más eficiente que hacerlo dato a dato.

  31. Movimiento de datos •Para copiar datos (equivalente a memcpy): > upc_memcpy(dest,orig,tam)//tam en bytes copia de zona shared a zona shared > upc_memput(dest,orig, tam) copia de zona private a zona shared > upc_memget(dest,orig,tam) copia de zona shared a zona private •Para inicializar una zona de memoria (memset): > upc_memset(dest,char,tam) inicializa memoria shared con char

  32. Memoria dinámica Funciones UPC para la gestión dinámica de la memoria • Recordad: cada thread tiene una zona de memoria privada, y una zona de memoria compartida accesible por todos los threads. • Para el caso de la memoria privada de cada thread se utilizan las mismas funciones que en C. • Para el caso de memoria compartida (shared) las funciones de reserva de memoria pueden ser globales –colectivas o individuales- o locales.

  33. Memoria dinámica • Funciones globales(reservan memoria en todos los threads): a. Colectiva: debe ser llamada por todos los threads >sharedvoid *upc_all_alloc(nblq,nbytes); Los nblq bloques de nbytes de memoria se reparten r.r. entre los threads (parámetros de tiposize_t). Devuelve un puntero a shared, el mismo en todos los threads, que luego podemos asignar, por ejemplo, a un puntero privado en cada thread.

  34. Memoria dinámica • Funciones globales(reservan memoria en todos los threads): b. Individual: sólo la threads >sharedvoid*upc_global_alloc(nbloq,nbytes); El thread que ejecuta la función recibe un puntero a un bloque de datos shared, de tamaño nbloq×nbytes, repartido por todos los threads.

  35. ptr ptr ptr P S ptr ptr ptr P S Memoria dinámica • Ejemplos: shared [N] int *ptr; ptr=(shared[N]int*)upc_all_alloc(THREADS,N*sizeof(int)); shared [N] int *ptr; ptr=(shared[N]int*)upc_global_alloc(THREADS,N*sizeof(int));

  36. Memoria dinámica • Función local(reserva memoria sólo en la parte shared del thread que ejecuta la función): > shared void *upc_alloc(nbytes); Devuelve un puntero al bloque de datos shared de la memoria afín. •Para liberar memoria: > void upc_free(shared void *ptr);

  37. Índice 1. Introducción. 2. Variables y punteros. Movimiento de datos. Memoria dinámica. 3. Reparto de tareas (work sharing). 4. Sincronización y consistencia. 5. Librería de operaciones colectivas. 6. Conclusiones.

  38. Reparto de tareas (work sharing) UPC sólo dispone de un constructor para repartir tareas; en concreto, las iteraciones de un bucle for, de forma estática. El modelo de reparto es SPMD, y se busca favorecer la localidad en los accesos (afinidad), teniendo en cuenta cómo están distribuidos los datos (declaración de variables). No se efectúa ningún análisis de dependencias. Las iteraciones deben ser independientes, ya que no se controla el orden de ejecución.

  39. Reparto de tareas El constructor de reparto de las iteraciones de un bucle es: upc_forall(inic; fin; incr; afinidad) El parámetro de afinidad indica qué thread ejecutará la iteración correspondiente. Puede ser: - un entero (n); la iteración del bucle se asigna al thread n%THREADS. - una dirección (&A); la iteración se asigna al threadque tiene afinidad con esa dirección (al que tiene la variable en su zona de memoria).

  40. Reparto de tareas Ejemplos: upc_forall(i=0;i<N;i++;i) A[i]=A[i]+1; El parámetro de afinidad indica que el reparto va a ser entrelazado, iteración a iteración, ya que el bucle equivale a: for(i=0;i<N;i++) if(MYTHREAD== i%THREADS)A[i]=A[i]+1;

  41. Reparto de tareas Ejemplos: upc_forall(i=0;i<N;i++;i*THREADS/N) A[i]=A[i]+1; Ahora el reparto de las iteraciones es consecutivo. upc_forall(i=0;i<N;i++;&A[i]) A[i]=A[i]+1; La iteración i la ejecutará el thread que tenga afinidad con A[i], es decir el que tenga la variable en “su” memoria.

  42. Reparto de tareas Para el caso de bucles anidados, sólo se repartirá la ejecución del bucle upc_forall más externo; el resto se repetirá en todos los threads. Si no se indica el parámetro de afinidad, todos los threads ejecutarán todas las iteraciones del bucle upc_forall. upc_forallno lleva una barrera de sincronización al final; si se necesita, hay que añadir la correspondiente función.

  43. A se reparte por filas 1 elemento de B y de X por thread (round robin) reparto estático entrelazado 1 accesos i locales Reparto de tareas Ejemplo #include <upc.h> #defineN 100*THREADS; shared [N] double A[N][N] shared double B[N], X[N] void main (int argc, char **argv) { int i, j; /* inicializaciones */ upc_forall(i=0; i<N; i++; i) for (j=0; j<N; j++;) B[i] += A[i][j] * X[j]; }

  44. Índice 1. Introducción. 2. Variables y punteros. Movimiento de datos. Memoria dinámica. 3. Reparto de tareas (work sharing). 4. Sincronización y consistencia. 5. Librería de operaciones colectivas. 6. Conclusiones.

  45. Sincronización y consistencia Al utilizarse un modelo de memoria compartida, es necesario disponer de algún mecanismo para poder sincronizar el acceso de los threads a las variables compartidas (cerrojos) y para sincronizar la ejecución de los threads de manera global (barreras). Además, debemos conocer (y poder controlar) el modelo de consistencia de la memoria bajo el cual se van a ejecutar los programas.

  46. Sincronización y consistencia Funciones de sincronización que ofrece UPC: 1Barreras(dos tipos) - bloqueante: upc_barrier; - no bloqueante: upc_notify; upc_wait; (para solapar cálculo y sincronización) (pueden llevar un entero -o una expresión que se evalúe a un entero- para identificar unas llamadas de otras: upc_notify 1)

  47. Sincronización y consistencia Funciones de sincronización que ofrece UPC: 2Cerrojos Las variables cerrojo son de tipo upc_lock_t,sólo manejables a través de punteros: upc_lock_t *C; Para crear un puntero a un cerrojo: todos:C = upc_all_lock_alloc(); uno: C = upc_global_lock_alloc(); Para liberar memoria del cerrojo: upc_lock_free(C);

  48. Sincronización y consistencia Funciones de sincronización que ofrece UPC: 2 Cerrojos Las dos funciones típicas con cerrojos: upc_lock(C); upc_unlock(C); Para mirar cómo está el cerrojo: upc_lock_attempt(C);1: acierto / 0: fallo

  49. Sincronización y consistencia Modelo de consistencia. Recordad: > orden de los accesos a memoria > cuándo se ven los cambios de las variables compartidas UPC pemite trabajar con los dos modelos típicos de consistencia: - strict: orden secuencial estricto de las operaciones de memoria: todos ven los cambios antes de poder acceder (no se optimiza!). -relaxed: accesos no “controlados” (por defecto, como si sólo hubiera un proceso); el usuario debería sincronizarlos.

  50. Sincronización y consistencia Modelo de consistencia Podemos definir la consistencia: • para todo el programa #include<upc_strict/relaxed.h> • para un bloque básico concreto del programa #pragma upc strict/relaxed • para una variable en concreto strict shared int X;

More Related