Funciones dependencia openMP

Nov 24 2020

Tengo 5 funciones A, B, C, D, E.

Para ejecutar DI es necesario ejecutar B, C, para ejecutar EI es necesario ejecutar A, D.

He probado esto

int main()
{
    #pragma omp parallel num_threads(5) 
    {
        long t1 = clock();
        int a = 0, b = 0, c = 0, d = 0, e = 0;
        int th = omp_get_thread_num();
        if (th == 0) {
            a += A();
            printf("A is finished after %d\n", clock() - t1);
        }
        if (th == 1) {
            b += B();
            printf("B is finished after %d\n", clock() - t1);
        }
        if (th == 2) {
            c += C();
            printf("C is finished after %d\n", clock() - t1);
        }
        if (th == 3 && (b == 1 && c == 1)) {
            d += D();
            printf("D is finished after %d\n", clock() - t1);
        }
        if (th == 4 && (a == 1 && d == 1)) {
            e += E();
            printf("E is finished after %d\n", clock() - t1);
        }

    }
    return 0;
}

pero D, E no se han ejecutado

Todas estas funciones devuelven 1 hasta ahora con fines de depuración

Respuestas

1 dreamcrash Nov 24 2020 at 16:58

Las variables a, b, cy d,no se pueden utilizar para comunicarse entre los hilos porque todos son privados . Por lo tanto, cada hilo tiene su propia copia privada de ellos. Además, normalmente no es una buena idea utilizarlos con fines de sincronización.

En su código thread=3nunca esperaría if (th == 3 && (b == 1 && c == 1))porque:

  1. by cson privados, también lo thread=3han hecho b=0e c=0independientemente de lo que los otros hilos hayan hecho con sus copias de las variables b=0y c=0.
  2. No hay nada que le diga a ese hilo que espere ( por ejemplo, algún constructor similar a la sincronización).

Si desea que los hilos se esperen entre sí, use el omp barrier. Todos los hilos tendrán que llamar a la barrera antes de que puedan continuar con su cálculo.

 int main()
    {
        #pragma omp parallel num_threads(5) 
        {
            long t1 = clock();
            int a = 0, b = 0, c = 0, d = 0, e = 0;
            int th = omp_get_thread_num();
            if (th == 0) {
                a += A();
                printf("A is finished after %d\n", clock() - t1);
            }
            if (th == 1) {
                b += B();
                printf("B is finished after %d\n", clock() - t1);
            }
            if (th == 2) {
                c += C();
                printf("C is finished after %d\n", clock() - t1);
            }
            // Threads will wait for each other here
            #pragma omp barrier 
            if (th == 3) {
                d += D();
                printf("D is finished after %d\n", clock() - t1);
            }
            // Threads will wait for each other here
            #pragma omp barrier 
            if (th == 4) {
                e += E();
                printf("E is finished after %d\n", clock() - t1);
            }
        }
        return 0;
    }

Un enfoque más sofisticado sería utilizar tareas con dependencias, una característica lanzada en el estándar OpenMP 4.0. Ya existe una buena explicación sobre cómo funciona esta característica en este hilo.

int a = 0, b = 0, c = 0, d = 0, e = 0;
#pragma omp parallel num_threads(5) shared(a, b, c, d)
{
  #pragma omp single nowait
  {
      long t1 = clock();

      int th = omp_get_thread_num();
      #pragma omp task  depend (out:a) 
      {
          a += A();
          printf("A is finished after %d\n", clock() - t1);
      }
      #pragma omp task depend (out:b) 
      {
         b += B();
         printf("B is finished after %d\n", clock() - t1);
      }
      #pragma omp task depend (out:c) 
      { 
          c += C();
          printf("C is finished after %d\n", clock() - t1);
      }
     #pragma omp task depend (in:a, b) depend(out:d) 
     {
        d += D(); 
        printf("D is finished after %d\n", clock() - t1);
     }
     #pragma omp task depend (in:a, b)  
     {
       e += E();
       printf("E is finished after %d\n", clock() - t1);
     }
  }
}
return 0;
}
2 HristoIliev Nov 24 2020 at 17:43

Una solución OpenMP adecuada sería utilizar tareas con dependencias de datos:

    #pragma omp parallel num_threads(3)
    #pragma omp single
    {
        double t1 = omp_wtime();
        int a = 0, b = 0, c = 0, d = 0, e = 0;
        #pragma omp task shared(a) depend(out: a)
        {
            a += A();
            printf("A is finished after %f\n", omp_wtime() - t1);
        }
        #pragma omp task shared(b) depend(out: b)
        {
            b += B();
            printf("B is finished after %f\n", omp_wtime() - t1);
        }
        #pragma omp task shared(c) depend(out: c)
        {
            c += C();
            printf("C is finished after %f\n", omp_wtime() - t1);
        }
        #pragma omp task shared(b,c,d) depend(in: b,c) depend(out: d)
        {
            d += D();
            printf("D is finished after %f\n", omp_wtime() - t1);
        }
        #pragma omp task shared(a,d,e) depend(in: a,d)
        {
            e += E();
            printf("E is finished after %f\n", omp_wtime() - t1);
        }

    }

Aquí, la tarea Ase marca como productor para el valor de acon depend(out: a)y la tarea Dse marca como productor de dcon depend(out: d). La tarea Ese marca como consumidor de esos dos valores con depend(in: a,d). Siguiendo las dependencias de salida (productor) y entrada (consumidor), el tiempo de ejecución de OpenMP construye un DAG de ejecución (gráfico acíclico dirigido) que le indica el orden correcto de ejecución de todas las tareas. Tampoco necesita cinco hilos, tres son suficientes.

Tener el código de tareas dentro de una singleconstrucción es OpenMP muy idiomático.

Las dependencias de tareas fueron introducidas por OpenMP 4.0 en 2013, por lo que cualquier compilador moderno, excepto MSVC ++, debería proporcionar soporte para esa función.