Funciones dependencia openMP
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
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:
bycson privados, también lothread=3han hechob=0ec=0independientemente de lo que los otros hilos hayan hecho con sus copias de las variablesb=0yc=0.- 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;
}
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.