Funzioni dipendenza openMP
Ho 5 funzioni A, B, C, D, E.
Per eseguire DI è necessario eseguire B, C, per eseguire EI è necessario eseguire A, D.
L'ho provato
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;
}
ma D, E non sono stati eseguiti
Tutte queste funzioni restituiscono 1 fino ad ora per scopi di debug
Risposte
Le variabili a, b, ce d,non possono essere utilizzati per comunicare tra i thread perché sono tutti privati . Quindi, ogni thread ha la propria copia privata di essi. Inoltre, normalmente non è una buona idea usarli per scopi di sincronizzazione.
Nel tuo codice thread=3non aspetteresti mai if (th == 3 && (b == 1 && c == 1))perché:
becsono privati, cosìthread=3comeb=0ec=0indipendentemente da ciò che gli altri thread hanno fatto alle loro copie delle variabilib=0ec=0.- Non c'è niente che dica a quel thread di aspettare ( ad esempio, qualche costruttore di sincronizzazione).
Se vuoi che i thread si aspettino l'un l'altro usa omp barrierinvece. Tutti i thread dovranno chiamare la barriera prima di poter procedere con il loro calcolo.
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 approccio più sofisticato potrebbe essere quello di utilizzare attività con dipendenze, una funzionalità rilasciata sullo standard OpenMP 4.0. C'è già una bella spiegazione su come funziona questa funzione su questo thread.
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 soluzione OpenMP adeguata sarebbe quella di utilizzare attività con dipendenze dai dati:
#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);
}
}
Qui, l'attività Aè contrassegnata come produttore per il valore di awith depend(out: a)e l'attività Dè contrassegnata come produttore di dcon depend(out: d). L'attività Eè contrassegnata come consumatore di questi due valori con depend(in: a,d). Seguendo le dipendenze di output (producer) e input (consumer), il runtime di OpenMP crea un DAG di esecuzione (grafico aciclico diretto) che gli indica il corretto ordine di esecuzione di tutte le attività. Inoltre non hai bisogno di cinque thread: tre sono sufficienti.
Avere il codice di tasking all'interno di un singlecostrutto è OpenMP molto idiomatico.
Le dipendenze delle attività sono state introdotte da OpenMP 4.0 nel 2013, quindi qualsiasi compilatore moderno, ad eccezione di MSVC ++, dovrebbe fornire supporto per tale funzionalità.