Intro to UPC, HPF and OpenACCvicente/tcc/UPC-HPF-OpenACC.pdfIntro to UPC, HPF and OpenACC FIM -...

Post on 18-Aug-2020

6 views 0 download

Transcript of Intro to UPC, HPF and OpenACCvicente/tcc/UPC-HPF-OpenACC.pdfIntro to UPC, HPF and OpenACC FIM -...

Intro to UPC, HPFand OpenACC

FIM - 2012/13

Vicente Martín v0.0

High Performance Computing

Contents

● HPF: High Performance Fortran.● UPC: Unified Parallel C

Taxonomy

OpenMPUPCHPF

MPIUPCHPF

UPC en Triqui 1-2-3-4: – Berkeley UPC

– Añadir /usr/local/berkeley_upc/bin a vuestro PATH

– Comprobadlo con upcc -V ● Nota 1: El UPC esta configurado para usar por

detras los compiladores de Intel. Aseguraos que teneis las variables bien (ejecutad iccvars.sh intel64 )

HPF en Arturo:

Irrelevant: Arturo just died. – Se usa el driver normal del compilador.

– Se utiliza la opción -hpf

– Otras opciones: distribuir en nn procesadores virtuales: -distribute nn

– Librería de paso de mensajes por debajo de HPF : -hpf_target cmpi

– Ejecución en nn procesadores: dmpirun -np nn

UPC● Extension del ANSI C con ideas del modelo de espacio de

direcciones global particionado: Memoria compartida con información de qué es local y qué es remoto.

● De nuevo un consorcio: Universidades, laboratorios y fabricantes. Especificación 1.0 en 2001, 1.1 en 2003. 1.2

● Hilos de ejecución + memoria privada del hilo + memoria global dividida en particiones + relación de afinidad entre particiones e hilos (la partición reside en el espacio de memoria lógico del hilo) .

● Punteros compartidos con acceso a todo el espacio global. Posibilidad de especificar consistencia estricta o relajada durante el acceso a posiciones compartidas por varios hilos.

HPF● Lenguaje de programación completo para programación

tipo paralelismo de datos (modelo SPMD). ● Objetivos: Buen rendimiento, portabilidad,

compatibilidad con el lenguaje base (f95). Tanto en máquinas de memoria compartida como distribuida.

● Entra en forma de directivas (salvo intrínsecos nuevos) en el programa fuente.

● Especificación 1.0 en 1993. La 2.0 en 1997. La mayoría de implementaciones son subset de 1.1

● Proceso de compilación muy complejo, rendimiento difícil de prever. Futuro incierto.

Ejemplos.● Para obtener una idea de estos paradigmas, de su

filosofía, complejidad y rendimiento, los siguientes programas calculan el valor de la integral usando una suma de rectángulos:

– En el caso de HPF se dan varias declaraciones de la función, todas correctas, para estudiar el distinto rendimiento de cada una (en el ejemplo sólo se usa una, inlined)

=∫0

1 4

1x2dx

MPI program main include "mpif.h" double precision PI25DT parameter (PI25DT = 3.141592653589793238462643d0) double precision mypi, pi, h, sum, x, f, a double precision starttime, endtime integer n, myid, numprocs, i, ierrc function to integrate f(a) = 4.d0 / (1.d0 + a*a)

call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)

10 if ( myid .eq. 0 ) then print *, 'Enter the number of intervals: (0 quits) ' read(*,*) n endifc broadcast n starttime = MPI_WTIME() call MPI_BCAST(n,1,MPI_INTEGER,0, & MPI_COMM_WORLD,ierr)c check for quit signal if ( n .le. 0 ) goto 30c calculate the interval size h = 1.0d0/n sum = 0.0d0

program main include "mpif.h" double precision PI25DT parameter (PI25DT = 3.141592653589793238462643d0) double precision mypi, pi, h, sum, x, f, a double precision starttime, endtime integer n, myid, numprocs, i, ierrc function to integrate f(a) = 4.d0 / (1.d0 + a*a)

call MPI_INIT(ierr) call MPI_COMM_RANK(MPI_COMM_WORLD, myid, ierr) call MPI_COMM_SIZE(MPI_COMM_WORLD, numprocs, ierr)

10 if ( myid .eq. 0 ) then print *, 'Enter the number of intervals: (0 quits) ' read(*,*) n endifc broadcast n starttime = MPI_WTIME() call MPI_BCAST(n,1,MPI_INTEGER,0, & MPI_COMM_WORLD,ierr)c check for quit signal if ( n .le. 0 ) goto 30c calculate the interval size h = 1.0d0/n sum = 0.0d0

do 20 i = myid+1, n, numprocs x = h * (dble(i) - 0.5d0) sum = sum + f(x) 20 continue mypi = h * sumc collect all the partial sums call MPI_REDUCE(mypi,pi,1,MPI_DOUBLE_PRECISION, & MPI_SUM,0, MPI_COMM_WORLD,ierr)c node 0 prints the answer. endtime = MPI_WTIME() if (myid .eq. 0) then print *, 'pi is ', pi, ' Error is', abs(pi - PI25DT) print *, 'time is ', endtime-starttime, ' seconds' endif goto 10 30 call MPI_FINALIZE(ierr) stop end

do 20 i = myid+1, n, numprocs x = h * (dble(i) - 0.5d0) sum = sum + f(x) 20 continue mypi = h * sumc collect all the partial sums call MPI_REDUCE(mypi,pi,1,MPI_DOUBLE_PRECISION, & MPI_SUM,0, MPI_COMM_WORLD,ierr)c node 0 prints the answer. endtime = MPI_WTIME() if (myid .eq. 0) then print *, 'pi is ', pi, ' Error is', abs(pi - PI25DT) print *, 'time is ', endtime-starttime, ' seconds' endif goto 10 30 call MPI_FINALIZE(ierr) stop end

