!$omp parallel CLAUSE
!$omp DIRECTION
[ structured block of code ]
!$omp end DIRECTION
!$omp end parallelhtml
其中DIRECTION是Openmp指令,有sections,do等,指定并行行为。中间的linux
[ structured block of code ]express
便是须要并线的程序块。除了上面的称为DIRECTION的指令语句外,Openmp还须要称为CLAUSE的从句对并行进行限制和说明。好比,须要对私有变量进行声明时就须要用到private从句(这是常常要遇到的,后面会以例子说明)。在fortran的串行编译下,以“!”打头的都处于屏蔽状态是不起做用的。加了openmp编译参数后才会生效。编程
我在程序编写中用到最多的是do指令,偶尔用一下sections。do指令一般用来并行化do循环。原本用一个线程来执行的长的do循环被分割成几个部分让多个线程同时执行,这样就节省了时间。sections指令一般用来将先后没有依赖关系的程序块(也即本来不分前后,你换下顺序也无所谓)并行化。由于无关联,因此能够同时执行。windows
能够说,若程序主要用来作计算,掌握了do和sections这两个指令足矣! 数组
!$OMP PARALLEL SHARED(A,B,C), PRIVATE(I) !//Paralell块开始
!$OMP SECTIONS !//Sections开始
!$OMP SECTION !//第一个section
DO I = 1, N/2
C(I) = A(I) + B(I)
END DO
!$OMP SECTION !//第二个section
DO I = 1+N/2, N
C(I) = A(I) + B(I)
END DO
!$OMP END SECTIONS NOWAIT !//Sections结束
!$OMP END PARALLEL !//Paralell块结束app
这个并行语句将原本从1到N的循环手动分为两个部分并行执行。上面的shared,private就是从句(clause),声明A,B,C为公有的,而循环指标I是私有的。由于两个section同时执行,都会对I进行改变,因此两个section的循环指标必须彼此独立,不能是同一个变量。PRIVATE会自动将这个会引起冲突的变量按需生成多个拷贝以供使用。最后的!$OMP END SECTIONS NOWAIT语句告诉两个线程可各自自行结束,无需相互等待。函数
2.Do 指令的应用:
上面用Section实现的功能彻底能够用Do来实现:spa
!$OMP PARALLEL SHARED(A,B,C), PRIVATE(I) !//Paralell块开始
!$OMP DO !//Do的并行开始
DO I = 1, N
C(I) = A(I) + B(I)
END DO
!$OMP END DO !//Do的并行结束
!$OMP END PARALLEL !//Paralell块结束线程
Do循环原本是从1到N,如今有多少个线程就分为多少个部分执行,比上面的section更方便智能。不用担忧循环次数N不能被线程数整除~。通常状况下,各个线程均分循环次数,可是在某些循环指标下运算可能比较快,因此各个线程的运算时间可能不尽相同。这时候若是须要让各个线程都结束了才能再往下(没有NOWAIT),快的线程就必须等待慢的线程。为了解决这个问题须要加上schedule从句,首行变为以下:
!$OMP PARALLEL SHARED(A,B,C), PRIVATE(I),SCHEDULE(DYNAMIC)
这个SCHEDULE(DYNAMIC)从句告诉程序动态调整并线方式,那些任务轻松运算快的线程会自动去帮任务重运算慢的线程,力争全部线程同时完成任务。
关于Do的积累计算,如累加,须要加上REDUCTION从句:
C=0.d0
!$OMP PARALLEL SHARED(A,C), PRIVATE(I),REDUCTION(+:C)
!$OMP DO
DO I = 1, N
C =C+ A(I)
END DO
!$OMP END DO
!$OMP END PARALLEL
这里将累加分为几个部分由多个线程进行运算,因为各个线程都在0.d0的基础上开始计算它该算的部分,因此最后必须将各部分计算的结果再次求和。REDUCTION(+:C)从句就实现了这个效果。相似的叠乘等等用相似写法,只需把“:”前的运算符改成乘法“*”便可。
2.据个人经验,Fortran中若是不特别声明,变量都是默认公有的。这一点能够用DEFAULT(PRIVATE/SHARED)从句强行改变。循环指标默认是私有的,无需本身另外声明。放在common域中的变量都是全局的,若要将这些全局变量私有化,可以使用threadprivate指令(参见文章:OpenMP并行编程:threadprivate指令)。
3.并行引导语句能够简化,但要注意先后配对。好比上面那个累加的例子能够这样写:
C=0.d0
!$OMP PARALLEL DO SHARED(A,C), PRIVATE(I),REDUCTION(+:C)
DO I = 1, N
C =C+ A(I)
END DO
!$OMP END PARALLEL DO
也便可以将从句加在指令以后。
4.Fortran+Openmp的编译问题:
通常来讲,加上-openmp编译参数便可。如:
ifort -openmp -o exe.out main.f
gfortran用-fopenmp编译参数,g77和ifort同样用-openmp参数。
若是用Makefile,将编译参数放在合适的地方。
5.对于多重do循环,若是中间变量太多,对私有公有弄不清楚或者虽然清楚可是闲麻烦,能够保留最外层循环,将里面的循环在别处写成一个子函数或子程序 ,而后在此处调用。这样从结构上看就是对一重循环进行并行化,条理清楚不容易出错。固然,传递给子函数或子程序的参数通常是要声明私有的。
6.将串行程序改成Openmp并行程序后,在加与不加-openmp编译参数的状况下分别编译并运算,比较并行与串行的结果,确保并行块没有改错。
7.能够在并行开始前指定由多少个线程来并行。在单cpu单核心的机器上也能够(虽然没有实际意义,但能够用来调试并行程序):
CALL OMP_SET_NUM_THREADS(scalar_integer_expression)
其中scalar_integer_expression是个整形变量,指定并行的线程数目。
8.Openmp对私有变量的大小有限制。因此当遇到这样的状况,通常就是由这个限制形成的:不加openmp并行时程序没有问题,加了openmp并行时出现断错误(segmentation fault),可是当把某个(一些)私有数组的维数变小时,段错误消失并且和串行时结果一致。
解决办法(linux下,windows下另外search)以下:
在linux终端执行
ulimit -s unlimited ;export KMP_STACKSIZE=2048000后一个数字参数足够大便可。