fortran运算符重载:函数或子例程

Cha*_*les 4 fortran function operator-overloading gfortran subroutine

我最近将我的.f90代码更新为.f03,我期待看到加速,因为我的旧版本涉及许多分配和解除分配(7个3D阵列 - 45x45x45)在do循环内的每次迭代(总共4000个).使用派生类型,我在模拟开始时分配这些数组,并在结束时释放它们.我以为我会看到加速,但它实际上运行得慢得多(30分钟而不是23分钟).

我运行了一个分析器,看起来加/减/乘/除运算符需要相对较长的时间.除了标准变化的变化之外,就我所知,运营商是唯一的区别.我想知道这是否是因为函数在每次操作期间都返回了字段数量的新副本.

所以这是我的问题:如果我将函数更改为子例程以便这些字段通过引用传递(我认为?),它可能运行得更快吗?此外,如果这更快,更优选,那么为什么所有这些示例都显示运算符重载的函数而不是使用子例程?我觉得我错过了什么.

具有运算符重载的函数的引用:

http://www.mathcs.emory.edu/~cheung/Courses/561/Syllabus/6-Fortran/operators.html

http://research.physics.illinois.edu/ElectronicStructure/498-s97/comp_info/overload.html

https://web.stanford.edu/class/me200c/tutorial_90/13_extra.html

https://www.ibm.com/developerworks/community/blogs/b10932b4-0edd-4e61-89f2-6e478ccba9aa/entry/object_oriented_fortran_does_fortran_support_operator_overloading55?lang=en

这是我的一个运营商的例子:

    function vectorVectorDivide(f,g) result(q)
      implicit none
      type(vectorField),intent(in) :: f,g
      type(vectorField) :: q
      q%x = f%x / g%x; q%y = f%y / g%y; q%z = f%z / g%z
      q%sx = f%sx; q%sy = f%sy; q%sz = f%sz
    end function
Run Code Online (Sandbox Code Playgroud)

任何帮助或信息都非常感谢!

fra*_*lus 7

这里有两个问题:

  1. 在某些情况下,使用子程序方法比函数方法可以获得更好的性能吗?
  2. 为什么,如果性能更差,我想使用一个函数吗?

关于第一个问题,一个重要的事情是,你可能最好自己测试一些东西:这里有很多具体的方面.

但是,我很快就敲了一些可以指导你的事情.

module test

  implicit none

  type t1
     real, allocatable :: x(:)
  end type t1

contains

  function div_fun(f,g) result(q)
    type(t1), intent(in) :: f, g
    type(t1) q
    q%x = f%x/g%x
  end function div_fun

  subroutine div_sub1(f, g, q)
    type(t1), intent(in) :: f, g
    type(t1), intent(out) :: q
    q%x = f%x/g%x
  end subroutine div_sub1

  subroutine div_sub2(f, g, q)
    type(t1), intent(in) :: f, g
    type(t1), intent(inout) :: q
    q%x(:) = f%x/g%x
  end subroutine div_sub2

end module test
Run Code Online (Sandbox Code Playgroud)

有了这个,我观察到有时在使用函数和子例程之间没有显着差异,有时会有.也就是说,它取决于编译器,标志等.

但是,重要的是要注意发生了什么.

对于函数,结果需要分配,对于子例程div_sub1,intent(out)参数需要分配.[分配功能结果会增加内容 - 请参阅后面的内容.]

div_sub2重新使用分配("结果"参数是intent(inout)),我们通过使用抑制自动重新分配q%x(:).后一部分很重要:编译器经常会遇到检查是否需要调整大小的开销.这后一部分可以通过改变的意图进行测试qdiv_sub1inout.

[注意,这种div_sub2方法的假设是尺寸不变; 这似乎得到你的文字支持.]

总结第一个问题:检查自己,但想知道你是否只是通过使用派生类型"隐藏"分配而不是删除它们.使用参数化派生类型可能会得到非常不同的答案.

谈到第二个问题,为什么常用函数?你会注意到我已经看过非常具体的案例:

q = div_fun(f,g)
call div_sub2(f,g,q)  ! Could be much faster
Run Code Online (Sandbox Code Playgroud)

从问题文本和链接(以及之前提出的问题)我会假设你有一些超载/运算符的东西

interface operator (/)
  module procedure div_fun
end interface
Run Code Online (Sandbox Code Playgroud)

允许

q = f/g               ! Could be slower, but looks good.
call div_sub2(f,g,q)
Run Code Online (Sandbox Code Playgroud)

我们注意到要用作二元运算符(参见Fortran 2008 7.1.5,7.1.6),该过程必须是一个函数.回应您对此答案的先前修订版的评论

不像div_fun那样div_sub1和div_sub2二元运算符?

答案是"不",至少就Fortran定义为二元运算符而言(链接如上).[此外,div_fun它本身不是二元运算符,它是函数和形成操作的通用接口的组合.

使函数方法具有吸引力的是二元运算可以是表达式的一部分:

q = q + alpha*(f/g)                ! Very neat
call div_sub2(f,g,temp1)
call mult_sub(alpha, temp1, temp2)
call add_sub(q, temp2, temp3)
call assign_sub(q, temp3)
Run Code Online (Sandbox Code Playgroud)

使用子程序可能会有点乱.上面的例子可以通过处理"就地"方面(或专家子程序)来略微整理,但这让我想到了最后一点.因为函数结果在之后的使用(包括赋值)之前完全被评估,所以我们有类似的情况

f = f/g  ! or f=div_fun(f,g)
call div_sub2(f,g,f) ! Beware aliasing
Run Code Online (Sandbox Code Playgroud)

总结第二个问题:表现并非一切.

[最后,如果你的意思是你正在使用.f90.f03提交后缀来表示/管理标准合规性,那么你可能想看看人们对此的看法.]