The OpenMP 4.0 specification introduces the depend clause on the task directive, which enforces additional constraints on the scheduling of tasks. These constraints establish dependences between sibling tasks only. Sibling tasks are OpenMP tasks that are child tasks of the same task region.
When the in dependence-type is specified with the depend clause, the generated task will be a dependent task of all previously generated sibling tasks that reference at least one of the list items in an out or inout dependence-type list. When the out or inout dependence-type is specified on the depend clause, the generated task will be a dependent task of all previously generated sibling tasks that reference at least one of the list items in an in, out, or inout dependence-type list.
The following example illustrates task dependence.
Example 4-3 Illustrating the depend Clause Synchronizing Only Sibling Tasks% 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
In this example, Tasks 1, 2, 3, and 4 are all child tasks of the same implicit task region, and so are sibling tasks. Task 3 is a dependent task of Tasks 1 and 2 because of the dependences on the a argument specified in the depend clauses. Therefore, Task 3 cannot be scheduled until both Tasks 1 and 2 have completed. Similarly, Task 4 is a dependent task of task 3 so Task 4 cannot be scheduled until Task 3 has completed.
Note that the depend clause synchronizes sibling tasks only. The following example (Example 4–4) shows a case where the depend clause does not affect non-sibling tasks.
Example 4-4 Illustrating the depend Clause Not Affecting non-Sibling Tasks% 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
In this above example, Task 5 is a child task of Task 2 and is not a sibling of Tasks 1, 2, 3 or 4. So, despite the depend clauses referencing the same variables (a, b, c), there is no dependence between Task 5 and Tasks 1, 2, 3, or 4.