OMP Program Integral ! El metodo de la suma de Riemann en version Open MP

Integer(Kind(1)) :: n,i Real( Kind(1.D0)) :: w, x, suma, pi, a Integer :: InitialClock, FinalClock, TicksPerSecond

Print *,' Numero de intervalos=' Read *,n

w = 1.0d0/n suma = 0.0d0

Call System_Clock(InitialClock)

!$OMP PARALLEL DO PRIVATE(x), SHARED(w), REDUCTION(+: suma)

Do i=1, n

x= w * (i-0.5D0) suma = suma + f(x) End Do

Call System_Clock(FinalClock,TicksPerSecond)

Print *,' Segundos :', Float(FinalClock-InitialClock)/(Float(TicksPerSecond)) Pi = w * suma Print *,' Pi= ' , Pi

End

Program Integral ! El metodo de la suma de Riemann en version Open MP

Integer(Kind(1)) :: n,i Real( Kind(1.D0)) :: w, x, suma, pi, a Integer :: InitialClock, FinalClock, TicksPerSecond

Print *,' Numero de intervalos=' Read *,n

w = 1.0d0/n suma = 0.0d0

Call System_Clock(InitialClock)

!$OMP PARALLEL DO PRIVATE(x), SHARED(w), REDUCTION(+: suma)

Do i=1, n

x= w * (i-0.5D0) suma = suma + f(x) End Do

Call System_Clock(FinalClock,TicksPerSecond)

Print *,' Segundos :', Float(FinalClock-InitialClock)/(Float(TicksPerSecond)) Pi = w * suma Print *,' Pi= ' , Pi

End

UPC/*Copyright (C) 2000 Chen Jianxun, Sebastien Chauvin, Tarek El-Ghazawi */#include <upc.h>#include <upc_relaxed.h> /*modo por omision, no es necesario ponerlo*/ #include <math.h>

#define N 32767

#define f(x) 1/(1+x*x)

upc_lock_t l;shared float pi;

void main(void){ float local_pi=0.0; int i;

upc_forall(i=0; i<N; i++; i%THREADS) local_pi+=(float)f((0.5+i)/N);

local_pi*=(float)4.0/N;

upc_lock(&l); pi+=local_pi; upc_unlock(&l);

upc_barrier 1; // Ensure all is done

if (MYTHREAD==0) printf("PI=%10.9f\n",pi);}

/*Copyright (C) 2000 Chen Jianxun, Sebastien Chauvin, Tarek El-Ghazawi */#include <upc.h>#include <upc_relaxed.h> /*modo por omision, no es necesario ponerlo*/ #include <math.h>

#define N 32767

#define f(x) 1/(1+x*x)

upc_lock_t l;shared float pi;

void main(void){ float local_pi=0.0; int i;

upc_forall(i=0; i<N; i++; i%THREADS) local_pi+=(float)f((0.5+i)/N);

local_pi*=(float)4.0/N;

upc_lock(&l); pi+=local_pi; upc_unlock(&l);

upc_barrier 1; // Ensure all is done

if (MYTHREAD==0) printf("PI=%10.9f\n",pi);}

HPF Program Integral! El metodo de la suma de Riemann en version HPF Integer(Kind(1)) :: n,i Real( Kind(1.D0)) :: w, x, suma, pi, a Real(Kind(1.D0)), Allocatable :: Puntos(:), funcion(:) Integer :: InitialClock, FinalClock, TicksPerSecond

!HPF$ PROCESSORS P(4)!HPF$ DISTRIBUTE (BLOCK) ONTO P :: Puntos , Funcion

Print *,' Numero de intervalos=' Read *,n Allocate (Puntos(n)) Allocate (funcion(n))

w = 1.0d0/n suma = 0.0d0 Print *,' *** FORALL + Intrinseco SUM'

Call SYSTEM_CLOCK(InitialClock) !HPF$ INDEPENDENT ForAll ( i=1:n ) funcion(i) = 4.0D0/(1.0D0+ (w * (i-0.5D0))**2) suma = SUM(funcion)

Call SYSTEM_CLOCK(FinalClock)

Print *,' Segundos :', &Float(FinalClock-InitialClock)/Float(TicksPerSecond) Pi = w * suma Print *,' Pi= ' , Pi

Program Integral! El metodo de la suma de Riemann en version HPF Integer(Kind(1)) :: n,i Real( Kind(1.D0)) :: w, x, suma, pi, a Real(Kind(1.D0)), Allocatable :: Puntos(:), funcion(:) Integer :: InitialClock, FinalClock, TicksPerSecond

!HPF$ PROCESSORS P(4)!HPF$ DISTRIBUTE (BLOCK) ONTO P :: Puntos , Funcion

Print *,' Numero de intervalos=' Read *,n Allocate (Puntos(n)) Allocate (funcion(n))

w = 1.0d0/n suma = 0.0d0 Print *,' *** FORALL + Intrinseco SUM'

Call SYSTEM_CLOCK(InitialClock) !HPF$ INDEPENDENT ForAll ( i=1:n ) funcion(i) = 4.0D0/(1.0D0+ (w * (i-0.5D0))**2) suma = SUM(funcion)

Call SYSTEM_CLOCK(FinalClock)

Print *,' Segundos :', &Float(FinalClock-InitialClock)/Float(TicksPerSecond) Pi = w * suma Print *,' Pi= ' , Pi

