Dépendance des fonctions openMP
J'ai 5 fonctions A, B, C, D, E.
Pour exécuter DI il faut que B, C soit exécuté, pour exécuter EI il faut que A, D soit exécuté.
J'ai essayé ça
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;
}
mais D, E n'ont pas exécuté
Toutes ces fonctions renvoient 1 jusqu'à présent à des fins de débogage
Réponses
Les variables a
, b
, c
et d,
ne peuvent pas être utilisés pour communiquer entre les threads parce qu'ils sont privés . Par conséquent, chaque thread en a sa propre copie privée. De plus, ce n'est normalement pas une bonne idée de les utiliser à des fins de synchronisation.
Dans votre code, vous thread=3
n'attendriez jamais if (th == 3 && (b == 1 && c == 1))
parce que:
b
etc
sont privées, doncthread=3
ab=0
etc=0
peu importe ce que les autres threads ont fait à leurs copies des variablesb=0
etc=0
.- Rien n'indique à ce thread d'attendre ( par exemple, un constructeur similaire à la synchronisation).
Si vous souhaitez que les threads attendent les uns les autres, utilisez à la omp barrier
place. Tous les threads devront appeler la barrière avant de pouvoir procéder à leur calcul.
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;
}
Une approche plus sophistiquée consisterait à utiliser des tâches avec des dépendances une fonctionnalité publiée sur la norme OpenMP 4.0. Il y a déjà une belle explication sur le fonctionnement de cette fonctionnalité sur ce fil.
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;
}
Une bonne solution OpenMP serait d'utiliser des tâches avec des dépendances de données:
#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);
}
}
Ici, la tâche A
est marquée comme producteur pour la valeur de a
avec depend(out: a)
et la tâche D
est marquée comme producteur de d
avec depend(out: d)
. La tâche E
est marquée comme consommateur de ces deux valeurs avec depend(in: a,d)
. Suite aux dépendances de sortie (producteur) et d'entrée (consommateur), le runtime OpenMP construit un DAG d'exécution (graphe acyclique dirigé) qui lui indique le bon ordre d'exécution de toutes les tâches. Vous n'avez pas non plus besoin de cinq threads - trois suffisent.
Avoir le code de tâche dans une single
construction est OpenMP très idiomatique.
Les dépendances de tâches ont été introduites par OpenMP 4.0 en 2013, donc tout compilateur moderne à l'exception de MSVC ++ devrait prendre en charge cette fonctionnalité.