2015年11月 新的 OpenMP 4.5 标准已经被释放了。(OpenMp是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多处理器程序设计的一套指导性的编译处理方案(Compiler Directive)并且有多个新的结构体带给用户。OpenMP API由编译器指令和库例程组成高度并行于 C, C++ 和 Fortran 项目. 即将更新的GCC版本为最近版的标准添加了支持。
本文高亮了一些最新特性,以及众所周知的地方方便查看
Taakloop construct(任务循环结构)
咱们马上开始,这个“任务循环”结构是被新添加到4.5版本之中的,如名所示,允许分割迭代循环到任务,它能随意的等待完成哪些任务并且每个被创建的任务都可以分配1个或多个迭代循环。例如:
#pragma omp taskloop num_tasks (32)
for (long l = 0; l < 1024; l++)
do_something (l);
上面的代码将在一个任务组中创建32项相互关系的任务,还有一项合理的实施就是给每项任务分配32次迭代。因为有一个隐式的课题在那里面,我们遇到的任务将依赖其它所有任务的完成。在OpenMP 4.0和更早的版本里,为了实现类似的效果,我们必须手动完成那些任务,像下面的例子:
#pragma omp taskgroup
for (int tmp = 0; tmp < 32; tmp++)
#pragma omp task
for (long l = tmp * 32; l < tmp * 32 + 32; l++)
do_something (l);
当代码中有很多死循环或者如果在循环中使用C++迭代器时,我们手动处理将会变得越来越困难,所以运用任务循环概念将会大大简化代码源。我们可以使用grainsize措施,而不是指定我们应该创建的任务数目。它指定了每项任务中应该有多少次的迭代(从指定的grain-size到小于它两倍的值),并且这个措施可以自动计算任务的的数目。另外,如果num_tasks和grainsize选项都不见了,这个措施里会选择一些适宜的默认选项。
OpenMP4.5中另一个任务分配的改变是增加了任务优先级, 可以用priority子句指定任务的优先级。正如你所预见的,任务优先级更高的任务将被优先调度。
交叉并行
一些样例考虑对有内部迭代依赖的循环进行并行化。这通常是可行的,只要并行工作的线程不会花费大部分的时间等待其他线程。
OpenMP 4和更早的版本提出的“有序”的构建。在一个循环结构中,以一个ordered子句为标记,将有序结构的主体在循环中执行。OpenMP 4.5可以使用命令(无本体)表示依赖于以前版本的迭代循环,所有其他的迭代可能取决于一个迭代的数据计算。例如:
#pragma omp for ordered(2)
for (int i = 0; i < M; i++)
for (int j = 0; j < N; j++)
a[i][j] = foo (i, j);
#pragma omp ordered depend (sink: i - 1, j) depend (sink: i, j - 1)
b[i][j] = bar (a[i][j], b[i - 1][j], b[i][j - 1]);
#pragma omp ordered depend (source)
baz (a[i][j], b[i][j]);
}
以上的例子表明,只有外部循环分布在一组线程中。第一个ordered的指令告诉运行时等待指定的早期迭代完成,而最后一个ordered的指令标志着循环计算出的所有数据的其他循环可能存在依赖。
在这个例子中,编译器会忽视 depend (sink: i, j - 1) 依赖,因为外部循环参与了工作负载,内部循环使用字典顺序迭代;因此,等到 i, j 迭代被执行的时候, i, j – 1迭代肯定就完成了。
除了允许 depend 子句在命令指令中,OpenMP 4.5 还一直允许 threads 和 simd 子句有序构造,还允许 在 SIMD 循环中使用 #pragma omp ordered simd 。这标志着在字典顺序执行代码的区域内 SIMD 循环,对于一小部分代码,不应向量化的 vectorizable 循环。
数据共享变更
包括最新的更新,C++参考现在在私有化条款中已经被允许,以前他们只允许共享条款。
C++的方法中,对象的方法调用时,现在对它的非静态数据成员进行私有化访问是有可能的,只要在相应的OpenMP区域中通过使用id-expression(id表达式)来访问,而非显式使用this->memeber来引用。
C和C++中的reduction clause(reduction语块)允许数组区间,因此C/C++的数组可以reduction而无须包装为结构体或是类,也不需要定义用户所定义的reduction。 linear clause(线性语块)现在可以用在循环体中,而此前只允许在SIMD结构中使用。
卸载(Offloading)的变化
Offloading 可能是OpenMP标准中变化最大的部分, 由此引入一了些代码级别的不兼容——这些大多与scalar变量的非显式的引用有关,如C/C++对像区域中的指针!
OpenMP 4,这些变量,除非明确在地图条款提到建设目标,进行隐式映射从–意味着指针的主机端值将被复制到目标(通常是不是真的有用,有一个指针)目标值复制到主机(通常也不受欢迎),或最后没有复制如果变量已经映射。
OpenMP 4.5,除非defaultmap(来自:标量)条款使用标量变量是隐式的私有化,如果新firstprivate条款允许建设的目标是让他们使用–意义的值都复制到目标区域,并没有复制回。
在许多情况下,甚至会用OpenMP 4的代码编写工作,但异常主要是如果你需要一个标量值从装置在目标区域的结束。例如,下列OpenMP 4将不再工作:
void foo () {
double sum = 0.0;
#pragma omp target map(array[0:N])
#pragma omp teams distribute parallel for simd reduction(+:sum)
for (int i = 0; i < N; i++)
sum += array[i];
return sum;
目标结构现在被视为一个隐式的任务区域,并添加新的目标输入数据和目标退出数据结构。这意味着映射和映射变量联合国现在可以进行同步或异步,和在不同的函数或方法–例如可以输入数据在C++构造函数和出口数据在C++析构函数。
声明目标指令已被扩展,并且现在可以标记为延迟映射的全局变量(例如大数组)。设备内存例程已经添加显式分配,分配和内存传输主机和卸载设备之间,以及与本地设备实现交互的条款被加入:use_device_ptr条款目标的数据结构和is_device_ptr条款目标构建。
GCC 6目前支持卸载英特尔XeonE5骑士着陆,一些OpenMP 4.5目标构建可卸载到AMD HSA GPGPU,OpenMP卸载Nvidia PTX在作品(OpenACC卸载Nvidia PTX已经支持)。
在OpenMP 4.5 中还有一些较小的改进,包括:
提升了对Fortran 2003 的支持。
关键架构和新提示锁API例程添加了一个hint子句,它允许应用通知运行时资源争用和猜测的必要性。
扩展了if子句,当使用组合和复合构造时,可以指定if子句应用的构造,并且可以对不同构造指定不同的if子句。
在#pragma omp declare simd指令的线性子句中,现在可以通过val,uval和ref修饰指定,不管引用是线性的或引用的变量值是线性的。