CONTAINS ! Funcion pura. Recibe UN ESCALAR y ! devuelve UN ESCALAR.

PURE REAL(Kind(1.D0)) FUNCTION f(a)

Real(Kind(1.D0)), INTENT(IN) :: a

f = 4.0D0/(1.D0 + a*a)

End FUNCTION f

! Funcion pura. Recibe UN VECTOR y devuelve ! otro VECTOR.

PURE Function fv(a)

Real(Kind(1.D0)), Dimension(:), Intent(IN) :: a Real(Kind(1.d0)),Dimension(Size(a)) :: fv

fv = 4.0D0/(1.D0 + a*a)

End Function fv End PROGRAM Integral

CONTAINS ! Funcion pura. Recibe UN ESCALAR y ! devuelve UN ESCALAR.

PURE REAL(Kind(1.D0)) FUNCTION f(a)

Real(Kind(1.D0)), INTENT(IN) :: a

f = 4.0D0/(1.D0 + a*a)

End FUNCTION f

! Funcion pura. Recibe UN VECTOR y devuelve ! otro VECTOR.

PURE Function fv(a)

Real(Kind(1.D0)), Dimension(:), Intent(IN) :: a Real(Kind(1.d0)),Dimension(Size(a)) :: fv

fv = 4.0D0/(1.D0 + a*a)

End Function fv End PROGRAM Integral

Introducción a HPF● El modelo básico de funcionamiento de HPF se

conoce como SPMD (Single Program Multiple Data). Un programa único se ejecuta simultáneamente en una serie de procesadores actuando sobre conjuntos de datos distintos.

● Por ello, las principales directivas de HPF (marcadas con !HPF$) se ocupan de distribuir conjuntos de datos alineados (con una relación de proximidad de uso) entre si sobre arrays regulares de procesadores que, en principio, son virtuales.

Modelo de asignación de datos a procesadores físicos.

● ALIGN: Especifica elementos que, si es posible, serán asignados al mismo procesador, de modo que operaciones entre elementos alineados mantendrán localidad de referencias..– Las dimensiones “*” no se

distribuyen: se colapsan

Directivas: Distribución de Datos

Dbpp: Foster

Directivas: Creación de Disposiciones de Procesadores

● Para crear las disposiciones regulares (cartesianas) de procesadores abstractos sobre las que se distribuirán los datos alineados.

● Directiva PROCESSORS:

– !HPF$ processors linea(4)

– !HPF$ processors red(2,2)● La primera crea un vector con cuatro procesadores.

La segunda, una matriz de 2x2 procesadores.

Directivas: Creación de Disposiciones de Procesadores

● Se puede averiguar el número de procesadores disponibles y su forma usando los intrínsecos:– NUMBER_OF_PROCESSORS(): Se puede utilizar dentro de

una línea iniciada con !HPF$ como en ● !HPF$ Processors Linea(NUMBER_OF_PROCESSORS())

– PROCESSORS_SHAPE(): Si no aparece una directiva PROCESSORS, el sistema usará una por omisión, que no tiene por qué ser óptima. Si el HW sobre el que funciona es un multiprocesador UMA, la forma de distribución de procesadores no tendrá mucho impacto.

Directivas: Distribución de Datos

● Sobre las disposiciones de procesadores se distribuyen los datos alineados usando DISTRIBUTE.

● Admite como parámetros BLOCK, CYCLIC o * (que expresa que no habrá distribución) BLOCK y CYCLIC admiten un argumento indicando el tamaño de bloque o el ciclo.

● Las únicas tablas distribuibles son los “objetivos de alineamiento final”: Referencias para los alineamientos de las demás tablas y que ellas mismas no están alineadas con ninguna.

Ejemplo de Directivas Básicas.● Imaginemos que hemos definido las disposiciones linea(4) y red(2,2) como en los ejemplos anteriores y que tenemos una matriz M de 8x8.

Efecto en las Comunicaciones.● Estas tres directivas especifican cuales serán las

comunicaciones en una operación dada.● Ejemplo (del estándar), si tenemos el siguiente trozo de

código:Real A(1000),B(1000),C(1000),X(500),Y(0:501)Integer INX(1000)

!HPF$ Processors PROCS(10)!HPF$ DISTRIBUTE A(BLOCK) ONTO PROCS!HPF$ DISTRIBUTE B(BLOCK) ONTO PROCS!HPF$ DISTRIBUTE INX(BLOCK) ONTO PROCS!HPF$ DISTRIBUTE C(CYCLIC) ONTO PROCS!HPF$ ALIGN x(I) WITH Y(I-1)

A(I) = B(I) ! (1)X(I) = Y(I-1) ! (2)A(I) = C(I) ! (3)A(I) = A(I-1) + A(I) + A(I+1) ! (4)C(I) = C(I-1) + C(I) + C(I+1) ! (5)X(I) = Y(I) ! (6)A(I) = A(INX(I)) + B(INX(I)) ! (7)

Real A(1000),B(1000),C(1000),X(500),Y(0:501)Integer INX(1000)

!HPF$ Processors PROCS(10)!HPF$ DISTRIBUTE A(BLOCK) ONTO PROCS!HPF$ DISTRIBUTE B(BLOCK) ONTO PROCS!HPF$ DISTRIBUTE INX(BLOCK) ONTO PROCS!HPF$ DISTRIBUTE C(CYCLIC) ONTO PROCS!HPF$ ALIGN x(I) WITH Y(I-1)

