web-dev-qa-db-ja.com

セクションとタスクのopenmpの違い

OpenMPの違いは何ですか?

#pragma omp parallel sections
    {
        #pragma omp section
        {
           fct1();
        }
        #pragma omp section
        {
           fct2();
        }
}

および:

#pragma omp parallel 
{
    #pragma omp single
    {
       #pragma omp task
       fct1();
       #pragma omp task
       fct2();
    }
}

2番目のコードが正しいかどうかわかりません...

47
Arkerone

タスクとセクションの違いは、コードが実行される時間枠にあります。セクションはsectionsコンストラクトで囲まれ、(nowait句が指定されていない限り)スレッドはすべてのセクションが実行されるまでそれを残しません。

_                 [    sections     ]
Thread 0: -------< section 1 >---->*------
Thread 1: -------< section 2      >*------
Thread 2: ------------------------>*------
...                                *
Thread N-1: ---------------------->*------
_

ここで、Nスレッドは、2つのセクションを持つsectionsコンストラクトに遭遇します。最初の2つのスレッドは、それぞれ1つのセクションを実行します。他の_N-2_スレッドは、セクションコンストラクトの最後にある暗黙のバリアで単純に待機します(ここでは_*_として表示)。

タスクは、いわゆるタスクスケジューリングポイントで可能な限りキューに入れられ、実行されます。ある条件下では、ランタイムは、その寿命の途中であっても、スレッド間でタスクを移動することを許可される可能性があります。このようなタスクはアンタイドと呼ばれ、アンタイドタスクは1つのスレッドで実行を開始し、あるスケジューリングポイントでランタイムによって別のスレッドに移行される場合があります。

それでも、タスクとセクションは多くの点で似ています。たとえば、次の2つのコードフラグメントは、本質的に同じ結果を達成します。

_// sections
...
#pragma omp sections
{
   #pragma omp section
   foo();
   #pragma omp section
   bar();
}
...

// tasks
...
#pragma omp single nowait
{
   #pragma omp task
   foo();
   #pragma omp task
   bar();
}
#pragma omp taskwait
...
_

taskwaitbarrierと非常によく似ていますが、タスクの場合-現在の実行フローは、キューに入れられたすべてのタスクが実行されるまで一時停止します。これはスケジューリングポイントです。つまり、スレッドがタスクを処理できるようにします。タスクが1つのスレッドのみで作成されるように、single構成が必要です。 singleコンストラクトがなかった場合、各タスクは_num_threads_回作成されますが、これは望んでいない場合があります。 nowaitコンストラクトのsingle句は、singleコンストラクトが実行されるまで待機しないように他のスレッドに指示します(つまり、singleコンストラクト)。したがって、彼らはすぐにtaskwaitをヒットし、タスクの処理を開始します。

taskwaitは、明確にするためにここに示す明示的なスケジューリングポイントです。明示的か暗黙的かに関係なく、最も重要なのはバリア同期内の暗黙的なスケジューリングポイントもあります。したがって、上記のコードは単純に次のように書くこともできます。

_// tasks
...
#pragma omp single
{
   #pragma omp task
   foo();
   #pragma omp task
   bar();
}
...
_

以下に、3つのスレッドがある場合に発生する可能性のあるシナリオの1つを示します。

_               +--+-->[ task queue ]--+
               |  |                   |
               |  |       +-----------+
               |  |       |
Thread 0: --< single >-|  v  |-----
Thread 1: -------->|< foo() >|-----
Thread 2: -------->|< bar() >|-----
_

ここに表示される_| ... |_は、スケジューリングポイントのアクション(taskwaitディレクティブまたは暗黙的なバリアのいずれか)です。基本的に、スレッド_1_および_2_は、その時点で実行していることを中断し、キューからタスクの処理を開始します。すべてのタスクが処理されると、スレッドは通常の実行フローを再開します。スレッド_1_および_2_は、スレッド_0_がsingleコンストラクトを終了する前にスケジューリングポイントに到達する可能性があるため、左側の_|_ sを揃える必要はありません。 (これは上の図に表されています)。

また、スレッド_1_がfoo()タスクの処理を終了し、他のスレッドがタスクを要求できるようになる前に別のタスクを要求できる場合もあります。したがって、foo()bar()の両方が同じスレッドによって実行される可能性があります。

_               +--+-->[ task queue ]--+
               |  |                   |
               |  |      +------------+
               |  |      |
Thread 0: --< single >-| v             |---
Thread 1: --------->|< foo() >< bar() >|---
Thread 2: --------------------->|      |---
_

スレッド2が遅すぎる場合、シングルアウトスレッドが2番目のタスクを実行する可能性もあります。

_               +--+-->[ task queue ]--+
               |  |                   |
               |  |      +------------+
               |  |      |
Thread 0: --< single >-| v < bar() >|---
Thread 1: --------->|< foo() >      |---
Thread 2: ----------------->|       |---
_

場合によっては、コンパイラまたはOpenMPランタイムがタスクキューを完全にバイパスして、タスクをシリアルに実行することさえあります。

_Thread 0: --< single: foo(); bar() >*---
Thread 1: ------------------------->*---
Thread 2: ------------------------->*---
_

リージョンのコード内にタスクスケジューリングポイントが存在しない場合、OpenMPランタイムは、適切と判断したときにタスクを開始する場合があります。たとえば、parallelリージョンの最後のバリアに到達するまで、すべてのタスクが延期される可能性があります。

119
Hristo Iliev