1 Introduccion 3
2 Entendiendo el paralelismo 6
2.1 Paralelismo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2.1 Speedup . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
1
3.1 Task creation in OpenMP (summary) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.2.1 Ejemplo 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.6 Fibonacci . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.6.1 Sequencial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2
Tema 1
Introduccion
Retornos decrecientes cuando se intenta utilizar los transistores para lograr un mayor numero de
paralelismo a nivel de instrucciones.
El programa esta compuesto por una secuencia de instrucciones, que se ejecutan de una en una y
solo 1 en un momento determinado
Explotar la concurrencia consisten en romper un problema en partes discretas, llamadas tareas, para
garantizar su correcta ejecucion simultanea
Cada tarea se ejecuta de manera serial y la ejecucion de las tareas es multiplexadas en una unica
CPU.
Es necesario controlar y coordinar la ejecucion de las tareas, garantizando el correcto acceso a los recur-
sos compartidos.
3
1.2.3 Ejecucion paralela
Es cuando utilizamos multiples procesadores (CPU) para ejecutar en paralelo las tareas creadas para
ejecucion concurrente.
Multiples procesadores tambien pueden ser utilizados para incrementar el numero de programas ejecuta-
dos por unidad de tiempo.
Throughput computing: Multiple, unrelated, instrucin streams (programs) executing at the same
time on multiple processors.
p
n programas on p procesadores; si (n p) cada programa recibe n procesadores, o un procesador
en caso contrario.
Hay que fijarse que esto no es lo mismo que el paralelismo, cuyo objetivo es reducir el tiempo de eje-
cucion de un solo programa:
Race condition: multiples tareas leen y escriben datos y el resultado final depende del timing
relativo de la ejecucion
Deadlock: 2 o mas tareas no pueden avanzar debido a que cada una esta esperando a que las
otras hagan alguna cosa
Starvation: Una tarea no puede conseguir acceso a los recursos compartidos debido a que no
puede progresar.
Livelock: 2 o mas tareas cambian su estado continuamente en respuesta a cambios en otras tar-
eas, sin llegar a hacer trabajo util.
Corregir: Deadlock
4
Estadsticas simples del banco
5
Tema 2
Entendiendo el paralelismo
2.1 Paralelismo
Nodo = tarea
T1 = nodes
i=1 (work nodei ). Es la suma de todos los nodos.
6
Es la suma del camino crtico: camino hasta una hoja que tarda mas en ejecutarse.
Paralelismo = TT1 , independiente del numero de procesadores P. Como de rapido ira el programa si tengo
suficientes (infinitos) recursos disponibles.
Arista = dependencia
El nodo sucesor necesita (lee) datos producidos (escritos) por el nodo predecesor.
2.2.1 Speedup
Tp = tiempo de ejecucion usando P procesadores. Depende de la planificacion del grafo de nodos en los
procesadores.
Lower bounds:
Tp = T1
P
Tp T
T1
Speedup en P procesadores: Sp = Tp
T1
Paralelismo = T
7
Ejemplo de speedup
T1 = Tseq + Tpar
Tpar
Fraccion paralela: = T1
T1 = (1 ) T1 + T1
Tpar
Tp = Tseq + P
T1
Tp = (1 ) T1 + ( P )
T1
Sp = Tp
Taccess = ts + m tw
8
Tema 3
#pragma omp parallel: se crea una tarea implcita para cada thread en el equipo y se ejecuta in-
mediatamente
#pragma omp task: Se crea una tarea explcita, empaquetando codigo y datos para una posible
ejecucion en diferido
Tarea = cuerpo de la estructura iterativa, por ejemplo bucles (con contador o sin)
Ejemplos: Calculo de Pi, equacion de difusion de Mandelbrot y calor, operaciones sobre vec-
tores y matrices.
9
3.2.1 Ejemplo 1
Una tarea es una secuencia de instrucciones, como por ejemplo la suma de 2 vectores divididos artificial-
mente en 2 partes:
1 v o i d vector add ( i n t A , i n t B , i n t C , i n t n ) {
2 #pragma omp t a s k
3 f o r ( i n t i =0; i< n / 2 ; i++)
4 C[i] = A[i] + B[i];
5
6 #pragma omp t a s k
7 f o r ( i n t i=n / 2 ; i< n ; i++)
8 C[i] = A[i] + B[i];
9 }
10 v o i d main ( ) { . . . .
11 #pragma omp p a r a l l e l
12 #pragma omp s i n g l e
13 vector add ( a , b , c , N ) ;
14 ...
15 }
1 v o i d vector_add ( i n t A , i n t B , i n t C , i n t n ) {
2 f o r ( i n t i =0; i< n ; ii++)
3 #pragma omp t a s k
4 C[i] = A[i] + B[i];
5 }
6
7 v o i d main ( ) {
8 ....
9 #pragma omp p a r a l l e l
10 #pragma omp s i n g l e
11 vector_add ( a , b , c , N ) ;
12 ...
13 }
Cada tarea explicita ejecuta una iteracion i del bucle, hay un overhead en la creacion de muchas tareas y
la granularidad es muy fina.
1 v o i d vector_add ( i n t A , i n t B , i n t C , i n t n ) {
2 #pragma omp t a s k l o o p g r a i n s i z e (BS)
3 f o r ( i n t i =0; i< n ; i++)
4 C[i] = A[i] + B[i];
5 }
6
7 v o i d main ( ) {
8 #pragma omp p a r a l l e l
9 #pragma omp s i n g l e
10 ...
11 vector_add ( a , b , c , N ) ;
12 ...
13 }
10
Otro ejemplo utilizando tareas implicitas y worksharing:
1 v o i d vector_add ( i n t A , i n t B , i n t C , i n t n ) {
2 #pragma omp p a r a l l e l f o r s c h e d u l e ( dynamic , BS)
3 f o r ( i n t i =0; i< n ; i++)
4 C[i] = A[i] + B[i];
5 }
6
7 v o i d main ( ) {
8 ...
9 vector_add ( a , b , c , N ) ;
10 ...
11 }
1 i n t main ( ) {
2 s t r u c t node p ;
3 p = init list ( n ) ; . . .
4 #pragma omp p a r a l l e l
5 #pragma omp s i n g l e
6 w h i l e ( p != NULL ) {
7 #pragma omp t a s k f i r s t p r i v a t e ( p )
8 process_work ( p ) ;
9 p = p>next ;
10 }
11 ...
12 }
1 #d e f i n e N 1024
2 #d e f i n e MIN SIZE 64
3 v o i d vector add ( i n t A , i n t B , i n t C , i n t n ) {
4 f o r ( i n t i =0; i< n ; i++) C [ i ] = A [ i ] + B [ i ] ;
5 }
6
7 v o i d rec vector add ( i n t A , i n t B , i n t C , i n t n ) {
8 i f ( n>MIN_SIZE ) {
9 i n t n2 = n / 2 ;
10 rec vector add ( A , B , C , n2 ) ;
11 rec vector add ( A+n2 , B+n2 , C+n2 , nn2 ) ;
12 }
13 e l s e vector add ( A , B , C , n ) ;
14 }
15
16 v o i d main ( ) {
17 ....
18 rec vector add ( a , b , c , N ) ;
11
19 ...
20 }
Leaf strategy
Una tarea corresponde con cada invocacion de vector add una vez las invocaciones recursivas paran.
Implementacion:
1 #d e f i n e N 1024
2 #d e f i n e MIN SIZE 64
3 v o i d vector_add ( i n t A , i n t B , i n t C , i n t n ) {
4 f o r ( i n t i =0; i< n ; i++) C [ i ] = A [ i ] + B [ i ] ;
5 }
6
7 v o i d rec_vector_add ( i n t A , i n t B , i n t C , i n t n ) {
8 i f ( n>MIN_SIZE ) {
9 i n t n2 = n / 2 ;
10 rec_vector_add ( A , B , C , n2 ) ;
11 rec_vector_add ( A+n2 , B+n2 , C+n2 , nn2 ) ;
12 } else
13 #pragma omp t a s k
14 vector_add ( A , B , C , n ) ;
15 }
16
17 v o i d main ( ) {
18 ....
12
19 #pragma omp p a r a l l e l
20 #pragma omp s i n g l e
21 rec_vector_add ( a , b , c , N ) ;
22 ...
23 }
Tree strategy
Generacion paralela de tareas 6 Granularidad: algunas tareas simplemente generan nuevas tareas
Implementacion:
1 #d e f i n e N 1024
2 #d e f i n e MIN SIZE 64
3 v o i d vector_add ( i n t A , i n t B , i n t C , i n t n ) {
4 f o r ( i n t i =0; i< n ; i++) C [ i ] = A [ i ] + B [ i ] ;
5 }
6
7 v o i d rec_vector_add ( i n t A , i n t B , i n t C , i n t n ) {
8 i f ( n>MIN_SIZE ) {
9 i n t n2 = n / 2 ;
10 #pragma omp t a s k
11 rec_vector_add ( A , B , C , n2 ) ;
12 #pragma omp t a s k
13 rec_vector_add ( A+n2 , B+n2 , C+n2 , nn2 ) ;
14 }
15 e l s e vector_add ( A , B , C , n ) ;
16 }
17
18 v o i d main ( ) {
19 ....
20 #pragma omp p a r a l l e l
21 #pragma omp s i n g l e
22 rec_vector_add ( a , b , c , N ) ;
23 ...
24 }
13
3.3 Control de la generacion de tareas
1 #d e f i n e CUTOFF 3
2 ...
3 v o i d rec_vector_add ( i n t A , i n t B , i n t C , i n t n , i n t depth ) {
4 i f ( n>MIN_SIZE ) {
5 i n t n2 = n / 2 ;
6 i f ( depth < CUTOFF ) {
7 #pragma omp t a s k
8 rec_vector_add ( A , B , C , n2 , depth +1) ;
9 #pragma omp t a s k
10 rec_vector_add ( A+n2 , B+n2 , C+n2 , nn2 , depth +1) ;
11 }
12 else {
13 rec_vector_add ( A , B , C , n2 , depth +1) ;
14 rec_vector_add ( A+n2 , B+n2 , C+n2 , nn2 , depth +1) ;
15 }
16 }
17 e l s e vector_add ( A , B , C , n ) ;
18 }
19
20 v o i d main ( ) {
21 ....
22 #pragma omp p a r a l l e l
14
23 #pragma omp s i n g l e
24 rec_vector_add ( a , b , c , N , 0 ) ;
25 ...
26 }
1 #d e f i n e CUTOFF 2
2 ...
3 v o i d rec_vector_add ( i n t A , i n t B , i n t C , i n t n , i n t depth ) {
4 i f ( n>MIN_SIZE ) {
5 i n t n2 = n / 2 ;
6 i f ( depth == CUTOFF ) {
7 #pragma omp t a s k
8 rec_vector_add ( A , B , C , n2 , depth +1) ;
9 #pragma omp t a s k
10 rec_vector_add ( A+n2 , B+n2 , C+n2 , nn2 , depth +1) ;
11 }
12 else {
13 rec_vector_add ( A , B , C , n2 , depth +1) ;
14 rec_vector_add ( A+n2 , B+n2 , C+n2 , nn2 , depth +1) ; }
15 } else
16 i f ( depth <= CUTOFF )
17 #pragma omp t a s k
18 vector_add ( A , B , C , n ) ;
19 else
20 vector_add ( A , B , C , n ) ;
21 }
22 ...
if: Si la expresion de un if se evalua a falso, la tarea padre se suspende y se inicia una nueva (no
necesariamente en el mismo thread). El padre vuelve a ejecutarse cuando la tarea finaliza.
final clause: Si la expresion de una clausula final evalua a true, todas las tareas que descienda de
ella seran final. La ejecucion de una tarea final es incluida secuencialmente en la tarea generada.
mergeable clause:
15
3.5 Task syncronization in OpenMP
Barrearas de threads (#pragma omp barrier): espera a que todos los threads acaben el trabajo que
estaba haciendo
Task barriers:
taskwait: Suspende la tarea actual esperando a que finalicen las tareas hijas de ella.
taskgroup: suspende la tarea actual al final del bucle estructurado, esperando a la finalizacion
de las tareas hijas de la tarea actual y sus tareas descendientes.
Taskwait
1 #pragma omp t a s k {} // T1
2 #pragma omp t a s k // T2
3 {
4 #pragma omp t a s k {} // T3
5 }
6 #pragma omp t a s k {} // T4
7
8 #pragma omp t a s k w a i t
9 // Only T1 , T2 and T4 a r e g u a r a n t e e d t o have f i n i s h e d a t t h i s p o i n t
Taskgroup
1 #pragma omp t a s k {} // T1
2 #pragma omp t a s k g r o u p
3 {
4 #pragma omp t a s k // T2
5 {
6 #pragma omp t a s k {} // T3
7 }
8 #pragma omp t a s k {} // T4
9 }
10 // Only T2 , T3 and T4 a r e g u a r a n t e e d t o have f i n i s h e d a t t h i s p o i n t
3.6 Fibonacci
3.6.1 Sequencial
1 l o n g fib ( l o n g n ) {
2 i f ( n < 2) return n ;
3 r e t u r n ( fib ( n1) + fib ( n2) ) ;
4 }
5
6 v o i d main ( i n t argc , c h a r argv [ ] ) {
7 n = atoi ( argv [ 1 ] ) ;
16
8 res = fib ( n ) ;
9 printf ( F i b o n a c c i f o r %d i s %d , n , res ) ;
10 }
Las llamadas a fib para n-1 y n-2 pueden ser una task
Necesitamos garantizar que ambas instancias de fib acaben antes de devolver el resultado
1 l o n g fib parallel ( l o n g n , i n t d ) {
2 long x , y ;
3 i f ( n < 2 ) r e t u r n n ; i f ( d<CUTOFF {
4 #pragma omp t a s k s h a r e d ( x ) // f i r s t p r i v a t e ( n ) by d e f a u l t
5 x = fib parallel ( n 1, d+1) ;
6 #pragma omp t a s k s h a r e d ( y ) // f i r s t p r i v a t e ( n ) by d e f a u l t
7 y = fib parallel ( n 2, d+1) ;
8 #pragma omp t a s k w a i t
9 }
10 else {
11 x = fib parallel ( n 1, d ) ; // o r f i b ( n1)
12 y = fib parallel ( n 2, d ) ; // o r f i b ( n2)
13 }
14 r e t u r n ( x+y ) ;
15 }
16
17 v o i d main ( i n t argc , c h a r argv [ ] ) {
18 n = atoi ( argv [ 1 ] ) ;
19 #pragma omp p a r a l l e l
20 #pragma omp s i n g l e
21 res = fib parallel ( n , 0 ) ;
22 printf ( F i b o n a c c i f o r %d i s %d , n , res ) ;
23 }
OpenMP permite definir dependencias entre tareas hermanas (p.e. que vengan del mismo padre)
Tipo in: la task generada sera dependiente de todas las tareas hermanas generadas anteriormente que
hagan referencia a al menos uno de los telementos de la lista en tipo de dependencia out o inout.
Out e inout: la tarea creada sera dependiente de todas las tareas hermanas creadas anteriormente que
hagan referencia a un item de la lista en in, out o inout.
1 256 2 1 calls 3 528 4 64 tasks 5 4 calls 6 192 t.u 7 84 calls 8 4 calls 9 88 t.u
17