A(I) = B(I) ! (1)X(I) = Y(I-1) ! (2)A(I) = C(I) ! (3)A(I) = A(I-1) + A(I) + A(I+1) ! (4)C(I) = C(I-1) + C(I) + C(I+1) ! (5)X(I) = Y(I) ! (6)A(I) = A(INX(I)) + B(INX(I)) ! (7)

● A, B, INX se distribuye en bloque sobre PROCS:

A(1:100) en PROCS(1); A(101:200) en PROCS(2) etc.

● C se distribuye cíclicamente: C(1), C(11),..., C(991) irán a PROCS(1). C(2), C(12),...,C(992) a PROCS(2)

● La distribución de X e Y sobre PROCS no está especificada, pero sí su alineamiento relativo.

● Las asignaciones generarán, en principio, las siguientes asociaciones:– (1) Para todo i, A(i) y B(i) deberán ir al mismo procesador: No

se generan comunicaciones.

– (2) X e Y no están asignados a ningún procesador específico, pero sí se ha concretado su alineamiento relativo, de modo que no hay comunicación sea cual sea la distribución de X e Y sobre PROCS.

– (3) Parecido a (1), pero la distribución cíclica de C hace que A(i) coincida con C(i) sólo una de cada 10 veces: mucha comunicación.

– (4) La mayoría de los accesos a A son locales. Sólo hay comunicaciones en la frontera (i=100*k+1, i=100*k; k=1,2...9)

– (5) Parecida a (4), pero con C en distribución cíclica C(i-1) y C(i+1) siempre en procesadores distintos, por lo que siempre se producirán comunicaciones.

– (6) X e Y sólo están afectados por un ALIGN que especifica que X(i) e Y(i-1) están en el mismo procesador, lo cual no dice nada sobre la posición relativa de X(i) e Y(i), así que nada podemos decir sobre las comunicaciones.

– (7) A(INX(i)) estará en el mismo procesador que B(INX(i)). Lo mismo vale para A(i), B(i) e INX(i), pero sin conocer los valores de los elementos INX(i), nada sabemos de la relación entre A(i), A(INX(i)) y B(INX(i)).

Notar que cuando se dice “asignados al mismo procesador” se refiere a un procesador abstracto, la asignación de varios de estos a un sólo procesador físico puede aliviar los efectos de las comunicaciones en algunos casos.

Facilidades Adicionales con Alineamientos● TEMPLATE para expresar alineamientos múltiples

relativos con referencia a un objeto mayor sin necesidad de reservar espacio para éste. No habiendo reserva de memoria no puede ser usado en argumentos de subrutinas ni bloques COMMON.

Integer A(10,20), B(12,8), C(10,10)!HPF$ TEMPLATE T(20,35)!HPF$ ALIGN A(I,J) WITH T(I+1,J+1)!HPF$ ALIGN B(I,J) WITH T(I+5,J+18)!HPF$ ALIGN C(I,J) WITH T(I+1,J+24)!HPF$ DISTRIBUTE T(BLOCK,CYCLIC)

Integer A(10,20), B(12,8), C(10,10)!HPF$ TEMPLATE T(20,35)!HPF$ ALIGN A(I,J) WITH T(I+1,J+1)!HPF$ ALIGN B(I,J) WITH T(I+5,J+18)!HPF$ ALIGN C(I,J) WITH T(I+1,J+24)!HPF$ DISTRIBUTE T(BLOCK,CYCLIC)

Ejemplo: Duplicación de Datos

● Un vector, de tamaño relativamente pequeño, que se accede muchas veces, bien en su totalidad o de manera impredecible, junto con los elementos de una matriz de mayor tamaño es copiado junto con cada trozo repartido de la matriz grande. De éste modo los accesos al vector son locales.– Notar que la duplicación de datos, si el objeto se actualiza

con frecuencia, puede producir una mayor carga de comunicaciones. Hay que hacer un análisis previo de las ventajas/inconvenientes que puede producir.

● Ejemplo (Ewing, Koelbel): El vector lut, alineado con cada elemento de la matriz a es distribuido en una red de 2x2 procesadores.

Otras Construcciones ● HPF contiene más elementos para expresar paralelismo o

modos de ejecución. Algunos están en f95.– Sentencia FORALL: Para hacer asignaciones en paralelo.

– Directiva INDEPENDENT: Garantiza que la ejecución de iteraciones no tienen dependencias unas de otras.

– Atributo PURE: Las funciones así marcadas grantizan que no tienen efectos que impidan su uso en construcciones paralelas.

– Operaciones globales en arrarys: SUM, MAXLOC, MINLOC, etc.

– HPF_GLOBAL, HPF_LOCAL, HPF_SERIAL. Declaraciones para EXTRINSIC que garantizan un tipo de ejecución de la rutina marcada.

● Sentencia FORALL (también en f95):

– FORALL (especificaciones-índices [,máscara]) Asignación

– FORALL (especificaciones-índices [,máscara])

Sentencias de asignación

END FORALL

– Ejemplo: FORALL (I=1:N,J=1:N,A(J,I).NE.0.0) B(I,J)=1/A(J,I)

● FORALL se evalua en cuatro pasos: 1) Calcular el conjunto válido de índices (especificaciones-índices). 2) Definir el conjunto activo de índices (máscara). 3) Evaluar los lados derecho de la asignación. 4) Realizar las asignaciones.

● Claves: Todos los elementos de cada paso deben poder ser realizados en cualquier orden. Cada paso debe ser completado antes del siguiente.

