OpenMP 4.0 仕様では、task ディレクティブの depend 節を紹介しています。これは、タスクのスケジューリングに対する追加の制約を強制するものです。これらの制約では、兄弟タスク間の依存関係のみを確立します。兄弟タスクとは、同じタスク領域の子タスクである OpenMP タスクです。
in 依存関係タイプを depend 節で指定すると、生成されたタスクは、out または inout 依存関係タイプリスト内の少なくともいずれか 1 つのリスト項目を参照する、以前に生成されたすべての兄弟タスクの従属タスクとなります。out または inout 依存関係タイプを depend 節で指定すると、生成されたタスクは、in、out、または inout 依存関係タイプリスト内の少なくともいずれか 1 つのリスト項目を参照する、以前に生成されたすべての兄弟タスクの従属タスクとなります。
次の例はタスクの依存関係を示しています。
使用例 5 兄弟タスクのみを同期化する depend 節の例% cat -n task_depend_01.c
1 #include <omp.h>
2 #include <stdio.h>
3 #include <unistd.h>
4
5 int main()
6 {
7 int a,b,c;
8
9 #pragma omp parallel
10 {
11 #pragma omp master
12 {
13 #pragma omp task depend(out:a)
14 {
15 #pragma omp critical
16 printf ("Task 1\n");
17 }
18
19 #pragma omp task depend(out:b)
20 {
21 #pragma omp critical
22 printf ("Task 2\n");
23 }
24
25 #pragma omp task depend(in:a,b) depend(out:c)
26 {
27 printf ("Task 3\n");
28 }
29
30 #pragma omp task depend(in:c)
31 {
32 printf ("Task 4\n");
33 }
34 }
35 if (omp_get_thread_num () == 1)
36 sleep(1);
37 }
38 return 0;
39 }
% cc -xopenmp -O3 task_depend_01.c
% a.out
Task 2
Task 1
Task 3
Task 4
% a.out
Task 1
Task 2
Task 3
Task 4
この例では、Task 1、2、3、および 4 はすべて同じ暗黙的タスク領域の子タスクであるため、それらは兄弟タスクです。a 引数への依存関係が depend 節に指定されているため、Task 3 は Task 1 と 2 の従属タスクです。したがって、Task 1 と 2 の両方が完了するまで、Task 3 をスケジュールすることはできません。同様に、Task 4 は Task 3 の従属タスクであるため、Task 3 が完了するまで Task 4 をスケジュールすることはできません。
depend 節は兄弟タスクのみを同期化することに注意してください。次の例 (使用例 6) は、depend 節が兄弟以外のタスクに影響を及ぼさないケースを示しています。
使用例 6 兄弟以外のタスクに影響を及ぼさない depend 節の例% cat -n task_depend_02.c
1 #include <omp.h>
2 #include <stdio.h>
3 #include <unistd.h>
4
5 int main()
6 {
7 int a,b,c;
8
9 #pragma omp parallel
10 {
11 #pragma omp master
12 {
13 #pragma omp task depend(out:a)
14 {
15 #pragma omp critical
16 printf ("Task 1\n");
17 }
18
19 #pragma omp task depend(out:b)
20 {
21 #pragma omp critical
22 printf ("Task 2\n");
23
24 #pragma omp task depend(out:a,b,c)
25 {
26 sleep(1);
27 #pragma omp critical
28 printf ("Task 5\n");
29 }
30 }
31
32 #pragma omp task depend(in:a,b) depend(out:c)
33 {
34 printf ("Task 3\n");
35 }
36
37 #pragma omp task depend(in:c)
38 {
39 printf ("Task 4\n");
40 }
41 }
42 if (omp_get_thread_num () == 1)
43 sleep(1);
44 }
45 return 0;
46 }
% cc -xopenmp -O3 task_depend_02.c
% a.out
Task 1
Task 2
Task 3
Task 4
Task 5
上記の例では、Task 5 は Task 2 の子タスクであり、Task 1、2、3、または 4 の兄弟ではありません。このため、depend 節が同じ変数 (a、b、c) を参照しているにもかかわらず、Task 5 と Task 1、2、3、または 4 の間に依存関係はありません。
タスクの依存関係に関する次のヒントに留意してください。
depend 節の in、out、および inout 依存関係タイプは読み取りと書き込みの操作に似ています。ただし、in、out、および inout 依存関係タイプはタスクの依存関係を確立するためだけのものです。それらは、タスク領域内でメモリーアクセスパターンを一切示しません。depend(in:a)、depend(out:a)、または depend(inout:a) 節を含むタスクは、その領域内で変数 a の読み取りまたは書き込みを行うことはできますが、変数 a にアクセスすることは一切できません。
同じ task ディレクティブに if 節と depend 節の両方があると、if 節の条件が false と評価されたときに負荷が高くなる可能性があります。task に if(false) 節が含まれている場合、それを検出したスレッドは生成されたタスク (if(false) 節を含むタスク) が完了するまで、現在のタスクを中断させる必要があります。同時に、タスクスケジューラはタスクの依存関係が満たされるまで、生成されたタスクをスケジュールしてはいけません。明示的タスクの生成直後のポイントがタスクスケジューリングポイントとなるため、タスクスケジューラは非延期タスクのタスク依存関係が満たされるようにタスクをスケジュールしようとします。プール内の該当するタスクを検索してスケジュールすると、負荷が高くなる可能性があります。最悪のケースでは、taskwait 領域を使用する場合と同じ負荷がかかる可能性があります。
同じタスクまたは兄弟タスクの depend 節で使用されるリスト項目は、同一ストレージまたは別個のストレージを示す必要があります。そのため、配列セクションが depend 節にある場合は、配列セクションが同一または別個のストレージを示していることを確認してください。