Fortran do concurrent语句备忘

本文用于记录一些我了解到的关于Fortran do concurrent语句的信息。本文不是教程,是笔记,而且是可能存在错误、过时、或关于特定编译器的信息。

do concurrent语句是do循环的一个扩展,具体的语法和限制看Modern Fortran ExplainedIntel Fortran Compiler 19.0 Developer Guide and Reference即可。

do concurrent的用途是为了帮助循环的并行计算,它要求各循环之间相互独立。但是并不是使用了do concurrent,其中的循环就会并行执行,实际上仍然需要开启编译器的并行选项才行。根据Intel Fortran Compiler 19.0 Developer Guide and Reference和一些讨论Advantage DO CONCURRENT against DOQuestions about DO CONCURRENT的信息,对于Intel Fortran编译器,当开启了-parallel(Linux和macOS)或/Qparallel(Windows)选项,编译器会尝试让语句内的代码并行化。

对于我目前使用的gfortran编译器,我找到的信息GNU Fortran的邮件列表 中的结论是:do concurrent可以帮助编译器额外优化,但是使用OpenMP并行计算时,最好还是使用!$OMP PARALLEL DOdo语句。一个事实是do concurrent!$OMP PARALLEL DO结构冲突,它无法和do循环一样显式地并行计算。我对此做了一个测试:

program main
  use iso_fortran_env, only: r8 => real64
  use m_timer
  implicit none
  type(timer) :: clock
  integer, parameter :: n = 500
  real(kind=r8), dimension(n, n) :: x
  integer :: i, j

  call clock%start()
  !$OMP PARALLEL DO
  do i = 1, n
    do j = 1, n
      x(i, j) = sqrt( real(i, kind=r8) ) * log( real(i, kind=r8) )
    end do
  end do
  !$OMP END PARALLEL DO
  call clock%stop()

end program
program main
  use iso_fortran_env, only: r8 => real64
  use m_timer
  implicit none
  type(timer) :: clock
  integer, parameter :: n = 500
  real(kind=r8), dimension(n, n) :: x
  integer :: i, j
  
  call clock%start()
  do concurrent (i=1:n, j=1:n)
    x(i, j) = sqrt( real(i, kind=r8) ) * log( real(i, kind=r8) )
  end do
  call clock%stop()

end program

将两个代码分别在开启了-fopenmp和未开启的情况下进行运行。结果如下:

do 0.012 s
do concurrent 0.013 s
do with OpenMP 0.004 s
do concurrent with OpenMP 0.011 s

根据这个结果,以及另一个不并行的条件循环的例子来看,目前do concurrent在执行速度上与do其实几乎没有差异。

结论:我认为do concurrent语句相对而言比do更为「优雅」,主要是对于多重循环以及需要逻辑判断的情况。而且对于编译器来说,原则上更有利于其进行优化(虽然尝试了两个例子,发现速度差不多)。但是如果真的要进行并行计算,do concurrent似乎并不是更加推荐的选择(因为它好像真的不能出现在OpenMP的结构内),实践上还是用大部分人使用的!$OMP PARALLEL DOdo语句。

发表评论

电子邮件地址不会被公开。 必填项已用*标注