Title: Introducci
1Introducción a la programación HPC
- Josep Vidal Canet
- Alejandro Soriano
- Universitat de València
2Objetivos
- Conocer los principios básicos que rigen la
programación de altas prestaciones (HPC) - Hardware
- Software
- Conocer las tecnologías más utilizadas para
implementar algoritmos paralelos - Posix Threads
- MPI
- OpenMP (Alex)
- Las tendencias en el campo de HPC
- Reducir el tiempo de ejecución
3Motivación
- Hay problemas que no se pueden resolver con un
algorítmo secuencial - Predicción meteorológica
- Hardware roadmap
- Imposible seguir aumentando la velocidad del
reloj a causa de la disipación de calor - Para aumentar el rendimiento -gt aumentar el
número de cores y threads per core - Configuración PCs actuales 4 cores
- Existen PCs (orientado segmento mercado
profesional) con 8 cores (2 socket) - La tendencia del Hardware implica rediseñar el
software para poder aprovechar toda la potencia
del hardware - Pasar de aplicaciones secuenciales mono-hilo a
paralelas multi-hilo - Actualmente existe software multihilo -gt
servidores web, J2EE, BBDD, etc - En definitiva, paralelismo masivo en la era del
paralelismo para las masas
4Una nueva era en el diseño de sistemas
- Los sistemas altamente paralelos construidos con
múltiples procesadores pequeños, están ocupando
un segmento cada vez mayor del mercado de
servidores - 41,995.00 por 1 servidor con 32 cores basado en
AMD - Generalmente, caracterizados por un rendimiento
modesto en aplicaciones mono-hilo, buen
rendimiento a nivel d chip (N cores) y excelente
relación entre consumo/rendimiento - La tecnología de paralelismo, anteriormente
utilizada en HPC (High Performance Computing) se
está convirtiendo de uso generalizado en toda la
pila de software.
5Evolución numero de transistores en los
Microprocesadores
- La litografía permitirá seguir escalando nº de
transistores por chip (densidad)
dual-core Power6 tiene 790 millones
6Tendencia en la frecuencia de reloj de los
procesadores
La disipación de calor limita el incremento en
las velocidades del reloj
7Chip Power Density
La disipación de calor está limitando el aumento
de la frecuencia de los relojes, dando lugar a
la emergencia de chips multicore con menor
consumo de energía
Low power multicores replacing single high-power
cores
8Tendencia en procesadores multicore
Cell 9 cores, 10 threads
9Crecimiento del número de threads por procesador
Estamos entrando en la era de la computación
masivamente multithread
1 Ejemplo Sun Niagara 1 chip 8 cores x 8
threads/core 64 threads
10Tendencias en el software que complementan las
del Hardware
- Las aplicaciones cada vez son escalables
- Muchas nuevas aplicaciones son inherentemente
escalables - Aplicaciones de búsqueda
- Minería de datos
- Ejemplos
- Servidores de aplicaciones, web, BBDD
- Google
- Minería de contenidos multimedia
- Muchas aplicaciones existentes, middleware y SO
están siendo modificadas para ser escalables - Oracle y apache, pasaron de un modelo basado en
procesos a hilos
11Ejemplo Arquitectura Software/Hardware escalable
(webges)
Servidors Aplicacions
Fonts Dades
Servidors WEB
WAS Grid
webges01
Switch Level 4 / pound Balanceig Càrrega
jdbc ctg
db2jd
Webges..
FireWall PIX Cisco
webges11
AIX pseries 4 cores, 8 threads (power 5)
RACF
CICS SERVER
pugin-cfg.xml
OS/390 Z890
THREAD LIMIT AUTOMAT
Soporta d 500 peticiones x segundo
12Programación paralela
- Según la wikipedia
- es una técnica de programación basada en la
ejecución simultánea, bien sea en un mismo
ordenador (con uno o varios procesadores) o en un
cluster de ordenadores, en cuyo caso se denomina
computación distribuida. Al contrario que en la
programación concurrente, esta técnica enfatiza
la verdadera simultaneidad en el tiempo de la
ejecución de las tareas. - Los sistemas con multiprocesador y
multicomputadores consiguen un aumento del
rendimiento si se utilizan estas técnicas. En los
sistemas monoprocesador el beneficio en
rendimiento no es tan evidente, ya que la CPU es
compartida por múltiples procesos en el tiempo,
lo que se denomina multiplexación o
multiprogramación. - El mayor problema de la computación paralela
radica en la complejidad de sincronizar unas
tareas con otras, ya sea mediante secciones
críticas, semáforos o paso de mensajes, para
garantizar la exclusión mutua en las zonas del
código en las que sea necesario. - Objetivo reducir el tiempo de ejecución
13Un ejemplo
javac MyThread.java java Xmx 512 MyThread
public class MyThread extends Thread static
final int CPU5,N100000000 private int
Anew intN private int Bnew intN
SynchronizedCounter varnew SynchronizedCounter()
public void run() int roll
var.readANDadd() //Atomic. Avoid condition race
with mutex System.out.println("roll"roll"
\n") for (int i(roll(N/CPU)) i lt
((roll1)(N/CPU))i) if ((i
1000000) 0) System.out.println("roll
"roll" i"i" \n") Bii
AiBii public
static void main(String args) for
(int i0iltCPUi) MyThread workernew
MyThread() worker.start() public
static class SynchronizedCounter //Binary
Object lock private static int c
public synchronized int readANDadd() //Not
concurrent code. return c //First
return current value, then increment
14 Modificación concurrente de datos compartidos
- 2 threads intentando incrementar el valor de la
variable i1 (i) - Th 1 i (i2)
- Th 2 i (i3)
- Thread 1 load value of i into a register on
processor 1 (lets call it r1) i1, r11 - Thread 2 load value of i into a register on
processor 2 (lets call it r2) i1, r11, r21 - Thread 1 increment r1 by 1 i1, r12, r21
- Thread 2 increment r2 by 1 i1, r12, r22
- Thread 1 store value in r1 back into i i2,
r12, r22 - Thread 2 store value in r1 back into i i2,
r12, r22
- Problema Cuando graben el valor en memoria i2,
cuando tendría q valer 3 ! - Solución Crear una sección crítica de manera q
la operación d lectura y incremento sea atómica. - Cómo? Mediante semáforos
15Solución Serializar acceso a datos compartidos
- Cuando N threads modifican los mismos datos en
paralelo, el resultado es impredecible - Solución Pasar de ejecución paralela a
secuencial con un semáforo - Como norma general, los N threads se ejecutaran
en paralelo cuando modifiquen su parte d datos
del problema y en secuencial cuando modifiquen
datos compartidos
16 Para qué sirve la computación paralela ?
- 2 motivos principales
- Reducir el tiempo de ejecución
- Resolver problemas cada vez mas grandes y
complejos - Otros
- Utilizar recursos remotos utilizando recursos
disponibles en WAN - Reducción de costos utilizando muchos nodos
baratos en vez de un supercomputador caro - Salvar las restricciones de memoria 1 servidor
tiene recursos de memoria finitos. Para problemas
grandes, utilizando la memoria de N servidores
ayuda a superar este obstáculo
17Taxonomía Flynn
SISD Computador secuencial SIMD Computadores
vectoriales (NEC, Fujitsu, Cray), procesadores
con instrucciones de extensión multimedia (SSE3,
Altivec), GPUs MIMD Multiprocesadores,
clusters, multi-core
18Arquitecturas de memoria compartida
Espacio de memoria global para todos los
procesadores
Tipos UMA Uniform Memory Access NUMA
Non-Uniform Memory Access cc-NUMA Cache Coherent
NUMA
Ventajas fácil programación Desventajas
escalabilidad, precio
19NUMA
- Acceder de la CPU0 a la memoria d la
- CPU0Muy rápido
- CPU1rápido
- CPU2 rápido
- CPU3 Menos rápido (2 saltos)
20Arquitecturas de Memoria Distribuida
Se requiere una red de comunicación para que los
procesadores puedan acceder a la memoria no local
Características No existe el concepto de memoria
global Procesadores independientes (no
coherencia) El programador explicita el
intercambio de datos Ventajas escalabilidad,
precio Desventajas programación
21 Arq. Híbridas Distributed-Shared Memory
Combinación de los dos modelos
Ejemplos Multivac, Tirant
Características Cada nodo es un multiprocesador
(p.e. cc-NUMA) Comunicación para mover datos de
un nodo a otro Actualmente, los supercomputadores
suelen seguir este modelo
22Paradigmas de Programación Paralela
- Principales paradigmas o modelos de programación
paralela - Hilos (threads)
- Paso de mensajes
- Características
- Son una abstracción sobre la arquitectura
- No hay una relación directa con la arquitectura
(p.e. paso de mensajes sobre memoria compartida) - cesar.uv.es
- En debian apt-cache search mpich-shmem-bin
- mpich-shmem-bin - MPI parallel computing system
implementation, SHMEM version - No se puede hablar de el mejor paradigma
23Paradigmas de programación paralela
- Hilos
- Un proceso puede tener múltiples caminos de
ejecución - Todos los hilos comparten el mismo espacio de
memoria - Necesidad de sincronizar el acceso a la memoria
mediante semáforos - Se utilizan normalmente en arquitecturas de
memoria compartida - Estándares
- Posix Threads flexible
- OpenMP sencillo
- Paso mensajes
- Un conjunto de tareas que utilizan su propia
memoria local para cálculos - Diversas tareas pueden residir en la misma
máquina física, así como también en un número
arbitrario de máquinas distintas (clusters) - Las tareas intercambian datos mediante el paso de
mensajes - Estándares PVM y MPI
24Hilos vs Paso de mensajes
- Hilos
- No hay necesidad de comunicación explícita
- Todos acceden al espacio de datos del problema
(memoria compartida) - 1 proceso, se encarga de crear los hilos
necesarios para balancear la computación
necesaria para resolver el problema
- Paso mensajes
- 1 proceso se encarga de distribuir (enviando
mensajes) los datos del problema a los restantes
N-1 procesos remotos - Cuando los procesos terminan la computación
necesaria para resolver su parte del problema,
envían los resultados de vuelta
25Metodologías de Programación Paralela
- Los paradigmas se pueden abordar con diferentes
metodologías - Paralelización automática (compilador
paralelizante) - Paralelización semi-automática (directivas de
compilador) - Lenguajes de programación nuevos
- Extensión de lenguajes estándar
- Librerías de subrutinas o funciones (API)
- Ejemplos
- OpenMP está basado en directivas
- MPI y Posix Threads están basados en librerías de
funciones
26Single/Multiple Program Multiple Data
Son modelos de programación de más alto nivel,
aplicables a cualesquiera de los paradigmas
descritos
SPMD el mismo programa se ejecuta por todas las
tareas
MPMD cada tarea puede tener un programa distinto
Los programas SPMD son más fáciles de
mantener Suelen instrucciones condicionales pidf
ork() if (pid 0) printf(Child\n)elseprint
f(Master\n)
27Single Program Multiple Data
28Esquemas de Programación Paralela
- La paralelización (manual) suele implicar
- Particionado (de datos y/o tareas)
- Comunicación y/o sincronización
- En la práctica, hay algunos esquemas de
paralelización que aparecen recurrentemente - Granja de tareas (maestro-trabajadores)
- Segmentación (pipelining)
- Divide y vencerás (encontrar máximo)
- Otros Ramificación y poda, algoritmos genéticos,
autómatas celulares
29Granja de tareas (maestro-trabajadores)
- Esquema habitual en prog. paralela
- Particionado Repartir el espacio de datos del
problema entre N trabajadores - Cada uno normalmente ejecuta el mismo código pero
sobre su parte de los datos
- El maestro se encarga del particionado y la
planificación de los trabajadores - En memoria compartida no se requiere comunicación
para el particionado - Necesidad de sincronizar el acceso a la memoria
compartida
- Necesidad de balancear y sincronizar
correctamente la ejecución de tareas
30Maestro-esclavo con fork()
- include ltstdio.hgt
- define N 10
- int main()
- int pid,i0 / identificador del proceso /
- pid fork()
- while (i lt N)
- if ( pid lt 0 ) printf("Cannot fork
!!\n") exit(1) - if ( pid 0 )
- / Proceso hijo /
- printf("I am child number d \n",i)
- fflush(stdout)
- else
- / Proceso padre /
- printf("I am the father with
PIDd\n",pid) - fflush(stdout)
-
-
- return 0
-
Para optimizar la llamadaal sistema fork() linux
utiliza la técnica d copy_on_write
Añadir un sleep(1) para q los mensajes se
intercalen
31Ejemplo Maestro-esclavo Aplicar 1 transformación
a 1000 matrices d 1000x20
sub transform() my i0 while ( i lt
f ) I want to create N slaves
(childs) to parallelize the work my
pidfork() if (pid 0 )
We are the child do_work(i)
exit 0 else
i nchilds if (
nchilds lt CPUS ) next If
there are less childs than CPU, we continue
else If the number
of childs is equal to the number of CPUS -gt we
must wait, until a child ends.
This is, we stop forking until a child ends its
work. wait
nchilds-- f
El maestro se encarga d la creación y
planificación d esclavos
Para cada matriz, se crea un esclavo (hilo) para
transformarla Se permiten hasta un máximo d N
threads, donde N es igual al numero d CPUs
32Trabajo a realizar x cada esclavo
sub do_work my ishift my aux0
my T"txt"."/T"."i" open(FILE,"ltT")
T"mod_txt"."/T"."i" open(FILEMOD,"gtT")
while (ltFILEgt) my line _
chomp line my _at_FIELDS split('
') foreach my field (_at_FIELDS)
Apply transformation Matrix x Scalar
auxfield10 print FILEMOD
aux." " print FILEMOD "\n"
ltFILEgt close(FILE)
close(FILEMOD)
33Inicialización Construcción d las 1000 matrices
d 1000x20 (T0 ..T999)
- sub build_dataset()
- my i0, j0 , k0
- create auxiliary directories.
- rm -r txt rm -r mod_txt
- mkdir -p txt mkdir -p mod_txt
- while (i lt f)
- my T"txt"."/T"."i"
- open(FILE,"gtT")
- j0
- while (j lt n)
- k0
- while(k lt m)
- print FILE j." "
- k
- k
- print FILE "\n"
- j
- j
- close(FILE)
34Maestro-esclavo Transformación 1000 matrices
Análisis temporal
Versión secuencial 1000 x 30 30000 s. Tiempo
total 8 h y 20 minutos
Versión paralela para 4 CPUs 30000 / 4 7500
segundos Tiempo total 2 horas y 5 minutos
Suponiendo q cada transformación sobrela tabla
(matriz) tarde 30 segundos
35Segmentación
- Técnica originalmente utilizada en el diseño de
procesadores - Consiste en dividir un trabajo en N etapas
- Existe una relación de orden
- Una etapa no puede empezar hasta q no termine la
predecesora
Versión secuencial 2 instrucciones en 15 ciclos
Versión segmentada 5 instrucciones terminadas
enlos mismos ciclos de reloj
http//cse.stanford.edu/class/sophomore-college/pr
ojects-00/risc/pipelining/pipelining1.mov
36Problema transformación matrices
- Número Tablas 24 , T10.000 s S1 S2 S3
(3 Etapas) - Step 1 matrix por escalar
- Step 2 Transformar la matriz en aleatoria
- Step 3 Ordenar las filas d la matriz
-
- Versión Secuencial 240.000 s
Versión segmentada 240.000 / 3 80.000 6666
86666 (teórico)
37Segmentación superescalar
- HW Lanzar N instrucciones en cada ciclo de
reloj - SW Lanzar N hilos, cada uno de ellos segmentado
Versión segmentada 10 instrucciones terminadas
enlos mismos ciclos de reloj
38Problema matrices combinar maestro-esclavo con
segmentación
- Solución algorítmica
- Crear tantos procedimientos maestro esclavos
como etapas - Cada procedimiento maestro-esclavo se encargará
de realizar la transformación correspondiente - Cada procedimiento lanzará a su vez N (4 en el
ejemplo) trabajadores - Programaremos barreras para garantizar el orden
de las transformaciones - evitar q una etapa se ejecute antes q su
predecesora
myproc-gtstart(\matrix_X_scalar)
myproc-gtstart(\rand_matrix)
sort_matrix_rows
39maestro-esclavo con segmentación
sub sort_matrix_rows() my i0 my
aux0 my nchilds0 rm -r
inorder_txt mkdir -p inorder_txt
while ( i lt f ) I am the master. I
want to create N slaves (childs) to parallelize
the work my pidfork() if
(pid 0 ) We are the child.
Barrier to wait until rand_matrix step finishes
with table Ti auxi1
if ((aux lt f ) (rand_matrix_finished 0))
while (! -e "random_txt"."/T"."aux" )
sleep(1) do_sort_matrix_rows_work(
i) exit 0 else
i nchilds if (
nchilds lt CPUS ) next
else Wai t until a
child ends. This is, we stop forking until a
child ends its work. wait
nchilds--
f
40Problema matrices combinar maestro-esclavo con
segmentación
myproc-gtstart(\matrix_X_scalar)
myproc-gtstart(\rand_matrix)
Barrier to wait until matrix_X_scalar step
finishes with table Ti auxi1
if ((aux lt f ) (rand_matrix_finished
0)) while (! -e "random_txt"."/T"."aux"
) sleep(1)
myproc-gtstart(\sort_matrix)
Barrier to wait until rand_matrix step
finishes with table Ti auxi1
if ((aux lt f ) (rand_matrix_finished
0)) while (! -e "random_txt"."/T"."aux"
) sleep(1)
41Problema matrices combinar maestro-esclavo con
segmentación
- Número Tablas 24 , T10.000 s S1 S2 S3
- Versión Secuencial 120.000 s
Maestro-esclavo segmentado 120.000 / 4
30.000 / 3 10.000 6666 10666 (teórico)
42(No Transcript)
43Replica Versión secuencial
Tiempo
t0
t1
- Solamente se ejecuta un proceso, en un
determinado instante de tiempo - En este espacio de tiempo, hemos conseguido
procesar casi 2 tablas - Sin embargo, cada transformación (etapa) consume
un determinado tipo de recurso Export IXF -gt
Net , Load, Unload -gt I/O, Index Statistics -gt
CPU - Por ejemplo mientras descargamos los datos del
origen (Export IXF), consumimos ancho de banda de
la red, pero las CPUs y el I/O están ociosos. - Es decir, estamos desperdiciando recursos!!!
44Replica Versión paralela
Tiempo
t0
t1
t2
t3
t4
t5
t6
t7
- En este espacio de tiempo, hemos conseguido
procesar 10 tablas (teóricamente)
45Posix Threads
- Objetivos
- Comprender las ventajas de desarrollar
aplicaciones paralelas basadas en hilos - Estudiar el estandard POSIX para hilos, llamado
Pthreads - Estudiar las primitivas de control de threads
creación, terminación, join, sincronización,
concurrencia, etc .. - Aprender a diseñar aplicaciones multi-hilo
46Threads
- Un thread es una unidad de trabajo para la CPU
- Consiste en un flujo de instrucciones y un estado
(pila, contador de programa y conjunto de
registros). - Los procesos tradicionales de UNIX están basados
en 1 hilo q tiene la posesión de toda la memoria
y recursos del proceso - Los threads contenidos en un proceso pueden ser
ejecutados y planificados independientemente - Muchos threads comparten el mismo espacio de
memória - Además de para desarrollar aplicaciones HPC, se
utilizan en otro SW BBDD, servidores Web y de
aplicaciones
47Monohilo versus Multihilo
48Planificación de threads
- Los threads son ejecutados y planificados
independientemente
Processor affinity Modificación al planificador
d Linux q permite indicar el procesador preferido
para una tarea (proceso o thread)
49Ventajas threads -gt rendimiento
- El cambio de contexto es muy pesado
(ineficiente) en el caso de procesos
- Hay q guardarla memoria del proceso y su estado
a disco - Leer el estado de disco del siguiente y ponerlo
en memoria para q se pueda ejecutar - I/O mucho lenta q CPU
50Ventajas threads -gt rendimiento
- Cambio de contexto mucho más eficiente
- Simplemente hay q cambiar el estado del thread (
conjunto de registros puntero d pila)
- Como la memoria es común a los 2 threads no hay
q guardarla en disco cuando cambiamos de contexto
51Librería de Pthreads
- Una API estándar POSIX (IEEE 1003.1c), para la
creación y sincronización de hilos - La API especifica el comportamiento de la
librería de hilos - La implementación es responsabilidad del
desarrollador - Portable Corre en todos los UNIX Linux,
Solaris, z/os UNIX Services, etc .. - Simplemente una colección de funciones en C
52Utilización de pthreads
- Siempre incluir la libreria include ltpthread.hgt
- Compilar gcc program.c -lpthread
- int pthread_create (pthread_t tp, const
pthread_attr_t attr, void (
start_routine)(void ), void arg) - Crea un nuevo hilo de ejecución que acaba
llamando a la función start_routine - Retorna 0, si todo ha ido bien. En la variable
tp, retorna el identificador dl thread. - Attr es para modificar los atributos del nuevo
thread. Si le pasamos NULL, se utilizan los
atributos por defecto - Arg, sirve para pasar los argumentos a la función
(staart_routine) a ejecutar por el thread - Ejemplo pthread_create(thread, NULL, do_work,
(void)i) - int pthread_join(pthread_t thid, void status)
- Se espera hasta q el thread (thid) termine
- Suspende la ejecución del thread actual, hasta q
el thread indicado en thid no termine su
ejecución - Ejemplo pthread_join(thread, status)
- void pthread_exit(void status)
- Termina la ejecución del thread actual
- No es obligatorio
53Un ejemplo
N, CPU
include ltstdio.hgt / standard I/O routines
/ include ltpthread.hgt /
pthread functions and data structures / /
Shared memory / define CPU 3 define N
100 void do_work(void data) int
roll((int )data),i / Private data / for
(i0iltNi) printf("Hello World from
Threadd\n",roll) sleep(1)
pthread_exit(NULL) / terminate the thread /
/ like any C program, program's execution
begins in main / int main() int
thr_id,i / thread ID for the newly
created thread / void status pthread_t
threadCPU / thread's structure
/ / create a new thread that will
execute 'do_work(). / for (i0iltCPUi)
thr_id pthread_create(threadi, NULL,
do_work, (void)i) / Waint until all threads
had finished / for (i0iltCPUi)
pthread_join(threadi,status) return 0
proceso
54array.c
include ltstdio.hgt / standard I/O routines
/ include ltstdlib.hgt include
ltpthread.hgt / pthread functions and data
structures / define __HPC__ define CPU 10
unsigned long long N(18000000000) unsigned
long long M10000000 unsigned long long j0
unsigned long long c,array void do_work(void
data) long long roll((long long)data)
unsigned long long i,j0,begin,end,slices
begin(roll(N/CPU)) end((roll1)(N/CPU))
printf("rolllld\n",roll) ifdef __HPC__
slicesend/M while (jltslices) endif for
(ibegin i lt end i) ifndef __HPC__ if
((i (unsigned long long) M ) 0)
printf("rolllld illd\n",roll,i)
fflush(stdout) endif arrayii
ifdef __HPC__ j beginjslices end(j1)s
lices printf("rolllld illd\n",roll,i)
fflush(stdout) endif / terminate the
thread / pthread_exit(NULL) / like any C
program, program's execution begins in main
/ int main(int argc, char argv) int
thr_id / thread ID for the newly
created thread / void status pthread_t
threadCPU / thread's structure
/ array (unsigned long long )malloc(
N sizeof(unsigned long long) ) if ( array )
/ create a new thread that will execute
'do_work()' / for (j0jltCPUj)
thr_id pthread_create(threadj, NULL,
do_work, (long long )j) else
printf("Problem with malloc cannot reserve
memory \n") for (j0jltCPUj)
pthread_join(threadj,status) / NOT REACHED
/ return 0
- Inicializa en paralelo un vector de enteros. Cada
thread inicializa su trozo del vector - Desde roll(N/CPU)
- Hasta (roll1)(N/CPU)
- Para el caso d N100 y CPU4
- Th00 .. 24, Th125 .. 49,
- Th250 .. 74, Th375 .. 99
55Ejercicio Calcular el máximo de un array (I)
- Pistas
- Utilizar como base el código array.c
- Cada thread debe encontrar el máximo local (de su
parte dl array) - Cada thread debe pasar su máximo local a main
- Por ejemplo, guardándolo en otro array
- int localmaxCPU
- El main, debe esperar a q todos los hijos acaben,
para calcular máximo global de todos los máximos
locales
56Aplicar una transformación a 1000 matrices de
1000x1000
- Estructuras de datos
- define N 1000 //1k
- define M 1000 //1k
- typedef double matrixNN //Array of M matrix
of NxN - matrix amatM
- Reserva de memoria
- for (i0iltMi) amati (matrix )malloc(
N N sizeof(double) ) - Acceder al elemento j,k de la matrix i
(amati)jk - Observad q amati equivale a la dirección dl
puntero. Mientras q (amati) es su contenido
(dirección apuntada por este). En este caso
equivale a la posición 0,0 d la matriz i - Transformación
- (amati)jk(amati)jksin((float)k)ra
nd()atan(rand()) - Ejercicio Testear el algoritmo, variando el
número de CPUs y midiendo tiempos. Disminuir N i
M.
57Acceso a memoria compartida
- El programador es responsible de sincronizar el
acceso (proteger) a la memoria global compartida - Cómo?
- Serializando el acceso mediante semáforos
- La idea es evitar q 2 o threads modifiquen una
variable concurrentemente
- Las variables declaradas dentrode la función
del thread se llamandatos locales. - Son ubicados en la pila de cada thread. Por
tanto, se mantienen allímientras dura la
ejecución de este.
58Mutex example
Distintos threads modificando la variable
compartida i
include ltstdio.hgt / standard I/O routines
/ include ltpthread.hgt /
pthread functions and data structures / define
CPU 4 long long N1000000, c,array,i0 pthread_
mutex_t mutex void do_work(void data) long
long roll((long long)data)
printf("rolld\n",roll) pthread_mutex_lock(
mutex) for (i0 i lt Ni) if ((i
(N/10)) 0) printf("rolld arraylldlld
\n",roll,i,arrayi) arrayi
pthread_mutex_unlock( mutex)
pthread_exit(NULL) / terminate the thread
/ int main(int argc, char argv) int
thr_id pthread_t threadCPU void status
long long i0 array (long long )malloc( N
sizeof(long long) ) memset(array,0,Nsizeof(lon
g long)) if ( array ) for
(i0iltCPUi) thr_id pthread_create(threadi
, NULL, do_work, (void)i) else
printf("Problem with malloc cannot reserve
memory \n") for (i0iltCPUi)
pthread_join(threadi,status) for (i0 i lt
Ni) if (arrayi ! CPU ) printf("Wrong
result arraylldlld ! d \n",i,arrayi,CPU)
return 0
Serializar el acceso
59Ejercicio Calcular el máximo de un array (II)
- Pistas
- Utilizar como base el código array.c
- Cada thread debe encontrar el máximo local (de su
parte dl array) - Cada thread debe pasar su máximo local a main a
través de una variable global - int max0
- Si el máximo local es mayor q el global lo
escribiremos en la variable max - Hay q proteger la comparación y la posible
escritura del nuevo máximo para q sea thread
safe - El main, debe esperar a q todos los hijos acaben,
para después imprimir el valor de max
60Ejercicio Multiplicación de matrices cuadradas
- Pistas
- Definición de matrices
- define SIZE 10
- int ASIZESIZE, BSIZESIZE, CSIZESIZE
- Particionado
- Cada thread debe ejecutar el algoritmo sobre su
parte de el problema (el nº de filas q le toquen) - Algoritmo a ejecutar x cada thread (do_work)
for (ifrom iltto i) for (j0 jltSIZE
j) Cij0 for (k0 kltSIZE
k) Cij AikBkj
61Estadísticashttp//www.top500.org/stats
62Familia de procesadores
Supervivientes x86 (AMD,intel), power, Itanic
63Número de procesadores
64Redes de interconexión
65Sistemas Operativos
66Lenguajes de programación
67Inhibidores de paralelismo
68Mercado
69(No Transcript)
70MPI
- Previamente PVM Parallel Virtual Machine.
- MPI Message Passing Interface.
- Una especificación para paso de mansajes.
- La primera librería de paso de mensajes estándar
y portable. - Por consenso MPI Forum. Participantes de unas 40
organizaciones.
71Paradigma de paso de mensajes
- Probablemente más extendido hoy día en
programación de aplicaciones paralelas. - Consiste en una serie de procesos que interactúan
por medio de mensajes. - Cada proceso puede ejecutar código distinto y
sobre diferentes datos. - El modelo de paso de mensajes es valido tanto
para sistemas de memoria compartida como para
sistemas de memoria distribuida (cluster grid
computing). - El intercambio de mensajes es cooperativo los
datos deben ser tanto enviados como recibidos
explícitamente. - Esto supone una ventaja en cuanto a que la
modificación en la memoria del proceso es
conocida por este.
72MPI
- Estandarización.
- Portabilidad multiprocesadores,
multicomputadores, redes, heterogéneos, ... - Buenas prestaciones, ..., si están disponibles
para el sistema. - Amplia funcionalidad.
- Implementaciones libres (mpich, lam, ...)
73Comunicaciones básicas en MPI
- Los datos son transferidos de un procesador a
otro - Un procesador envía datos
- Otro recibe los datos
- Síncrona
- La llamada no retorna hasta q el mensaje no es
enviado o recibido - Asíncrono
- La llamada indica el comienzo del envío o de la
recepción - Hay q realizar una llamada adicional para
determinar si la comunicación ha terminado
74Tipos de comunicaciones
- La comunicación MPI entre procesos puede ser de
dos tipos - Punto a punto el proceso origen conoce el
identificador del proceso destino y envía un
mensaje dirigido solo a él. Se usan las funciones
MPI_Send y MPI_Recv. - Típicamente un master envía la parte
correspondiente de los datos del problema a sus
esclavos. - Colectiva Operaciones en las que participan
todos los procesos de un operador. Ejemplo - Broadcast El proceso origen manda el mensaje a
todos los demás (que pertenezcan al mismo
comunicador). Esto es típico de un esquema
master-slave. Se usa la función MPI_Bcast. - Típicamente un master envía los mismos datos a
sus esclavos. - Las funciones MPI de recepción de datos son por
lo general bloqueantes, es decir, un proceso
que debe recibir un mensaje espera hasta que de
hecho lo ha recibido completo.
75MPI Funciones básicas
- Funciones básicas
- MPI_Init gt Inicialización de MPI.
- MPI_Finalize gt Termina MPI.
- MPI_Comm_size gt Para averiguar el número de
procesos. - MPI_Comm_rank gt Identifica el proceso.
- MPI_Send gt Envía un mensaje.
- MPI_Recv gt Recibe un mensaje.
- Referencia del estándar en
- http//www-unix.mcs.anl.gov/mpi/
- Con estas 6 funciones se puede hacer casi
cualquier programa
76Escribiendo programas en MPI
- De las 6 funciones básicas que mencionamos antes
MPI_Init y MPI_Finalize son imprescindibles para
que haya un programa MPI. - Veamos un ejemplo trivial
include "mpi.h" include ltstdio.hgt int main(
int argc, char argv ) MPI_Init( argc,
argv ) printf( "Hola Mundo\n" )
MPI_Finalize() return 0
77Corriendo programas en MPI
- El programa anterior solo inicializa y termina el
entorno MPI. Entre tanto cada proceso imprime un
mensaje por pantalla. - Compilación
- Para un programa pequeño como este podemos hacer
una llamada directamente al comando de
compilación - mpicc o gcc lmpi o icc lmpi (para programas
C) - mpif77 (Fortran 77)
- mpif90 (Fortran 90)
- Para aplicaciones más complicadas conviene usar
un Makefile. - En nuestro caso anterior (fichero hello.c)
- icc -lmpi hello.c
- gcc lmpi hello.c
78Corriendo programas en MPI
- Ejecución
- El modelo de ejecución de MPI sigue un esquema de
creación (spawn) simultánea de procesos al lanzar
la aplicación - La ejecución de una aplicación suele hacerse con
- mpirun -np p programa opciones
- -np N N indica el número de procesos que se
quiere en la ejecución del programa. - Ejemplo mpirun -np 2 ./a.out
- Al ejecutar una aplicación
- Se lanzan p copias del mismo ejecutable (p.e. con
ssh) - Se crea un comunicador MPI_COMM_WORLD que engloba
a todos los procesos
79Modelo de Programación Comunicadores
- Un comunicador es una abstracción que engloba los
siguientes conceptos - Grupo conjunto de procesos
- Contexto para evitar interferencias entre
mensajes distintos - Un comunicador agrupa a p procesos
- int MPI_Comm_size(MPI_Comm comm, int size)
- Cada proceso tiene un identificador (rango), un
número entre 0 y p - 1 - int MPI_Comm_rank(MPI_Comm comm, int rank)
80Hello.c (II)
Averiguaremos desde el programa el número de
procesos que participan y la identificación de
cada uno.
include ltstdio.hgt include ltmpi.hgt int main
(argc, argv) int argc char argv
int rank, size MPI_Init (argc, argv)
/ starts MPI / MPI_Comm_rank (MPI_COMM_WORLD,
rank) / get current process id /
MPI_Comm_size (MPI_COMM_WORLD, size) /
get number of processes / printf( "Hello world
from process d of d\n", rank, size )
MPI_Finalize() return 0
Ejecutar este ejemplo gcc lmpi hello.c mpirun
-np 2 ./a.out
81MPI_Send
- La operación básica de envío (bloqueante) es
- MPI_Send( start, count, datatype, dest, tag, comm
) - start puntero a los datos a enviar
- count número de elementos a enviar
- datatype tipo de dato
- dest Identificación del proceso destino
- tag etiqueta de la comunicación
- comm Identificación del comunicador
82MPI_Recv
- La operación básica de recepción correspondiente
- MPI_Recv(start, count, datatype, source, tag,
comm, status) - start puntero para la recepción de los datos
- count número de elementos
- datatype tipo de dato
- source Identificación del proceso origen
- tag etiqueta de la comunicación
- comm Identificación del comunicador
- status puntero para acceso a información sobre
mensaje
83Envio de mensajes N hijos enviando saludos al
padre
include ltstdio.hgt include ltstring.hgt include
"mpi.h" main(int argc, charargv) int
myrank, p, source, dest, tag 0 char
message100 MPI_Status status MPI_Init(argc,a
rgv) MPI_Comm_rank(MPI_COMM_WORLD,myrank) MPI_C
omm_size(MPI_COMM_WORLD,p)
if (myrank ! 0) printf("Processor d of
d\n",myrank, p) sprintf(message,"greetings
from process d!", myrank)
dest 0 MPI_Send(message,
strlen(message)1,
MPI_CHAR, dest, tag,
MPI_COMM_WORLD) else
printf("processor 0, p d ",p)
for(source1 source lt p source)
MPI_Recv(message,100, MPI_CHAR, source,
tag, MPI_COMM_WORLD, status)
printf("s\n",message)
MPI_Finalize()
Processor 2 of 4 Processor 3 of 4 Processor 1 of
4 processor 0, p 4 greetings from process
1! greetings from process 2! greetings from
process 3!
mpicc -o hello hello.c mpirun -np 4 hello
84Tipos de datos MPI
- Se definen los siguientes tipos de datos MPI
- MPI_CHAR
- MPI_SHORT
- MPI_INT
- MPI_LONG
- MPI_UNSIGNED_CHAR
- MPI_UNSIGNED_SHORT
- MPI_UNSIGNED
- MPI_UNSIGNED_LONG
- MPI_FLOAT
- MPI_DOUBLE
- MPI_LONG_DOUBLE
- MPI_BYTE
- MPI_PACKED
- Corresponde a los de C, pero se añaden el tipo
byte, y el empaquetado, que permite enviar
simultáneamente datos de distintos tipos.
85Programación paralela con MPI
- Típicamente se utiliza el modelo
maestro-trabajadores - El maestro distribuye a cada trabajador su parte
del problema MPI_Send - Los hijos reciben su parte d datos dl problema
MPI_Recv - Los hijos hacen transformaciones sobre la parte
de datos q les ha enviado el maestro - Posteriormente, los trabajadores le envían el
resultado de las transformaciones al maestro - Es similar al maestro-trabajadores de memoria
compartida, con las comunicaciones se hacen de
manera explícita ya q no tenemos acceso a la
memoria compartida
86 Broadcast
include ltstdio.hgt include "mpi.h" define N
10 int arrayN int main() int
rank,i MPI_Init( argc, argv )
MPI_Comm_rank( MPI_COMM_WORLD, rank ) if
(rank 0 ) //Only master performs
array initialitation for (i0iltNi)
arrayii MPI_Bcast( array , N,
MPI_INT , 0, MPI_COMM_WORLD ) for
(i0iltNi) printf("rankd
arraydd\n ",rank,i,arrayi)
//Until all threads arrive at this point, we will
wait! MPI_Barrier(MPI_COMM_WORLD)
MPI_Finalize( ) return 0
MPI_Bcast(start, count, datatype, root, comm)
- start puntero a los datos a enviar
- count número de elementos a enviar
- datatype tipo de dato
- root identificación del proceso origen
- comm Identificación del comunicador
87MPI Scatter
- Repartir datos sobre todos los procesos
- Calcular max array
- El master reparte con MPI_Scatter, la porción
correspondiente del array a cada esclavo
if (rank 0 ) //Only master performs
array initialitation array(double
)malloc(Nsizeof(double)) for (i0iltNi)
arrayi(double)i//rand() porcio(dou
ble )malloc((N/nprocs)sizeof(double))
MPI_Scatter(array, N/nprocs, MPI_DOUBLE, porcio,
N/nprocs, MPI_DOUBLE, 0, MPI_COMM_WORLD) // En
la variable porcio, todas las tareas reciben su
parte del array (N/procs elementos)
88MPI_Gatther
- MPI_Gather - obtener datos de todos los procesos.
MPI_Scatter
. max_array(double )malloc(nprocssizeof(double
)) double maximum-1 for (i0iltN/nprocsi
) printf("Valor actualf, Valor
maxf, en id\n",porcioi,maximum,i)
maximummax(maximum,porcioi)
printf("Local maximum f from rankd\n",maximum,
rank) MPI_Gather(maximum,1,MPI_DOUBLE,max_arra
y,1,MPI_DOUBLE,0,MPI_COMM_WORLD) //En la
variable max_array recibimos todos los máximos
locales de todas las tareas .
89Calcular el máximo de una array con MPI
include ltstdio.hgt include "mpi.h" define N
10 define max(a,b) (agtb ? a b) int main(int
argc,char argv) int
rank,i,nprocs double array, porcio,
max_array MPI_Init( argc, argv )
MPI_Comm_rank( MPI_COMM_WORLD, rank )
MPI_Comm_size( MPI_COMM_WORLD, nprocs) if
(rank 0 ) //Only master performs
array initialitation array(double
)malloc(Nsizeof(double)) if (array
NULL) printf("Can't allocate memory\n")
return -1 for (i0iltNi)
arrayi(double)i//rand()
max_array(double )malloc(nprocssizeof(double))
// All processes in communicator
porcio(double )malloc((N/nprocs)sizeof(double))
MPI_Scatter(array, N/nprocs, MPI_DOUBLE,
porcio, N/nprocs, MPI_DOUBLE, 0,
MPI_COMM_WORLD) double maximum-1 for
(i0iltN/nprocsi) printf("Valor
actualf, Valor maxf, en id\n",porcioi,maxi
mum,i) maximummax(maximum,porcioi)
printf("Local maximum f from
rankd\n",maximum,rank) MPI_Gather(maximum,1,
MPI_DOUBLE,max_array,1,MPI_DOUBLE,0, MPI_COMM_WORL
D) if(rank0) for
(i1iltnprocsi) maximummax(maximum
,max_arrayi) printf("Maximumf\n",maxi
mum) MPI_Finalize( ) return 0
90Repartir M matrices entre N procesos
include ltstdio.hgt include ltmpi.hgt include
ltstdlib.hgt include ltmath.hgt define N
10 define M 10 int CPU0 typedef double
matrixNN //Array d M matrices d NxN matrix
amatM define print_matrix(slice_mat,i,M,N,my
rank,j,k,nprocs) do \ printf(
"Received matrix d of d. My rankd. Proceding
to print it!\n ,i,M,myrank) \ for
(j0jltNj) \ printf("\n\t ")
\ for (k0kltNk) \
printf("Mddf ",j,k,(slice_mati/nproc
s)jk) \ printf("") \
\ while (0)
91Repartir M matrices entre N procesos
int main (argc, argv) int argc char
argv int myrank, nprocs,i,j,k
MPI_Status status double p,aux MPI_Init
(argc, argv) / starts MPI /
MPI_Comm_rank (MPI_COMM_WORLD, myrank)
MPI_Comm_size (MPI_COMM_WORLD, nprocs)
CPUnprocs matrix slice_matM/CPU
printf("Rankd nprocs d\n",myrank,nprocs)
fflush(stdout) for (i0iltMi) if
(myrank 0) // master
amati (double )malloc( N N sizeof(double)
) for (j0jltNj)
for(k0kltNk)
(amati)jki MPI_Send(amati,
NN, MPI_DOUBLE,inprocs,i ,MPI_COMM_WORLD)
if (myrank(inprocs)) printf(
"Receiving matrix d of d. My rankd\n",
i,M,myrank) slice_mati/nprocs
(double )malloc( N N sizeof(double) )
MPI_Recv(slice_mati/nprocs,NN,MPI_DOUBLE,0,
i,MPI_COMM_WORLD,status)
print_matrix(slice_mat,i,M,N,myrank,j,k,nprocs)
MPI_Finalize() return 0
92Aplicar una transformación a 1000 matrices de
1000x1000 usando MPI
- Estructuras de datos
- define N 1000 //1k
- define M 1000 //1k
- typedef double matrixNN //Array of M matrix
of NxN - matrix amatM
- Transformación
- (amati)jk(amati)jksin((float)k)ra
nd()atan(rand()) - Adaptaremos el código desarrollado en Posix
Threads para ejecutarlo en MPI - Simplemente necesitamos añadir la parte de
comunicación explícita, mediante el envio de
mensajes - Ejercicio Testear el algoritmo, variando el
número de CPUs y midiendo tiempos. Disminuir N i
M.
93Variables Trabajo a realizar por cada hijo
include ltstdio.hgt include ltmpi.hgt include
ltstdlib.hgt include ltmalloc.hgt include
ltmath.hgt define N 10 //1M define M 10
//1K typedef double matrixNN //Array d M
matrices d NxN matrix amatM int CPU0 void
do_work(int roll, matrix slice_mat)
int i,j,k,m,n double max0
printf("rolld\n",roll) fflush(stdout)
for (i0 i lt (M/CPU)i) for (j0
jltNj) for (k0kltNk)
(slice_mati)jk(slice_mati)jk
sin((float)k) rand() atan(rand())
for (m0 mltN m) for (n0 nltN
n) if ((slice_mati)mn gt
max ) max(slice_mati)mn
printf("Max f\n",max) fflush(stdout)
94 Comunicaciones transformaciones
- int main (argc, argv) int argc char
argv - int myrank, nprocs,i,j MPI_Status status
double p, aux - MPI_Init (argc, argv) / starts MPI /
- MPI_Comm_rank (MPI_COMM_WORLD, myrank)
- MPI_Comm_size (MPI_COMM_WORLD, nprocs)
- CPUnprocs
- matrix slice_matM/CPU
- printf("Rankd nprocs d\n",myrank,nprocs)
- fflush(stdout)
- for (i0iltMi)
- if (myrank 0)
- // master
- amati (matrix )malloc( N N
sizeof(double) ) - auxamati
- prand()
- for (j0jltNNj) auxjp
- MPI_Send((amati), NN,
MPI_DOUBLE,inprocs,i ,MPI_COMM_WORLD) -
if (myrank(inprocs)) printf(
"Receiving matrix d of d. My rankd\n",
i,M,myrank) slice_mati/nprocs
(double )malloc( N N sizeof(double) )
MPI_Recv((slice_mati/nprocs),NN,MPI_DOUBLE
,0,i, MPI_COMM_WORLD,status)
for (i0iltM/CPUi)
do_work(i,slice_mat) //TODO Send back results
to master MPI_Finalize() return 0
Ejercicio Enviar los datos de vuelta al maestro.
95MPI_Allgather
96MPI_Reduce
97Bibliografia
- Básica
- google
- Ian Foster Designing and Building Parallel
Programs. 1995. - Kumar, Grama, Gupta, Karypis Introduction to
Parallel Computing. Design and Analysis of
Algorithms. The Benjamin Cumming Publishing
Company. 1994. - Barry Wilkinson, Michael Allen Parallel
programming. Prentice-Hall. 1999. - Complementaria
- Akl Diseño y análisis de algoritmos paralelos.
Ra-ma. 1992. - Andrews Foundations of Multithreaded, Parallel,
and Distributed Programming. Addison-Wesley, 2000
- Bertsekas, Tsilikis Parallel and Distributed
Computation. Prentice-Hall. 1989. - Rajkumar Buyya (Editor) High Performance Cluster
Computing, Vol 2, Programming and Applications.
Prentice Hall. 1999. - Gibbons, Rytter Efficient Parallel Algorithms.
Cambridge University press. 1988. - Ja-Ja An Introduction to Parallel Algorithms.
Addison-Wesley. 1992. - Lester The art of Parallel Programming.
Prentice-Hall. 1993. - Modi Parallel Algorithms for Matrix Computation.
Oxford University Press. 1989. - Quinn Design of Efficient Algorithms.
McGraw-Hill. 1987.