● FORALL (I=1:N) S=V(I) !MAL • FORALL (I=1:N,J=1:I) A(I,J)=A(I,J)/A(I,I) !No Conforme • FORALL (I=1:N,J=1:N,J.LE.I) A(I,J)=A(I,J)/A(I,I) !ok

● Directiva INDEPENDENT:Aplicable a bucles DO o sentencias FORALL.

– En un bucle Do significa que índices distintos del bucle no interfieren y se pueden calcular en paralelo.

● Declaración de una variable como NEW para significar que esa variable se usa sólo dentro de cada iteración del bucle DO: su valor en distintas iteraciones no tiene relación ninguna.

– En un FORALL elimina las sincronizaciones obligadas entre las evaluaciones de los lados derechos y asignaciones de los izquierdos.

● No habrá mucha ganancia si no hay diferencias significativas de tiempos entre distintas evaluaciones de los lados derechos para distintos índices activos.

Ewing/EPCC

Ewing/EPCC

● El modo de ejecución con el que corre una rutina puede ser modificado a través de la declaración EXTRINSIC.

● Esto es muy útil cuando se desea usar una rutina altamente optimizada para una máquina secuencial desde un programa HPF o hay una sección crítica de código que sólo deba ser ejecutada por una CPU.

– EXTRINSIC (HPF_GLOBAL) Modo por omisión si no se declara. Es el modelo de ejecución normal, ya comentado. I/O en el procesador 0.

– EXTRINSIC (HPF_LOCAL) Todos los procesadores corren la rutina con acceso exclusivamente local a datos (normalmente cada sección de local de los datos distribuidos). Útil para usar rutinas secuenciales en cada nodo. I/O en cada nodo.

– EXTRINSIC (HPF_SERIAL) Sólo un procesador corre la rutina.

Nuevas Librerías.● HPF_LIBRARY y HPF_LOCAL_LIBRARY

– Las funciones de HPF_LIBRARY pueden ser llamadas desde cualquier modo de funcionamiento.

– HPF_LOCAL_LIBRARY sólo desde rutinas marcadas Extrinsic(HPF_LOCAL). Atiende a la conversión entre los modos de funcionamiento HPF_GLOBAL y HPF_LOCAL: MY_PROCESSOR, PHYSICAL_TO_ABSTRACT, LOCAL_TO_GLOBAL y añade nuevas funciones de reducción, combinación/dispersión. El número de funciones implementadas varía de sistema en sistema. Es normal disponer de funciones de información: HPF_DISTRIBUTION, HPF_ALIGNMENT y HPF_TEMPLATE.

UPC● Extension del ANSI C con ideas del modelo de espacio de

direcciones global particionado: Memoria compartida con información de qué es local y qué es remoto.

● De nuevo un consorcio: Universidades, laboratorios y fabricantes. Especificación 1.0 en 2001, 1.1 en 2003. 1.2

● Hilos de ejecución + memoria privada del hilo + memoria global dividida en particiones + relación de afinidad entre particiones e hilos (la partición reside en el espacio de memoria lógico del hilo) .

● Punteros compartidos con acceso a todo el espacio global. Posibilidad de especificar consistencia estricta o relajada durante el acceso a posiciones compartidas por varios hilos.

Modelo de Programación/Memoria

● DSM: Distributed Shared Memory. Pensado para hacer uso eficiente de la localidad de datos en máquinas NUMA.

● Hilos de ejecución con memoria privaday compartida.●A la memoria compartida no se la supone un acceso idéntico desde cualquier hilo:Se establece una afinidad entre determinadas secciones de la memoria y determinadoshilos, lo que permite mantener una ciertalocalidad de referencias para mejorar elrendimiento.

● UPC se pretende que sea fácil de usar: Funciona como una extension no demasiado grande de C.

– No presupone un conocimiento explícito del HW de la máquina... aunque pretende funcionar bien en todo, evidentemente hay arquitecturas donde se comportará mejor que en otras.

● Se pretende que sea eficiente: Permite controlar la localidad de referencias controlando la afinidad entre zonas de memoria e hilos de ejecución.

– La localidad se controla mediante las declaraciones de las variables (puede ser a distintos niveles).

– La escritura/lectura remota son básicamente sentencias de asignación (y algunas funciones)

Elementos del Lenguaje: palabras reservadas

Keywords: MYTHREAD strict relaxed THREADS shared UPC_MAX_BLOCK_SIZEFunctions: upc_addr_field upc_lock_attempt upc_all_lock_alloc upc_lock_free upc_global_lock_alloc upc_lock_t upc_all_alloc upc_memcpy upc_alloc upc_memget upc_barrier upc_memput upc_blocksizeof upc_memset upc_elemsizeof upc_notify upc_fence upc_phaseof upc_forall upc_threadof upc_free upc_resetphase upc_global_alloc upc_unlock upc_global_exit upc_wait upc_localsizeof

+ cabeceras (<upc_strict.h>, <upc_relaxed.h>,

<upc.h> ) + variables de entorno + funciones de librería

Elementos del Lenguaje.Variables: MYTHREAD : Entero con el número de índice que identifica el hilo que está ejecutando el código en ese momento. THREADS : Número total de hilos que ejecutan el programa. UPC_MAX_BLOCK_SIZE : Tamaño máximo del bloque de memoria a repartir

(dependiente del compilador)

Declaración de variables:Variables in UPC are by default private.

shared: La variable es compartida. Si no se especifica nada, por omisión se asignan a la memoria del hilo 0. Shared variables are static.

Modelo de consistencia de memoria:

