OpenMP 4.0 规范介绍了 task 指令中的 depend 子句,该子句会对任务调度强制使用其他约束。这些约束仅在同级任务之间建立依赖关系。同级任务是指属于同一任务区域的子任务的 OpenMP 任务。
当使用 depend 子句指定 in 依赖类型时,生成的任务将是所有以前生成的至少引用了 out 或 inout 依赖类型列表中一个列表项目的同级任务的从属任务。在在 depend 子句中指定了 out 或 inout 依赖类型时,生成的任务将是所有以前生成的至少引用了 in、out 或 inout 依赖类型列表中一个列表项目的同级任务的从属任务。
下面的示例对任务依赖性进行了说明。
示例 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
在本例中,任务 1、2、3 和 4 都是同一隐式任务区域的子任务,因此都是同级任务。任务 3 是任务 1 和 2 的从属任务,因为在 depend 子句中的 a 参数指定了依赖性。因此,在任务 1 和 2 完成前,无法调度任务 3。同样,任务 4 是任务 3 的从属任务,因此在任务 3 完成前,无法调度任务 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
在上例中,任务 5 是任务 2 的子任务,不是任务 1、2、3 或 4 的同级任务。因此,尽管 depend 子句引用相同的变量(a、b、c),任务 5 与任务 1、2、3 或 4 之间也不存在依赖关系。
请注意以下有关任务依赖性的提示:
depend 子句中的 in、out 和 inout 依赖类型类似于读写操作,但是 in、out 和 inout 依赖类型只用于建立任务依赖性。它们不指示任务区域内的任何内存访问模式。包含 depend(in:a)、depend(out:a) 或 depend(inout:a) 子句的任务可能可以在其区域内读取或写入变量 a,也可能根本无法访问变量 a。
如果在同一任务指令中同时包含 if 子句和 depend 子句,当 if 子句的条件求值结果为 false 时,开销可能很大。当某个任务包含 if(false) 子句时,遇到该任务的线程必须暂停当前的任务区域,直到生成的任务(包含 if(false) 子句的任务)完成为止。同时,任务调度程序在所生成任务的任务依赖项满足之前,无法调度该任务。由于紧随在显式任务生成之后的点是任务调度点,因此任务调度程序将尝试调度任务,以便满足不延迟 (undeferred) 任务的任务依赖项。查找和调度池中正确任务的开销可能会很高。在最糟糕的情况下,开销可能与拥有一个 taskwait 区域那么大。
相同任务或同级任务的 depend 子句中使用的列表项目必须指明相同存储或不相交存储。因此,如果 depend 子句中出现数组段,请确保数组段指明了相同存储或不相交存储。