将掩码数组添加到另一个fortran的最有效方法

mgi*_*son 5 performance fortran

我有一个"蒙面数组",我想添加到另一个数组 - 换句话说,我有3个数组A,Bmask.我的问题是什么是最有效的(在执行时间方面)存储掩码的方式(作为逻辑数组,作为1和0的真实数组)?

编辑

这是一个你可以玩的玩具程序(如果你有mpif77):

  program main
  implicit None
  include 'mpif.h'
  integer, parameter :: ntry=10000
  integer, parameter :: asize=1000000
  real,dimension(asize) :: A,B,maskr
  logical,dimension(asize) :: mask
  real*8 :: dd,dt,dtave,dtbest
  integer i

  do i=1,asize
     maskr(i)=mod(i,2)
     mask(i)=.False.
     if(mod(i,2).eq.0) mask(i)=.True.
  enddo

  A=1.0; B=1.0
  dtbest=1d33
  dtave=0.0
  do i=1,ntry
     dt=mpi_wtime()
     call add_arrays_logical(asize,A,B,mask)
     dt=mpi_wtime()-dt
     dtbest=min(dt,dtbest)
     dtave=dtave+dt
  enddo
  print*,"==== logical ==="
  print*,"Average",dtave/ntry
  print*,"Best",dtbest

  A=1.0; B=1.0
  dtbest=1d33
  dtave=0.0
  do i=1,ntry
     dt=mpi_wtime()
     call add_arrays_real(asize,A,B,maskr)
     dt=mpi_wtime()-dt
     dtbest=min(dt,dtbest)
     dtave=dtave+dt
  enddo
  print*,"==== Real ==="
  print*,"Average",dtave/ntry
  print*,"Best",dtbest

  A=1.0; B=1.0
  dtbest=1d33
  dtave=0.0
  do i=1,ntry
     dt=mpi_wtime()
     where(mask) A=A+B
     dt=mpi_wtime()-dt
     dtbest=min(dt,dtbest)
     dtave=dtave+dt
  enddo
  print*,"==== Where ===="
  print*,"Average",dtave/ntry
  print*,"Best",dtbest

  end

  subroutine add_arrays_logical(n,A,B,mask)
  integer n
  real A(n),B(n)
  logical mask(n)
  do i=1,n
     if(mask(i))then
        A(i)=A(i)+B(i)
     endif
  enddo
  end

  subroutine add_arrays_real(n,A,B,mask)
  integer n
  real A(n),B(n),mask(n)
  do i=1,n
     A(i)=A(i)+mask(i)*B(i)
  enddo

  end
Run Code Online (Sandbox Code Playgroud)

我的结果:

(gfortran -O2)

==== logical ===
Average  1.52590200901031483E-003
Best  1.48987770080566406E-003
==== Real ===
Average  1.78022863864898680E-003
Best  1.74498558044433594E-003
==== Where ====
Average  1.48216445446014400E-003
Best  1.44505500793457031E-003
Run Code Online (Sandbox Code Playgroud)

(gfortran -O3 -funroll-loops -ffast-math)

==== logical ===
Average  1.47997992038726811E-003
Best  1.44982337951660156E-003
==== Real ===
Average  1.40655457973480223E-003
Best  1.37186050415039063E-003
==== Where ====
Average  1.48403010368347165E-003
Best  1.45006179809570313E-003
Run Code Online (Sandbox Code Playgroud)

(pfg90 -fast) - 在一台很旧的机器上

==== logical ===
Average   5.4871437072753909E-003
Best   5.4519176483154297E-003
==== Real ===
Average   4.6096980571746831E-003
Best   4.5847892761230469E-003
==== Where ====
Average   5.3572671413421634E-003
Best   5.3288936614990234E-003
Run Code Online (Sandbox Code Playgroud)

(pfg90 -O2) - 在一台很旧的机器上

 ==== logical ===
 Average   5.4929971456527714E-003
 Best   5.4569244384765625E-003
 ==== Real ===
 Average   5.5974062204360965E-003
 Best   5.5701732635498047E-003
 ==== Where ====
 Average   5.3811835527420044E-003
 Best   5.3341388702392578E-003
Run Code Online (Sandbox Code Playgroud)

当然,有一些事情可以影响这一点 - 例如编译器对循环进行矢量化的能力 - 那么是否有关于应该如何实现这样的事情的经验法则?

M. *_* B. 5

为什么不用"哪里"?

where (mask) A = A + B
Run Code Online (Sandbox Code Playgroud)

可能使用面罩是最快的,但唯一可以确定的方法是测量.


jan*_*neb 3

如果你所说的触发器指的是浮点运算,那么第一个选项显然更好,因为在这种情况下,每个循环迭代有 1 次触发器,其中 mask(n) == .true。。而对于第二个选项,无论 mask(n) 的值如何,每个循环迭代都有 2 次触发器。

OTOH,如果您有兴趣最大限度地减少执行此函数所花费的时间,为什么不在数据上尝试这两个版本并测试哪个版本更快?

您可能还想测试使用 Fortran 90+ WHERE 构造的版本

where(mask) A = A + B
Run Code Online (Sandbox Code Playgroud)