strict: Los datos compartidos son sincronizados siempre antes de cada nuevo acceso. Si un hilo esta accediendo a ellos, los demás deben esperar a que termine. El compilador no está autorizado a reordenar los accesos para su optimización. relaxed: Los hilos no tienen ninguna restricción de acceso (modo por omisión).

Modelo de consistencia de memoria: Scoping

strict y relaxed: pueden marcar programas completos, bloques o solo variables.

#include <upc_strict.h> void main(){ }

#include <upc_relaxed.h>void main(){ }

Declaraciones globales

#include <upc_relaxed.h>shared int counter=0;void main(){ ... { #pragma upc strict counter++; printf(“Counter= %d\n”, counter); } ...}

El bloque más interno en modo estricto. El resto en relajado.

#include <upc_relaxed.h>strict shared int counter;void main(){ counter++; printf(“Counter now shows %d\n”, counter); } La variable counter en modo estricto, todo lo

demás en relajado.

Datos compartidos.● shared [tamaño-bloque] tipo nombre-variable

● Si no se especifica [tamaño-bloque] se asume [1], si se especifica [] se asume que todo va al hilo 0.

● shared int x[11];

int y;

shared int z;

(sobre 5 hilos)

● shared [2] int A[4][2];

(sobre 3 hilos)

● Sincronización:– Barreras bloqueantes: upc_barrier cada hilo espera

a que todos los demás lleguen al punto de sincronización.

...shared int a=0;int b;int main(){...do {ACTUALIZA a}upc_barrier;b=a;...}

Sin la barrera no habría garantia de que la variableprivada b tuviese el mismo valor que la variablecompartida a en todos los hilos

● Barreras en fases (no bloqueantes, split-phase):● upc_notify id● upc_wait id

– upc_notify notifica al resto de los hilos que ha pasado por allí y continua su proceso hasta que se encuentra con el upc_wait correspondiente, donde cada hilo espera hasta que todos los demás han pasado por el upc_notify.

– The idea is to use the upc_notify to mark that the thread that just passed has completed working with the data that must be synchronized and the rest of the threads can use them while not stopping the thread to work on local data that does not need to be synchronized.

● Locks: To control the access to critical sections of the code. A lock is an opaque object, implementation dependent and can be manipulated only through pointers.

– void upc_lock(upc_lock_t *lock)

– void upc_unlock(upc_lock_t *lock)● int upc_lock_attempt(upc_lock_t *lock) Para evitar

esperas: devuelve 1 si cierra y 0 si no ha podido.● upc_lock_free

– Los cierres pueden ser creados colectivamente: La siguiente llamada es ejecutada por todos los hilos y crea un cerrojo (abierto). Todos los hilos reciben el puntero.

● upc_lock_t * upc_all_lock_alloc(void)

– No colectivo: La llamada solo la realiza un hilo y solo el hilo que lo ejecuta recibe el puntero, a no ser que la variable que lo reciba haya sido declarada compartida.

● upc_lock_t * upc_global_lock_alloc(void)

● Distribución de trabajo:– upc_forall (i_inicial; condición; incremento, i_afinidad)

– La iteración i la realiza el hilo por el que tiene afinidad: i%THREADS

– Ej: Trabajo asignado cíclicamente: ● upc_forall (i=0; i<N; i++; i)

– Cada hilo recibe un bloque de trabajo● upc_forall (i=0; i<N; i++; i*THREADS/N)

– La iteración la hace el hilo que tiene afinidad con la dirección &a[i]:

● upc_forall (i=0; i<N; i++; &a[i])

#include<upc_relaxed.h>#define N 100*THREADSshared [N] double A[N][N];shared double b[N], x[N];void main(){int i,j;/* reading the elements of matrix A and thevector x and initializing the vector b to zeros*/upc_forall(i=0;i<N;i++;i)for(j=0;j<N;j++)b[i]+=A[i][j]*x[j] ;}

A[n][n] está distribuido cíclicamente por filas (una fila por hilo, cíclico). b[n] y x[n] están distribuidos cíclicamente (elemento a elemento).En el upc_forall el acceso será local a b[i] y A[i][j] en la iteración i.

● Dynamical Memory Allocation:– shared void *upc_all_alloc (size_t nblocks, size_t nbytes)

● Collectively allocate one global shared space. All threads make the same call with the same arguments. Each thread gets the same pointer to the same memory position.

– shared void *upc_global_alloc (size_t nblocks, size_t nbytes)

● Not collective: Allocate multiple shared global spaces. Returns a pointer to shared memory. Each thread that makes the call receives a pointer to a different location (in shared memory)

– void upc_free (shared void *ptr)

colectiva

no colectiva

● Librerías: Operaciones colectivas– upc_flag_t UPC_{IN,OUT}_{NO,MY,ALL}SYNC;

● Establece el control para el modo de sincronización de muchas operaciones colectivas. Los valores de los argumentos de funciones del tipo upc_flag_t se forman haciendo un OR con una constante UPC_IN_{NO,MY,ALL}SYNC y otra UPC_OUT_{NO,MY,ALL}SYNC

● Ej: (UPC_IN_NO_SYNC | UPC_OUT_NO_SYNC) establece que la función con estos valores en la llamada puede empezar a leer o escribir datos tan pronto como un hilo entre en la función y lo puede continuar haciendo hasta que el último hilo haya salido de la llamada. MY significa que solo las posiciones con afinidad a algún hilo que haya entrado son accesibles. ALL, que todos los hilos deben haber entrado.

– Ejemplos de operaciones colectivas en la librería:● void upc_all_broadcast (shared void *dst, shared const void *src, size_t nbytes, upc_flag_t sync_mode);

