Зависимость функций openMP
У меня есть 5 функций A, B, C, D, E.
Для выполнения DI нужно выполнить B, C, для выполнения EI нужно выполнить A, D.
Я пробовал это
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;
}
но D, E не выполнили
Все эти функции до сих пор возвращают 1 для целей отладки.
Ответы
Переменные a, b, cи d,не может быть использована для общения между потоками , потому что все они частные . Следовательно, каждый поток имеет свою собственную частную копию. Более того, обычно не рекомендуется использовать их для синхронизации.
В вашем коде thread=3никогда не будет ждать, if (th == 3 && (b == 1 && c == 1))потому что:
bиcявляются частными, такthread=3естьb=0иc=0независимо от того, что другие потоки сделали их копии переменныхb=0иc=0.- Ничто не указывает этому потоку ждать ( например, какой-то конструктор, похожий на синхронизацию).
Если вы хотите, чтобы потоки ожидали друг друга, используйте omp barrierвместо этого. Все потоки должны будут вызвать барьер, прежде чем они смогут продолжить свои вычисления.
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;
}
Более сложный подход - использовать задачи с зависимостями - функцию, выпущенную в стандарте OpenMP 4.0. В этой теме уже есть хорошее объяснение того, как эта функция работает .
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;
}
Правильным решением OpenMP было бы использовать задачи с зависимостями данных:
#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);
}
}
Здесь задача Aотмечена как производитель для значения awith, depend(out: a)а задача Dотмечена как производитель dwith depend(out: d). Задача Eотмечена как получатель этих двух значений значком depend(in: a,d). Следуя зависимостям вывода (производитель) и ввода (потребитель), среда выполнения OpenMP строит DAG выполнения (направленный ациклический граф), который сообщает ей правильный порядок выполнения всех задач. Также не нужно пять потоков - достаточно трех.
Наличие кода задания внутри singleконструкции - это очень идиоматичный OpenMP.
Зависимости задач были введены в OpenMP 4.0 еще в 2013 году, поэтому любой современный компилятор, кроме MSVC ++, должен обеспечивать поддержку этой функции.