Fortran 2008中的并行化“do concurrent”系统化,可能使用OpenMP。

10

Fortran 2008的do concurrent结构是一个循环,告诉编译器每次迭代之间互不影响,因此可以安全地进行并行化。

一个有效的例子:

program main
  implicit none
  integer :: i
  integer, dimension(10) :: array
  do concurrent( i= 1: 10)
    array(i) = i
  end do
end program main

迭代可以以任何顺序完成。您可以在此处了解更多信息。

据我所知,gfortran不会自动并行化这些do concurrent循环,但我记得有一封关于如何实现的gfortran-diffusion-list邮件(here)。它只是将它们转换为经典的do循环。

我的问题: 您是否知道一种系统性地并行化do concurrent循环的方法?例如使用系统性的openmp语法?


1
如果你还在使用它,请不要在workshare中使用FORALL。请参见下面更新的答案。 - Hristo Iliev
2个回答

14

要自动完成这个并不容易。 DO CONCURRENT 结构有一个 forall-header ,这意味着它可以接受多个循环、索引变量定义和掩码。基本上,您需要替换:

DO CONCURRENT([<type-spec> :: ]<forall-triplet-spec 1>, <forall-triplet-spec 2>, ...[, <scalar-mask-expression>])
  <block>
END DO
with:
[BLOCK
    <type-spec> :: <indexes>]

!$omp parallel do
DO <forall-triplet-spec 1>
  DO <forall-triplet-spec 2>
    ...
    [IF (<scalar-mask-expression>) THEN]
      <block>
    [END IF]
    ...
  END DO
END DO
!$omp end parallel do

[END BLOCK]

请注意,这不如使用 <iters 1>*<iters 2>*... 进行并行化一大循环有效,而这也是预期 DO CONCURRENT 要做的事情。 此外,请注意,forall-header 允许 type-spec,它允许您在标题中定义循环索引,并且您需要将整个内容包围在 BLOCK ... END BLOCK 结构中以保留语义。 您还需要检查 forall-header 的末尾是否存在 scalar-mask-expr,如果有,则还应在最内部的循环中放置该 IF ... END IF

如果您仅在 DO CONCURRENT 的正文中具有数组分配,您还可以将其转换为 FORALL 并使用 workshare OpenMP 指令。 这比以上方法要简单得多。

DO CONCURRENT <forall-header>
  <block>
END DO

将成为:

!$omp parallel workshare
FORALL <forall-header>
  <block>
END FORALL
!$omp end parallel workshare

考虑到上述情况,我唯一能想到的系统化方法就是系统地查看您的源代码,查找 DO CONCURRENT 并根据forall-header和循环体的内容系统地将其替换为上述转换结构之一。

编辑:目前不建议使用OpenMP workshare指令。事实证明至少Intel Fortran编译器和GCC会在编译过程中通过用OpenMP single指令包围FORALL语句和OpenMP workshare指令内的构造来进行串行化处理,这并不能提高速度。其他编译器可能会以不同的方式实现它,但如果需要达到可移植的性能,则最好避免使用它。


谢谢您的更新。您有关于这种不鼓励的行为的阅读来源吗? - max
使用GCC,您可以查看源代码(http://gcc.gnu.org/svn/gcc/branches/gcc-4_7-branch/gcc/fortran/)。一些结构是并行化的,例如数组赋值,但`FORALL`不在其中。使用其他编译器,您可以查看汇编输出。 - Hristo Iliev
我还应该补充一点,编译器供应商实际上正在解决你正试图解决的完全相同的问题 :) - Hristo Iliev

2

我不确定你所说的“系统并行化do并发循环”的意思。然而,如果只是想使用OpenMP简单地并行化普通的do循环,你可以尝试使用以下代码:

!$omp parallel private (i)
!$omp do
do i = 1,10
    array(i) = i
end do
!$omp end do
!$omp end parallel

您需要的是这个吗?


抱歉“系统化”表述不够清晰。举个例子,我能否在代码中使用_grep_或_awk_查找do concurrent;XX;end do,并将其替换为相同的openmp语法(例如_sed_或_awk_)。这不应该是特定于出现位置的(但循环变量当然是需要考虑的)。您的回答可能会有所帮助,但是否对于do concurrentend do之间所有类型的内容都适用于正确的语法呢? - max
据我所知,鉴于“do concurrent”结构中可以放置的内容受限制,这应该是足够的 - 希望更有经验的人能在这里发表意见。我对你尝试做的事情唯一担心的是,当使用“do concurrent”结构时,编译器将检查你在结构中所做的是否符合Fortran标准,但如果你使用sed/awk等工具,则不会发生这种情况。因此,如果出现错误,这种简单的转换可能不适合,并且可能导致意外结果,这可能很难追踪。 - Chris

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接