gpt4 book ai didi

function - fortran 运算符重载 : function or subroutine

转载 作者:行者123 更新时间:2023-12-04 23:48:36 26 4
gpt4 key购买 nike

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

非常感谢您对此的任何帮助或信息!

最佳答案

这里有两个问题:

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

  • 关于第一个问题要说的重要一点是,您最好自己测试一下:这有很多具体方面。

    但是,我很快敲响了一些可以指导你的东西。
    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

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

    但是,重要的是要注意正在发生的事情。

    对于函数,结果需要分配,对于子程序 div_sub1 intent(out)参数需要分配。 [分配函数结果会增加事情 - 见下文。]

    div_sub2分配被重新使用(“结果”参数是 intent(inout) )并且我们通过使用 q%x(:) 来抑制自动重新分配.后一部分很重要:编译器经常在检查是否需要调整大小时承受开销。后一部分可以通过更改 q 的意图来测试。在 div_sub1inout .

    [请注意,有一个假设 div_sub2尺寸不变的方法;你的文字似乎支持这一点。]

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

    来到第二个问题,为什么常用函数?你会注意到我已经研究了非常具体的案例:
    q = div_fun(f,g)
    call div_sub2(f,g,q) ! Could be much faster

    从问题文本和链接(以及您之前提出的问题)中,我假设您有一些使 / 过载的东西。运算符(operator)
    interface operator (/)
    module procedure div_fun
    end interface

    允许
    q = f/g               ! Could be slower, but looks good.
    call div_sub2(f,g,q)

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

    aren't the div_sub1 and div_sub2 binary operators just like the div_fun?



    答案是“不”,至少就 Fortran 定义的二元运算符而言(如上链接)。 [还有, div_fun它本身不是二元运算符,它是函数和构成运算的通用接口(interface)的组合。]

    使函数方法具有吸引力的是二元运算可以是表达式的一部分:
    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)

    使用子程序可能会有点困惑。上面的例子可以通过处理“就地”方面(或专业子程序)稍微整理一下,但这让我想到了最后一点。因为函数结果在以后使用(包括赋值)之前被完全评估,所以我们有类似的情况
    f = f/g  ! or f=div_fun(f,g)
    call div_sub2(f,g,f) ! Beware aliasing

    总结第二个问题:性能不是一切。

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

    关于function - fortran 运算符重载 : function or subroutine,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28241473/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com