Академический Документы
Профессиональный Документы
Культура Документы
Paralelos
Ejercicios OPENMP (2)
Prof. J.Fiestas
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy void
coresmaster_example
( float* x, float* xold, int n, float tol ) {
c = 0;
Constructor master: #pragma omp parallel {
do{
Especifica un bloque que es #pragma omp for private(i)
ejecutado por el master thread, for( i = 1; i < n-1; ++i ){
xold[i] = x[i]; }
dentro de la región en paralelo #pragma omp single {
correspondiente. toobig = 0; }
En el ejemplo, el master #pragma omp for private(i,y,error)
reduction(+:toobig)
controla el número de for( i = 1; i < n-1; ++i ){
iteraciones e imprime un y = x[i];
reporte de avance de la x[i] = average( xold[i-1], x[i], xold[i+1] );
error = y - x[i];
ejecución. Los demas threads if( error > tol || error < -tol ) ++toobig; }
evitan la región master sin #pragma omp master {
necesidad de esperar ++c;
Printf("iteration %d, toobig=%d\n", c, toobig );
#pragma omp master new-line }
}while( toobig > 0 );
structured-block }}
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Constructor atomic:
Asegura que la locación de memoria será accedida atomicamente,
evitando lectura o escritura simultánea de la misma, lo que podría
conducir a valores erróneos. Atomic fuerza acceso exclusivo con
respecto a otras regiones atomic que acceden a la misma locación
de memoria x, entre todos los threads del programa,
independientemente de los bloques a los que pertenecen
Constructor atomic:
Constructor atomic:
int main()
Note que las directivas {
atomic se aplican solo a los float x[1000];
bloques que le siguen. Como float y[10000];
int index[10000];
consecuencia, los elementos int i;
de y no son actualizados for (i = 0; i < 10000; i++) {
atómicamente en este index[i] = i % 1000;
ejemplo. y[i]=0.0;
}
for (i = 0; i < 1000; i++)
x[i] = 0.0;
atomic_example(x, y, index,
10000);
return 0;
}
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Constructor ordered
Especifica un bloque en un ciclo que será ejecutado en el orden
de las iteraciones en el ciclo, mientras el programa se ejecuta en
paralelo fuera de la región
#pragma omp ordered new-line
structured-block
ordered influencia todos los threads de la región, en el ciclo interior
inmediato. Mientras regiones ordered en distintos ciclos se ejecutan
independientemente. Cuando un thread ejecutando la primera iteración
encuentra un constructor ordered, ingresa sin esperar. Cuando un thread
ejecutando una iteración posterior encuentra una región ordered, espera a
que las iteraciones anteriores han sido completadas.
Constructor y cláusula
#include <stdio.h>
ordered void work(int k)
{
#pragma omp ordered
printf(" %d\n", k);
Constructores ordered son }
útiles para ordenar void ordered_example(int lb, int ub, int stride)
secuencialmente la salida de {
int i;
una región en paralelo. #pragma omp parallel for
El ejemplo imprime los índices ordered schedule(dynamic)
en orden secuencial. for (i=lb; i<ub; i+=stride)
work(i);
}
int main() {
ordered_example(0, 100, 5);
return 0;
}
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Constructor y cláusula
ordered void work(int i) {}
Es posible tener múltiples void ordered_wrong(int n)
{
constructores ordered en int i;
un ciclo con la cláusula #pragma omp for ordered
ordered declarada. for (i=0; i<n; i++) {
/* incorrect because an iteration may
Sin embargo, en este caso not execute more than one ordered region */
#pragma omp ordered
todas las iteraciones work(i);
ejecutan dos regiones #pragma omp ordered
ordered, por lo que el work(i+1);
código no es válido. }
Una iteración en un ciclo no }
debe ejecutar mas de una
región ordered.
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Constructor y cláusula
ordered void work(int i) {}
Es posible tener múltiples void ordered_wrong(int n)
{
constructores ordered en int i;
un ciclo con la cláusula #pragma omp for ordered
ordered declarada. for (i=0; i<n; i++) {
/* incorrect because an iteration may
Sin embargo, en este caso not execute more than one ordered region */
#pragma omp ordered
todas las iteraciones work(i);
ejecutan dos regiones #pragma omp ordered
ordered, por lo que el work(i+1);
código no es válido. }
Una iteración en un ciclo no }
debe ejecutar mas de una
región ordered.
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Constructor y cláusula
ordered void work(int i) {}
void ordered_good(int n)
{
int i;
Este código (con más de un
#pragma omp for ordered
constructor ordered) si es
for (i=0; i<n; i++) {
válido, ya que cada iteración if (i <= 10) {
ejecutará solo una región #pragma omp ordered
ordered work(i);
}
if (i > 10) {
#pragma omp ordered
work(i+1);
}
}
}
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Constructor y cláusula
ordered void work(int i) {}
void ordered_good(int n)
{
int i;
Este código (con más de un
#pragma omp for ordered
constructor ordered) si es
for (i=0; i<n; i++) {
válido, ya que cada iteración if (i <= 10) {
ejecutará solo una región #pragma omp ordered
ordered work(i);
}
if (i > 10) {
#pragma omp ordered
work(i+1);
}
}
}
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Directiva threadprivate:
Directiva threadprivate:
int counter = 0;
#pragma omp threadprivate(counter)
En el ejemplo, cada thread
int increment_counter()
tendrá {
un contador independiente. counter++;
return(counter);
}
int increment_counter_2()
Uso de threadprivate en una {
variables estática static int counter = 0;
#pragma omp threadprivate(counter)
counter++;
return(counter);
}
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Directiva threadprivate:
En el ejemplo se muestra el int x = 1;
T a(x);
comportamiento incierto de la
const T b_aux(x); /* Capture value of x = 1 */
inicialización de una variable T b(b_aux);
threadprivate. #pragma omp threadprivate(a, b)
Cláusula private
private(list)
Declara uno o más elementos de lista como privados para una tarea
(task)
Cada task que referencia un elemento private recibe una lista
copia del original con referencias a su nueva lista. El valor
original podrá cambiar si se accesa por punteros.
void g(int k) {
En el ejemplo, los usos a = k; /* Accessed in the region but outside
de la variable a en el * of the construct; therefore unspecified whether
* original or private list item is modified. */
ciclo en la función f() }
utilizan el elemento a
de la lista, mientras que void f(int n) {
int a = 0;
es incierto si las #pragma omp parallel for private(a)
referencias a a en la for (int i=1; i<n; i++) {
función g() pertenecen a = i;
g(a*2); /* Private copy of "a" */
a un elemento privado }
u original. }
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Cláusula firstprivate
firstprivate(list)
Declara una lista privada a una tarea e inicializa cada elemento
de la lista con el valor correspondiente de la variable original al
momento que el constructor es inicializado. Se aplican
restricciones de la cláusula private.
Cláusula lastprivate
Programas en los que la
ejecución depende del valor
en la última iteración, utilizan
un listado de variables en una void lastpriv (int n, float *a, float *b)
cláusula lastprivate, para que {
los valores sean equivalentes int i;
a los obtenidos en una #pragma omp parallel
ejecución secuencial. {
#pragma omp for lastprivate(i)
for (i=0; i<n-1; i++)
a[i] = b[i] + b[i+1];
}
a[i]=b[i]; /* i == n-1 here */
}
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Cláusula reduction
Especifica un operador y una lista
de elementos, para cada uno de los
cuales una copia privada es creada
en cada tarea (task) e inicializada
para el operador. Al final de la
región, el valor del elemento
original es actualizado con los
valores de las copias privadas
utilizando el operador.
reduction(operator :list )
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Cláusula reduction
El código no es correcto, ya que la #include <stdio.h>
inicialización (a=0) de la variable a int main (void)
{
original no esta sincronizada con la int a, i;
actualización de a como resultado de #pragma omp parallel shared(a) private(i)
la cláusula reduction en el ciclo for. {
Por lo que el código imprimirá un #pragma omp master
a = 0;
valor errado de a. // To avoid race conditions, add a barrier here.
Para evitar el problema, la #pragma omp for reduction(+:a)
inicialización debe hacerse antes de for (i = 0; i < 10; i++) {
la actualización debido a la claúsula a += i;
reduction. Para ello se puede añadir }
#pragma omp single
una directiva barrier luego de a=0, o printf ("Sum is %d\n", a);
incluir a=0 en una directiva single }
(que tiene barrera intrínseca), o }
inicializar a antes de iniciar la región
parallel.
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
void qs(int *z, int zstart, int zend, int int separate(int *x, int low, int high){
firstcall){ int i, pivot, last;
{int part; pivot = x[low];
if(firstcall == 1) { swap(x+low, x+high);
qs(z,0,zend,0); last = low;
} else { for(i = low; i<high; i++){
if(zstart<zend) { if(x[i] <= pivot){
part = separate(z, zstart,zend); swap(x+last, x+i);
qs(z, zstart, part-1,0); last += 1;
qs(z,part+1,zend,0); }
} }
} swap(x+last, x+high);
} return last;
} }
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
Ejercicio 1:
Programar las funciones de quicksort y paralelizar con OpenMP el
algoritmo tomando en cuenta que:
- El algoritmo recursivo qs() del ejemplo puede ser ejecutado en
tareas (tasks), dentro de un constructor parallel
- El condicional:
if(firstcall == 1) {
qs(z,0,zend,0); }
Debe ser ejecutado solo una vez (por uno de los threads)
- Los valores iniciales a ser ordenados serán generados
aleatoriamente con valores entre 1 y 99
- Demostrar el funcionamiento del algoritmo imprimiendo
valores para N=10 y N=20
- Graficar tiempos de cálculo para N=106 vs. np=2,4,8 (no
necesita imprimir los valores en este caso)
Algoritmos
Dynamics of Paralelos
growing SMBHs in galaxy cores
int fib(int n) {
int i, j;
Ejercicio 2: if (n<2)
Programar la función Fibonacci en paralelo return n;
utilizando directivas OPENMP
else {
La función debe ser llamada desde una región
i=fib(n-1);
en paralelo, y utilizando el constructor task para j=fib(n-2);
calcular ambas componentes de la función return i+j;
}
recursiva.
}
Asegúrese que ambos términos sean calculados
antes de ejecutar la suma y retornarla.
Desde la región paralela en el main() solo un
thread debe llamar a la función para evitar
competencia entre threads.
Calcule tiempos de ejecución para n=42 en 2,4,6
hilos vs el tiempo secuencial.