関数の依存関係openMP

Nov 24 2020

私は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を返します。

回答

1 dreamcrash Nov 24 2020 at 16:58

変数はabcd,するために使用することはできません通信、彼らはすべてであるため、スレッド間でのプライベート。したがって、各スレッドには独自のプライベートコピーがあります。さらに、通常、同期の目的でそれらを使用することはお勧めできません。

あなたのコードでthread=3は、次のif (th == 3 && (b == 1 && c == 1))理由で待つことはありません。

  1. bそしてcプライベートなので、thread=3持っているb=0c=0にかかわらず、他のスレッドが彼らにやっているのコピー変数のb=0c=0
  2. そのスレッドに待機するように指示するもの何もありませんたとえば、同期に似たコンストラクター)。

スレッドが互いに待機するようにしたい場合は、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;
    }

より洗練されたアプローチは、OpenMP4.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;
}
2 HristoIliev Nov 24 2020 at 17:43

適切な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値のプロデューサーとしてマークされるadepend(out: a)、タスクはDのプロデューサーとしてマークされているddepend(out: d)。タスクEは、これら2つの値のコンシューマーとしてマークされますdepend(in: a,d)。出力(プロデューサー)と入力(コンシューマー)の依存関係に続いて、OpenMPランタイムは、すべてのタスクの適切な実行順序を指示する実行DAG(有向非巡回グラフ)を構築します。また、5つのスレッドは必要ありません。3つで十分です。

singleコンストラクト内にタスクコードを含めることは、非常に慣用的なOpenMPです。

タスクの依存関係は2013年にOpenMP4.0によって導入されたため、MSVC ++を除く最新のコンパイラーはその機能をサポートする必要があります。