DO
do用来说明一个可以并行执行的循环,它的形式如下:
!$OMP DO [clause[[,] clause]...]
do_loop
[!$OMP END DO [NOWAIT]]
可以附加的子句包括:
PRIVATE(list)
FIRSTPRIVATE(list)
LASTPRIVATE(list)
REDUCTION({operator|intrinsic_procedure_name}:list)
SCHEDULE(type[,chunk])
ORDERED
其中PRIVATE, FIRSTPRIVATE, LASTPRIVATE和REDUCTION为变量属性说明,稍后会做详细说明。(思考,为什么没有SHARED?)
SCHEDULE给出了DO的各次迭代在各线程中的调度方式。它包括两个参数,type为调度类型,chunk为调度块大小,它必须为正整数。
type可能的取值如下:
☆ STATIC
☆ DYNAMIC
☆ GUIDED
☆ RUNTIME
STATIC 静态调度。循环在各线程中的任务分配编译时刻确定。设工作线程数目为nthread,如果chunk缺省,那么循环将被被尽可能均匀的分为nthread块,每个线程各一块;如果给定chunk,循环的各次迭代将按round-robin的方式在各线程中分配,块的大小为chunk。如下面的图所示(一维循环,四个线程):
!$OMP DO SCHEDULE(STATIC)
DO I = 1, 128, 1
do_body
END DO
!$OMP END DO
(a)STATIC调度,chunk缺省
!$OMP DO SCHEDULE(STATIC, 16)
DO I=1,128,1
do_body
END DO
!$OMP END DO
(b)STATIC调度,chunk = 16
DYNAMIC 动态调度。这种情况下,系统将循环的划分成chunk大小的块,放到一个任务池中,各线程各自到任务池中取回本次的任务,完成以后,再取下一个任务。chunk给出了每次所取任务块的大小,如果缺省,系统默认为1。
!$OMP DO SCHEDULE(DYNAMIC, 16)
DO I=1,128,1
do_body
END DO
!$OMP END DO
一个可能的运行时任务分配示意图如下:
(c)DYNAMIC调度,chunk = 16
GUIDED 指数动态调度。它和DYNAMIC调度的基本机制相同,不同的是调度块大小的确定。从任务池中取出的任务块尺寸依次成指数递减(一般是减半)(思考,这有什么好处?)。初始尺寸与编译器实现有关,而chunk给出的是最小的调度块尺寸(缺省为1)。
!$OMP DO SCHEDULE(GUIDED, 16)
DO I=1,128,1
do_body
END DO
!$OMP END DO
一个可能的运行时任务分配示意图如下(假设编译器中给出的初始块大小为32):
(d)GUIDED调度,chunk = 16
RUNTIME 运行时调度。调度参数由运行时环境变量OMP_SCHEDULE给出。请参见后面的内容。
ORDERED子句将这个并行循环标记为"顺序"循环,即循环体中有部分语句必须顺序执行(参见下面的ORDERED/END
ORDERED指导语句的内容)。
END DO处的NOWAIT子句指明已经完成任务的线程不用在END DO处等待,直接可以执行下面的任务(在END DO处默认有一个BARRIER操作,如果给出NOWAIT,则没有这个BARRIER操作,下面各指导语句的NOWAIT含义相同,不再重复说明)。
一个例子:下面的程序用第七章中提到的计算π的算法采用OpenMP Fortran来计算π。
PROGRAM pie
IMPLICIT NONE
INTEGER :: n,i,nnode,nthread,inode
INTEGER, EXTERNAL :: omp_get_num_procs,omp_get_thread_num
REAL(8) :: w,x,sum,pi
WRITE(6,'('' program pi'')')
nnode=omp_get_num_procs()
WRITE(6,'('' number of processors = '',i2)')nnode
! read in number of strips
WRITE(6,'('' enter number of strips to use for integration '')')
READ(5,*)n
w=1.0d0/REAL(n)
sum=0.0d0
WRITE(6,'('' enter number of threads to use '')')
READ(5,*)nthread
CALL omp_set_num_threads(nthread)
!$OMP parallel default(shared) private(i,x,inode) reduction(+:sum)
!$OMP do schedule(static,n/nthread)
DO i=1,n
inode=omp_get_thread_num()
WRITE(6,'(i2,'' doing iteration '',i8)')inode,i
x=w*(REAL(i)-0.5d0)
sum=sum+4.0d0/(1.0d0+x*x)
END DO
!$OMP end parallel
pi=w*sum
WRITE(6,'('' pi = '',g17.10)')pi
END PROGRAM pie
用OpenMP编译器可以直接把上面的程序编译为可运行程序,比如使用Intel编译器,使用命令可能如下:
ifc -openmp -o pie pie.f
然后用下面的命令设置环境变量并运行:
setenv OMP_NUM_THREADS 4
./pie
|