在循环内调用时会降低并行子例程的速度

时间:2019-06-25 07:49:05

标签: fortran openmp

我已经并行化了一个子例程。它具有非常好的基准:四核上的速度提高4倍。我有两个不同的来源:serial.f和paral.f。比较是从终端运行它们并打印经过的挂钟时间。在每个源代码中,只有对关联子例程的调用。但是,当我像这样修改源代码时:

serial.f :

do i=1,100
    call serial
end do 

并且这样

 paral.f :

 do i=1,100
      call paral
 end do

性能下降到0.96 X速度:并行版本比串行版本差!可以在why calling many N times a serial subroutine is faster than calling N times the parallel version of the same subroutin中找到该代码 为了获得serial.f,只需注释包含调用paral的块。为了获得paral.f,只需注释包含调用序列的块。 我问:这是一个普遍的问题吗?我如何解决它以维持循环调用4倍加速? 请注意 : (1)我尝试过转换为C,时间,基准和问题仍然相同 (2)我尝试过翻译成现代的fortran,时间,基准和问题仍然相同 (3)我尝试了各种技巧并重写了代码。我确定问题不是子程序如何并行化(我实现了4 X),而是在循环内多次调用了该子程序。 谢谢。

编辑:: 根据要求,我发布了一个用现代的fortran编写的程序,该程序存在相同的问题:

program main


use omp_lib

implicit none

integer ( kind = 4 ), parameter :: m = 5000
integer ( kind = 4 ), parameter :: n = 5000
integer ( kind = 4 ) i
integer ( kind = 4 ) j
integer ( kind = 4 ) nn
real ( kind = 8 ) u(m,n)
real ( kind = 8 ) w(m,n)
real ( kind = 8 ) wtime,h

call random_seed()


do j=1,n
  do i=1,m
    call random_number(u(i,j))
  end do
end do


wtime = omp_get_wtime ( )

do nn=1,100

!$omp parallel do default(none) shared(u, w) private(i,j)

do j = 2, n - 1
  do i = 2, m - 1
    w(i,j) = 0.25D+00 * ( u(i-1,j) + u(i+1,j) + u(i,j-1) + u(i,j+1) )
  end do
end do

!$omp end parallel do

end do

wtime = omp_get_wtime ( ) - wtime



h=0.0D+00
do j=1,n
  do i=1,m
    h=h+w(i,j)
  end do
end do



write ( *, '(a,g14.6)' ) '  Wall clock time serial= ', wtime
write ( *, '(a,g14.6)' ) '  h ', h

stop
end

为了获得serial_with_loop.f90,只需注释openmp指令和nn循环。您还必须使用类似的方法parall_with_loop.f90以及无循环的serial和parall获得。您可以使用“ gfortran -o name.out -fopenmp -O3 name.f90”进行编译,然后从终端启动,并将输出重定向到文本文件“ name.out> time_result.txt”

1 个答案:

答案 0 :(得分:0)

您遇到的问题是,您正在并行化位于nn上的循环内的j上的循环。因此,对于每个nn值,您的计算机都需要时间来创建线程池,这些线程可以为不同的j值执行任务。因此,这一次(创建池所需)是连续的,无法通过使用的线程数来区分。正如我看到的代码所示,没有理由不能并行化nn循环并只能创建一次该池,而不是nn次。我认为,如果您编写

,您的代码会更好地工作
 wtime = omp_get_wtime ( )
 !$omp parallel do default(none) shared(u, w) private(nn,i,j)
 do nn=1,100
   do j = 2, n - 1
     do i = 2, m - 1
       w(i,j) = 0.25D+00 * ( u(i-1,j) + u(i+1,j) + u(i,j-1) + u(i,j+1))
     end do
   end do
 end do
 !$omp end parallel do
 wtime = omp_get_wtime ( ) - wtime

希望对您有所帮助。