Sun Studio 12:Fortran 编程指南

10.3.1 可并行化的循环

如果以下条件成立,则循环适用于显式并行化:

10.3.1.1 作用域规则:专用和共享

专用变量或数组归循环的单次迭代专用。在一次迭代中赋予专用变量或数组的值不会传播给循环内的其他任何迭代。

共享变量或数组在所有迭代间共享。在某次迭代中赋予共享变量或数组的值为循环内的其他迭代所见。

如果显式并行化的循环包含共享引用,则必须确保共享不会造成正确性问题。编译器在共享变量的更新或访问上不同步。

如果在一个循环内将某变量指定为专用,并且其唯一一次初始化位于另一循环中,则该变量的值在此循环中可能保持未定义。

10.3.1.2 循环中的子程序调用

循环(或从已调用例程内调用的任何子程序)中的子程序调用可能会导致产生数据依赖性,这种数据依赖性很容易被忽略,而不通过调用链深入分析数据和控制流。尽管最好是并行化执行大量工作的最外层循环,但这些循环往往正是涉及子程序调用的循环。

由于这种过程间的分析很困难并且会大大增加编译时间,所以自动并行化模式不会尝试这样做。对于显式并行化,编译器会为标有 PARALLEL DODOALL 指令的循环生成并行化代码,即使它包含子程序调用。确保此循环以及此循环包括的所有循环(包括被调用的子程序)中不存在任何数据依赖性仍是程序员的责任。

不同线程多次调用某个例程会造成问题,这些问题源自对互相干扰的局部静态变量的引用。使例程中的所有局部变量均为自动而不是静态可防止这种情况。此时,子程序的每次调用都会在栈中保留自己唯一的局部变量存储,任何两次调用均不会相互干扰。

AUTOMATIC 语句中列出子程序局部变量或用 -stackvar 选项编译子程序,通过这两种方法中的任一种,可以使子程序局部变量变为驻留在栈中的自动变量。但是,DATA 语句中初始化的局部变量必须进行改写,才能在实际赋值中进行初始化。


注 –

将局部变量分配给栈会造成栈溢出。有关如何增加栈大小的信息,请参见10.1.6 栈、栈大小和并行化


10.3.1.3 显式并行化抑制因素

一般而言,如果您显式指导编译器对循环进行并行化,编译器就会执行。但也有例外情况-存在一些编译器不进行并行化的循环。

下面是可检测到的主要抑制因素,这些抑制因素可以防止DO 循环进行显式并行化:

通过使用 -vpara-loopinfo 进行编译,可以得到诊断消息,指出在显式并行化循环过程中编译器是否检测到问题。

下表列出了编译器检测到的典型并行化问题:

表 10–3 显式并行化问题

问题 

已并行化 

警告消息 

循环嵌套在并行化了的另一循环内。 

否 

否 

循环在并行化循环体内调用的某个子例程中。 

否 

否 

流控制语句允许跳出循环。 

否 

是 

循环的索引变量受副作用影响。 

是 

否 

循环中的某变量具有循环携带依赖性。 

是 

是 

在循环中使用 I/O 语句-通常是不明智的,因为输出顺序无法预料。

是 

否 

示例:嵌套循环:


      ...
!$OMP PARALLEL DO
      do 900 i = 1, 1000      !  Parallelized (outer loop)
        do 200 j = 1, 1000    !  Not parallelized, no warning
            ...
200   continue
900      continue
      ...

示例:子例程中已并行化的循环:


 program main
      ...
!$OMP PARALLEL DO
      do 100 i = 1, 200      <-parallelized
        ...
        call calc (a, x)
        ...
100      continue
      ...
subroutine calc ( b, y )
      ...
!$OMP PARALLEL DO
      do 1 m = 1, 1000       <-not parallelized
        ...
1      continue
      return
      end

在此例中,由于子例程本身是以并行方式运行的,所以子例程中的循环未被并行化。

示例:跳出循环:


!$omp parallel do
      do i = 1, 1000     ! <- Not parallelized, error issued
        ...
        if (a(i) .gt. min_threshold ) go to 20
        ...
      end do
20      continue
      ...

如果标记进行并行化的循环外有转跳,编译器会发出诊断错误。

示例:循环中的某个变量具有循环携带依赖性:


demo% cat vpfn.f
      real function fn (n,x,y,z)
      real y(*),x(*),z(*)
      s = 0.0
!$omp parallel do private(i,s) shared(x,y,z)
      do  i = 1, n
          x(i) = s
          s = y(i)*z(i)
      enddo
      fn=x(10)
      return
      end
demo% f95 -c -vpara -loopinfo -openmp -O4 vpfn.f
"vpfn.f", line 5: Warning: the loop may have parallelization inhibiting reference
"vpfn.f", line 5: PARALLELIZED, user pragma used

在此,循环被并行化,但在警告中诊断出可能的循环携带依赖性。但要注意,编译器并不能诊断出所有循环依赖性。

10.3.1.4 显式并行化时的 I/O

在下列情况下,可以在并行执行的循环中执行 I/O:

示例:循环中有 I/O 语句


!$OMP PARALLEL DO PRIVATE(k)
      do i = 1, 10     !  Parallelized
        k = i
        call show ( k )
      end do
      end
      subroutine show( j )
      write(6,1) j
1      format(’Line number ’, i3, ’.’)
      end
demo% f95 -openmp t13.f
demo% setenv PARALLEL 4
demo% a.out

Line number 9.
Line number 4.
Line number 5.
Line number 6.
Line number 1.
Line number 2.
Line number 3.
Line number 7.
Line number 8.

但递归的 I/O,即 I/O 语句包含对本身执行 I/O 的函数的调用,将会造成运行时错误。