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