shared int A[THREADS];shared int B[THREADS];// Initialize A.upc_barrier;upc_all_broadcast( B, &A[1], sizeof(int),UPC_IN_NOSYNC | UPC_OUT_NOSYNC );upc_barrier;

● void upc_all_scatter (shared void *dst, shared const void *src, size_t nbytes, upc_flag_t sync_mode);

● void upc_all_gather (shared void *dst, shared const void *src, size_t nbytes,upc_flag_t sync_mode);

● void upc_all_gather_all (shared void *dst, shared const void *src, size_t nbytes, upc_flag_t sync_mode);

Otros ejemplos de funciones colectivas en la librería

● Hay más, faltan:– Punteros privados/compartidos apuntando a áreas de

memoria privada/compartida y su aritmética.

– I/O paralela.

UPC/*Copyright (C) 2000 Chen Jianxun, Sebastien Chauvin, Tarek El-Ghazawi */#include <upc.h>#include <upc_relaxed.h> /*modo por omision*/ #include <math.h>

#define N 32767

#define f(x) 1/(1+x*x)

upc_lock_t l;shared float pi;

void main(void){ float local_pi=0.0; int i;

upc_forall(i=0; i<N; i++; i%THREADS) local_pi+=(float)f((0.5+i)/N);

local_pi*=(float)4.0/N;

upc_lock(&l); pi+=local_pi; upc_unlock(&l);

upc_barrier 1; // Ensure all is done

if (MYTHREAD==0) printf("PI=%10.9f\n",pi);}

/*Copyright (C) 2000 Chen Jianxun, Sebastien Chauvin, Tarek El-Ghazawi */#include <upc.h>#include <upc_relaxed.h> /*modo por omision*/ #include <math.h>

#define N 32767

#define f(x) 1/(1+x*x)

upc_lock_t l;shared float pi;

void main(void){ float local_pi=0.0; int i;

upc_forall(i=0; i<N; i++; i%THREADS) local_pi+=(float)f((0.5+i)/N);

local_pi*=(float)4.0/N;

upc_lock(&l); pi+=local_pi; upc_unlock(&l);

upc_barrier 1; // Ensure all is done

if (MYTHREAD==0) printf("PI=%10.9f\n",pi);}

OpenACC● Directive based programming model for accelerators

– As of today: GPUs

● Again, a consortium: NVIDIA, Cray, PGI, CAPS

– Compiler support from Cray, PGI and CAPS: portability.

● 1.0 Spec: Nov. 2011

● “Based” on OpenMP:

– Start with sequential (or OpenMP) code , annotate code with directives: Incremental.

● Currently there are no OpenACC support in OpenMP, but there is a plan to mix them.

– Offloads (regions or loops of ) C, C++ and Fortran code to be executed on the accelerator.

– Compiler manages:

● Initialization/stop of accelerator● the transfer of data between host and accelerator.● Scheduling.● But some control through clauses.

OpenACC: Execution Model● Host directed execution of code on the accelerator:

– The bulk of the code executes on the host.

– Compute intensive regions are offloaded to the accelerator.

– Accelerator executes parallel regions.● Usually a parallel region comprises kernels regions:

typically work-sharing loops.● Accelerators can support (but maybe not all) coarse-grain

parallel execution across execution units, fine-grain parallelism (thread support within an execution unit) and vector operation within each execution unit.

– e.g.: Fully parallel loops can be programmed for coarse-grain, whereas dependent ones would require to be split for coarse-grain or be executed used fine-grain, with several threads, using the SIMD extensions or sequentially if not possible.

OpenACC: Execution Model● The accelerator is assumed to have, at least, a number of Processing Elements

with some form of vector capability that can work in parallel.

● When OpenACC starts a parallel region in the accelerator, a gang of workers (possible with vector capability) is started:

Type: Nvidia GPU Cluster node

Gang Threadblocknum_gangs(expression)CUDA gridDim

CPU

Worker Warp (32 threads)num_workers(expression)CUDA blockDim

CPU core

Vector SMT among a group of threads)SIMD execution.

SIMD extensions (SSE, Altivec)

OpenACC: Memory Model● host+accelerator: The memory of the accelerator can be completely separate from

the memory of the host.

– Data movement: needs explicit calls to move data, typically using DMA.

– OpenACC: hides these calls and make all data movement implicit and managed by the compiler, but, in order to get good performance:

● The programmer must be aware of the limited bandwidth between host and accelerator to estimate the possible speedup.

● The memory size on the accelerator can be limited.– Memory model on the accelerator side can be weak: coherence not

supported at all levels. (e.g.: current GPUs)

– Cache management is done by the compiler (or left to the HW) with the hint of directives

OpenACC: Directives● Parallel Region:

!$ACC PARALLEL [ espec.[[,], espec.] ...]

[code block]

!$ACC END PARALLEL

#pragma acc parallel [ espec.[[,], espec.] ...]

{

[code block]

}

– [code block] will be executed by gangs of workers. The number of gangs and workers remin constant during the parallel region. One worker in each gang starts executing the code in the block.

– Parallel regions cannot contain other parallel regions or kernels. Branching in or out is forbidden.

– The async clause elliminates the implicit barrier at the end of the parallel region.

● num_gangs, num_workers, vector_length, copy, copyin, copyout, present, private, firstprivate...

● The loop directive applies to the for/do loop inmediately following the directive. Controls the parallel execution of the folowing loop, declaring the needed loop-private variables, reduction operations, etc.

!$acc loop [clause[[,] clause]...]

[do loop]

!$acc end loop

#pragma acc loop [clause[[,] clause]...] newline

[for loop]

