从OpenMP3.0开始,OpenMP增加了task指令,这是OpenMP3.0中最激动人心的一个新特性。
本文的”术语“大多数是根据个人理解用词,不保证用词准确性。
(1)task基础
OpenMP Tutorials: 任务构造https://computing.llnl.gov/tutorials/openMP/#Task
从功能上说:
1. The TASK construct defines an explicit task, which may be executed by the encountering thread, or deferred for execution by any other thread in the team.
2. The data environment of the task is determined by the data sharing attribute clauses.
3. Task execution is subject to task scheduling - see the OpenMP 3.0 specification document for details.
任务构造定义一个显式的任务,可能会被遇到的线程马上执行,也可能被延迟给线程组内其他线程来执行。任务的执行,依赖于OpenMP的任务调度。
语法:
#pragma omp task [clause ...] newline
if (scalar expression)
final (scalar expression)
untied
default (shared | none)
mergeable
private (list)
firstprivate (list)
shared (list)
structured_block
先来理解task的基本使用再来分析其子句的含义。
task,简单的理解,就是定义一个任务,线程组内的某一个线程来执行此任务。和工作共享结构很类似,我们都知道,for也是某一个线程执行某一个迭代,如果把每一个迭代看成一个task,那么就是task的工作方式了,在for只能用于循环迭代的基础上,OpenMP提供了sections构造,用于构造一个sections,然后里面定义一堆的section,每一个section被一个线程去执行,这样,每一个section也类似于for的每一次迭代,只是使用sections会更灵活,更简单,但是其实,for和sections在某种程度上是可以转换的,用下面的例子来看一个使用sections和for指令分别执行“三个”任务的例子:
当然,这个程序不是这里要讨论的重点,只是为了说明for和sections的一些类似之处,或者其实可以理解为sections其实是for的展开形式,适合于少量的“任务”,并且适合于没有迭代关系的“任务”。很显然,上面的例子适合用sections去解决,因为本身是三个任务,不存在迭代的关系,三个任务和循环迭代变量没有什么关联。
接下来,分析下面的例子:
在此,先总结一下for和sections指令的”缺陷“:无法根据运行时的环境动态的进行任务划分,必须是预先能知道的任务划分的情况。
(2)task的基本使用(说明:当前我的VS2010不支持OpenMP3.0的task指令,下面的测试内容无法使用VS运行,可以采用支持OpenMP的task特性的编译器运行)
使用task解决上面遗留的问题的方法如下:
那么,task和前面的for和sections的区别在于:task是“动态”定义任务的,在运行过程中,只需要使用task就会定义一个任务,任务就会在一个线程上去执行,那么其它的任务就可以并行的执行。可能某一个任务执行了一半的时候,或者甚至要执行完的时候,程序可以去创建第二个任务,任务在一个线程上去执行,一个动态的过程,不像sections和for那样,在运行之前,已经可以判断出可以如何去分配任务。而且,task是可以进行嵌套定义的,可以用于递归的情况等等。
总结task的使用:task主要适用于不规则的循环迭代(如上面的循环)和递归的函数调用。都是无法使用for来完成的情况。
(3)显示任务和隐式任务(implicit&explicit)
task的作用就是创建一个显式的任务,那么什么是隐式的任务呢?OpenMP的任务分为显式和隐式两种,根据我的个人理解,分析下面的代码: