当前位置: 首页 > 并行计算基础 > 正文

openMP并行程序设计基础1

什么是openMP?

OpenMp是由OpenMP Architecture Review Board牵头提出的,并已被广泛接受的,用于共享内存并行系统的多处理器程序设计的一套指导性的编译处理方案(Compiler Directive)。OpenMP支持的编程语言包括C语言C++Fortran;而支持OpenMp的编译器包括Sun Compiler,GNU Compiler和Intel Compiler等。OpenMp提供了对并行算法的高层的抽象描述,程序员通过在源代码中加入专用的pragma来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。当选择忽略这些pragma,或者编译器不支持OpenMp时,程序又可退化为通常的程序(一般为串行),代码仍然可以正常运作,只是不能利用多线程来加速程序执行。

OpenMp 和 Pthread 一样都是共享内存编程的API但是,他们有许多本质的不同。简单来说Pthread 是比较偏底层的编程方式,由程序员分别管理每一个进程的行为与姿态。相反的,OpenMp 则要求编译器支持某些操作,所以完全有可能本地的编译器并不能把程序编译成并行程序。

代价就是,每个线程行为的每一个细节都需要我们自己定义,他被明确的设计为可以用来对已有串行程序进行增量式的并行化。

hello 程序例程

程序使用宏来启动每一个线程,类似pthread 明显,OpenMP 比Pthread 层次更高。

parallel 是一条指令,表明结构化代码块。

注意:标准输出被所有的线程共享,每一个线程都可以执行输出语句,对于标准输出的访问没有调度,因此线程打印他们结果的实际顺序是不确定的。

错误检查相关:

step 1:检查传参是否正确。

step2:编译器支持OpenMp

OPENMP 的互斥指令

指令下的程序只能被互斥的执行。

归约操作符:是一个二元操作,归约就是将相同的归约操作符重复的应用到操作数序列来得到一个计算结果。另外,所有操作的中间结果保存在同一个变量里:归约变量。OpenMP 创建一个临界区,并且在临界区中,将存储在私有变量中的值进行相加。

操作符是 +, —, *, |,|| ,& ,&& ,^ 中的任意一个。

parallel for 指令:生成一组线程来执行后面的结构化代码块。然而在parallel for 指令后的结构块必须是for 循环。运用parallel for 指令,系统通过线程在线程之间划分循环迭代并行化for 循环。线程间的缺省划分方式由系统决定。大部分系统会粗略的使用块划分。在一个被parallel for 指令循环并行化循环中,循环变量的缺省作用是私有的,在我们的代码中,每个线程组中的线程拥有自己的I的副本。(如下,approx 是一个归约变量)

注意:只能并行化for 循环。不能处理while   do_while 循环。并且不能并行化带有其他出口的for循环。

要点:

OpenMp不检查被parallel for指令并行化的循环所包含的迭代间的依赖关系。而是由程序员来识别这依赖关系。

一个或者给更多的迭代结果依赖于其他迭代的循环,一般不能被OpenMp 争取的并行化。一般是数据依赖和循环依赖。

循环调度:

系统会粗略的优化循环,大体使用块划分的方式,但是我们有时需要自己规定线程的调度方式。API为我们提供了指令schedule 指令帮助我们自定义循环的调度。

例如:

若函数f(x) 的执行实现随着参数的复杂而增大,那么粗暴的块划分就是没有用的,我们需要使用自定义循环调度方式。

如一般式所表达结果:

type                        可以是以下的任意一个:

static                       迭代可以在循环结束前分配给进程。

dynam 和 guided 迭代在循环执行的时被分配给线程,因此在一个线程完成了它的当前迭代集合后,他能从运行时系统请求更多。

auto                       编译器和操作系统决定调度方式

runtime                调度在运行时决定

chuncksize 是一个正整数,迭代次数依赖这个数字,只有static  dynamic  guided 调度有chunksize .虽然确定了调度的细节,但是准确的解释还是依赖于type.

上一篇讲到了循环调度指令,接着说循环调度参数。

static调度类型

系统以轮转的方式分配chunksize块个迭代给每个线程。也可以理解为以数字为公差迭代。

dynamic 和 guided 调度类型

在dynamic 调度中,迭代被分成chunksize个连续的迭代块,每个线程执行一块,当一个线程完成时请求分配另一任务。知道所有任务完成。缺省的线程数设置1.

在guided 调度中每个线程也执行一块,并且当一个线程完成一块,将请求另一块。然而在这种调度下,每当一个块完成后,新块的大小会变小。缺省设置为1.

runtime调度类型

根据环境变量OMP_SCHEDULE来决定如何调度循环。

调度选择:

开销比较 guided 》 dynamic 》 static

当一个程序的性能基本满意的时候,我们可以不必刻意的去追求修改。

有一些默认的情况选择可以供我们选择:

1.如果循环的每次迭代量几乎需要相同的计算量,那么默认的调度方式较好。

2.如果随着循环的进行,迭代的计算量线性递增或者递减。那么采用较小的 chunksize 的static较好。

3.如果每次不确定我们就使用runtime 修改环境变量来测试。

critical atomic 指令和锁的比较:

一般而言atomic 指令是实现互斥访问最快的方法。因此如果临界区是由特定形式的赋值语句组成,则使用atomic 指令至少不会比使用其他方式慢。

OpenMp 有两种锁:简单锁和嵌套锁。简单锁再被释放前只能获得一次,而只有一个嵌套锁再被释放前可以被同一个线程获得多次。简单锁的类型是omp_lock_t ,定义简单锁的函数包括:

void omp_init_lock(omp_lock_t *lock_p);
void omp_set_lock(omp_lock_t * lock_p);
void omp_unset_lock(omp_lock_t * lock_p);
void omp_destroy_lock(omp_lock_t *lock_p);

 

本文固定链接: http://zmrlinux.com/2017/01/28/openmp%e5%b9%b6%e8%a1%8c%e7%a8%8b%e5%ba%8f%e8%ae%be%e8%ae%a1%e5%9f%ba%e7%a1%801/ | Kernel & Me

该日志由 root 于2017年01月28日发表在 并行计算基础 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: openMP并行程序设计基础1 | Kernel & Me
【上一篇】
【下一篇】