● [clause]can be collapse(n), gang/worker/vector(scalar), seq,independent, private(list), reduction(operator: list)

● n nested loops are associated with the loop construct and distributed among the scalar gangs created in the parallel region. Worker specifies that the iterations are executed in parallel within the gangs. Vector specifies that the iterations will execute with simd vectors of scalar length. Private(list) creates a copy of the list for each iteration. Seq executes the loop sequentially.

● The kernels directive makes the compiler produce a kernel out of the subsequent loops to execute in the accelerator

!$acc kernels [clause[[,] clause]...]

[structured block]

!$acc end kernels

#pragma kernels [clause[[,] clause]...] newline

[structured block]

● [clause]can be if(condition), async(expression). Any data clause.

●!$acc kernels Do i=1, n Body kernel 1 End Do Do i=1, n Body Kernel 2 End Do !$acc end kernels

● The data directive manages data movement. Data regions can be nested to preserve the strcuture of the original program.

!$acc data [clause[[,] clause]...]

[structured block]

!$acc end data

#pragma acc data [clause[[,] clause]...] newline

[structured block]

● [clause]can be copy/copyin/copyout/create/present(list),

– present_or_copy[in|out], present_or_create.

● copy Allocates memory on the accelerator, copies data from host to acc. when entering the region and copies back when exiting.

● copyin/copyout Allocates memory on the accelerator, copies data from host to acc. when entering the region/from acc to host when exiting.

● create/present Allocates memory but does not copy/uses data on the acc present from other containing data region.

Data Mgmt.

● ACC_DEVICE device The device type to connect to.

● ACC_DEVICE_NUM num Specifies which device number to connect to.

● _OPENACC Preprocessor directive for conditional compilation.

● Other things:

– Cache Specifies data that in a SW managed cache has to be close to the CPU.

– host_data makes the address of device data available to the host.

– wait waits for asynchronous accelerator activity to finish.

– if(expression) The compiler produces a version to execute on the host and other on the accelerator.

– async(expression) Do not wait to the barrier at the end of the construct.

Environment:

www.openacc-standard.org

OpenACC#include <openacc.h>

#include <math.h>

#define N 32767

#define f(x) 1/(1+x*x)

void main(void){ double pi=0.0; long i;

#pragma acc parallelfor (i=0; i<N; i++){ pi+=(float)f((0.5+i)/N);

} printf("PI=%10.9f\n",pi);}

#include <openacc.h>

#include <math.h>

#define N 32767

#define f(x) 1/(1+x*x)

void main(void){ double pi=0.0; long i;

#pragma acc parallelfor (i=0; i<N; i++){ pi+=(float)f((0.5+i)/N);

} printf("PI=%10.9f\n",pi);}

● High Performance Fortran:– A.K. Ewing et al. Writing Parallel Programmes with High

Performance Fortran. Edinburgh Parallel Computing Centre. http://www.epc.ed.ac.uk/epcc-tec/courses.html

– C.H. Koelbel et al. The High Performance Fortran Handbook. Scientific and Engineering Computation Series, The MIT Press, 1994.

– AA. VV. High Performance Fortran Languaje Specification. High Performance Fortran Forum, 1997. http://www.crcp.rice.edu/HPF

– AA. VV. HPF Tutorial. Hewlett-Packard 2001. http://www.hp.com/techservers/tutorials3/hpf.html

– The Portland Group. HPF para una gran variedad de plataformas.Manuales en línea: http://www.pgroup.com

● UPC:– S. Chauvin et al. UPC Manual (V 1.2), The George Washington

University (2003)

– K. Yelick et al. A Proposal for a UPC Memory Consistency Model, v1.0 Lawrence Berkeley National Lab Tech Report LBNL-54983, 2004.

– High Performance Laboratory at GWU http://upc.gwu.edu/

– Berkeley UPC - Unified Parallel C http://upc.lbl.gov/

● OpenACC:– The OpenACC Application Programming Interface v1.0.

Noviembre 2011. http://www.openacc-standard.org

– John Urbanic, Pittsburg SC Center and Mark Harris, Nvidia GPU Computing with ACC Directives. http://www.psc.edu

– Take a look at Dr. Dobbs OpenACC three part paper and PRACE material.

● OpenMP:– The OpenMP Architecture Review Board. OpenMP Fortran

Application Program Interface Version 2.0 Noviembre 2000. http://www.openmp.org

– AA. VV. OpenMP: A Proposed Industry Standard API for Shared Memory Programming, 1997. http://www.openmp.org

– AA.VV. RS/6000 Scientific and Technical Computing: Power3 Introduction and Tuning Guide. IBM Redbooks, 1998. http://www.redbooks.ibm.com

– AA.VV. OpenMP, a Parallel Programming Model for Shared Memory Architectures. Edinburgh Parallel Computing Centre, 1998. http://www.epcc.ed.ac.uk/epcc-tec/documents

● MPI:– W. Gropp et al. Using MPI: Portable Parallel Programming

with the Message Passing Interface. Scientific and Engineering Computation Series. The MIT Press, 1994. También con MPI-2, 1999.

– M. Snir et al. MPI: The Complete Reference, Vol. 1: The MPI Core. W. Gropp et al. Vol. 2: The MPI-2 Extensions. Scientific and Engineering Computation Series. The MIT Press, 1998

– The MPI Standards page http:/www-unix.mcs.anl.gov/mpi

– Netlib: The Net Library http://www.netlib.org/mpi

– Cursos en EPCC http://www.epcc.ed.ac.uk

– IMPI: Interoperable MPI. http://impi.nist.gov/IMPI