1. Indice de pagina
  2. 1. Objetivos
  3. 2. Explicación
  4. 2.1. Código Inicio
  5. 2.2. Ejercicios propuestos
  6. 3. Solución
  7. 3.1. Apartado A
  8. 3.2. Apartado B
  9. 3.3. Apartado C

Cálculo de PI

Objetivos

Explicación

Una vez obtenidos los conocimientos sobre la creación de hebras y reparto de las iteraciones entre estas, vamos a resolver el calculo de PI mediante OpenMP. Pero principalmente en este tutorial prestaremos atención a la memoria compartida y sus problemas.

Código Inicio Descarga

#include <math.h>
#include <iostream>
using namespace std;
 
int main(int argc, char *argv[])
{
    int n;
    cout<<"introduce la precision del calculo (n > 0): ";
    cin>>n;
    double PI25DT = 3.141592653589793238462643;
    double h = 1.0 / (double) n;
    double sum = 0.0;
 
    #pragma omp parallel for shared( sum )
    for (int i = 1; i <= n; i++) {
        double x = h * ((double)i - 0.5);
        sum += (4.0 / (1.0 + x*x));
    }
 
    double pi = sum * h;
    cout << "El valor aproximado de PI es: " << pi << ", con un error de " << fabs(pi -
PI25DT) << endl;
    return 0;
}

La clausula private que acompaña a la directiva #pragma omp parallel indica que las hebras creadas tendrán copias locales de estas.

> g++ -fopenmp 03.cpp -o pi
> ./pi
introduce la precision del calculo (n > 0): 10
El valor aproximado de PI es: 3.14243, con un error de 0.000833331

> ./pi
introduce la precision del calculo (n > 0): 10000
El valor aproximado de PI es: 2.08146, con un error de 1.06013

> ./pi
introduce la precision del calculo (n > 0): 10000
El valor aproximado de PI es: 1.07802, con un error de 2.06357

Vemos tres ejecuciones del programa inicial, el primer resultado con precisión 10 (es decir 10 iteraciones) el resultado es muy bueno. En las dos siguientes ejecuciones con una precisión mucho mas alta, da resultados totalmente erróneos, y dispares. Estos resultados erróneos se deben a que la variable sum es una variable compartida pudiéndose dar el caso de entrelazamiento de instrucciones que den lugar a valores erróneos.
Existen tres soluciones para este caso.

Ejercicios propuestos

Ahora toca experimentar:

Solución

Apartado A

Añadiendo la clausula #pragma omp critical antes de la instrucción que escribe la variable sum conseguimos que ese conjunto de instrucciones no se ejecute a la vez por varias hebras (exclusión mutua). Esta clausula permite un nombre como parámetro que permite coexistir con otras regiones criticas, pudiendo entre dos regiones con distinto nombre ejecutarse simultaneamente.

Código

#pragma omp parallel for shared( sum )
for (int i = 1; i <= n; i++) {
    double x = h * ((double)i - 0.5);
    #pragma omp critical
    sum += (4.0 / (1.0 + x*x));
}

Salida

./pi
introduce la precision del calculo (n > 0): 1000
El valor aproximado de PI es: 3.14159, con un error de 8.33333e-08

Apartado B

La directiva #pragma omp critical se queda grande ya que esta permite la exclusión mutua de bloques de código. Para nuestro caso al tratarse de una instrucción simple podemos decir que sea una instrucción atómica, siento esto mas eficiente que una región critica.

Código

#pragma omp parallel for shared( sum )
for (int i = 1; i <= n; i++) {
    double x = h * ((double)i - 0.5);
    #pragma omp atomic
    sum += (4.0 / (1.0 + x*x));
}

Salida

./pi
introduce la precision del calculo (n > 0): 1000
El valor aproximado de PI es: 3.14159, con un error de 8.33333e-08

Apartado C

Otra solución mas eficiente que las anteriores seria el uso de la clausula reduction. Cuando la variable compartida puede ser tratada por separado y luego mezclar los resultado de cada hebra la aplicación de esta clausula es lo mas eficiente.
recution crea una copia local a cada hebra, cuando terminan todas las hebras terminan las iteraciones se aplica la operación de reducción en nuestro caso se suman todas.

Código

#pragma omp parallel for reduction(+:sum)
for (int i = 1; i <= n; i++) {
    double x = h * ((double)i - 0.5);
    sum += (4.0 / (1.0 + x*x));
}

Salida

./pi
introduce la precision del calculo (n > 0): 1000
El valor aproximado de PI es: 3.14159, con un error de 8.33333e-08
Creado por: Daniel Guerrero Martínez y Sergio Rodríguez Lumley 2010

Valid HTML 4.01 Transitional