如何为假定形状数组使用可选属性

How to use optional attribute for assumed shape arrays

我在使用 Fortran 可选属性时遇到了以下情况 对于假定的形状数组并尝试找出最佳(在性能方面)解决方案。 如果有人能给我一个很好的提示,我会很高兴。 请注意,我对我能获得的每一个性能提升都很感兴趣,因为我的数组很大,循环的数量和它们的周期甚至更大。

我的情况是使用可选参数完成计算,或者如果它不存在,它会在其位置使用另一个数组。

  subroutine compute(tmat,av,av2)
  implicit none
  complex,intent(out)           :: tmat(:)   
  complex,intent(in)             :: av(:)      
  complex,intent(in),optional    :: av2(:)     

  if(present(av2)) then
      tmat = av *av2
  else
      tmat = av *av
  end if

  end subroutine compute_transition_matrices_bosonic

在这个简单的例子中,上面的解决方案就可以了。尽管如此,我的真实代码要复杂得多。 这意味着例程的内部块可能非常大(数百行)并且最重要的是包含许多嵌套循环。 可以想象如下:

  if(present(av2)) then
      tmat = "function"(av,av2)
  else
      tmat = "function"(av,av)
  end if

其中 "function" 代表很多操作和循环(因此是“”)。关键是对于这两种情况,这些都是相同的操作 if 语句,这样我需要写两次代码。另一种解决方案是检查 av2 是否出现在使用位置 会产生一些开销的代码,因为这种检查会非常(非常)频繁地进行。

我想知道是否有更聪明的方法来解决这样的问题。首先,人们可能会想到使用临时变量

  complex,                :: phi_tmp(:)

  if(present(av2)) then
      phi_tmp = av2
  else
      phi_tmp = av
  end if

  tmat = "function"(av,phi_tmp)

在使用可选参数时经常这样做。但这会复制数据,在我的例子中这是一个非常大的数组。 因此,我在考虑使用指针

  complex,intent(in),target             :: av(:)      
  complex,intent(in),optional,target    :: av2(:)   
  complex,pointer                :: phi_tmp(:)

  if(present(av2)) then
      phi_tmp => av2
  else
      phi_tmp => av
  end if

  tmat = "function"(av,phi_tmp)

但这需要 avav2TARGET 属性。在这里我不确定这是否会导致性能下降,因为编译器 不能再假设 avav2 在其优化过程中没有别名,即使这里都有 INTENT(IN) 属性 这样就不会出现混叠问题。此外,如果调用例程时输入中的参数确实是什么意思 没有 TARGET 属性? (这编译和工作!)

有没有人对这些问题有一些经验? 有"standard"解决方案吗? (我找不到一个) 最终什么是最好的解决方案?

一种方法是对存在和不存在的情况使用不同的子程序。

subroutine compute1(tmat,av)
  implicit none
  complex,intent(out)           :: tmat(:)   
  complex,intent(in)            :: av(:)      

  call compute2(tmat, av, av)  ! Ensure there is an explicit interface for compute2 here
end subroutine

subroutine compute2(tmat,av,av2)
  implicit none
  complex,intent(out)           :: tmat(:)   
  complex,intent(in)            :: av(:)      
  complex,intent(in)            :: av2(:)     

  tmat = "function"(av,av2)
end subroutine

两者都没有可选参数。

这可以通过使 compute 成为这两者的通用来添加。

当然,这不会在所有情况下都有效,但是这里 av 上的 intent(in) 属性和 compute2 上的 av2 我们不必担心别名。

我建议的是你最后一个用指针的解决方案,如果实参没有目标属性就没有问题,指针在例程退出时会变成未定义的,但只要你不传递它回到无关紧要的调用过程。

您可以使用其他子例程(例如内部子例程)来执行实际计算,该例程不会知道它的参数是别名的,基本上,如果它们都具有 intent(in)属性,这样就好了。所以不会有性能损失。

subroutine compute(tmat, av, av2)
  implicit none
  complex, intent(out)                  :: tmat(:)
  complex,intent(in),target             :: av(:)      
  complex,intent(in),optional,target    :: av2(:)   
  complex,pointer                :: phi_tmp(:)

  if(present(av2)) then
     phi_tmp => av2
  else
     phi_tmp => av
  end if

  call compute_internal(tmat, av, phi_tmp)

contains
  subroutine compute_internal(tmat, av, av2)
    complex,intent(out)       :: tmat(:)
    complex,intent(in)        :: av(:)      
    complex,intent(in)        :: av2(:)

    .....

  end subroutine
end subroutine