関数の依存関係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;
}
より洗練されたアプローチは、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;
}
適切な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
値のプロデューサーとしてマークされるa
とdepend(out: a)
、タスクはD
のプロデューサーとしてマークされているd
とdepend(out: d)
。タスクE
は、これら2つの値のコンシューマーとしてマークされますdepend(in: a,d)
。出力(プロデューサー)と入力(コンシューマー)の依存関係に続いて、OpenMPランタイムは、すべてのタスクの適切な実行順序を指示する実行DAG(有向非巡回グラフ)を構築します。また、5つのスレッドは必要ありません。3つで十分です。
single
コンストラクト内にタスクコードを含めることは、非常に慣用的なOpenMPです。
タスクの依存関係は2013年にOpenMP4.0によって導入されたため、MSVC ++を除く最新のコンパイラーはその機能をサポートする必要があります。