代码之家  ›  专栏  ›  技术社区  ›  Enlico

Fortran-函数与子程序性能

  •  6
  • Enlico  · 技术社区  · 6 年前

    大约几年前,我对Fortran还是个新手,所以我过度使用了 SUBROUTINE 没有参数的,以及共享数据,因此这些过程对实际参数进行了计算,可通过 USE 声明。现在,我需要重用其中的一些过程(想想计算卷中的散度 大的 DIMENSION(:,:,:) 数组,从该卷中的向量场,3 大的 维度(:,:,:) 数组以派生类型粘合在一起),我想

    • 保留它们 子例程 s但删除 使用 声明和使用 IN / OUT / INOUT 伪参数(简单),或
    • 将其转换为 FUNCTION s(因为我要学习一点,所以要努力一点)

    我想这两种方法的性能可能会有所不同,我很想理解这一点。在接下来的MWE中,我编写了3个过程来进行相同的计算,但我不知道应该如何选择其中一个或其他;我也不知道其他方法是否更可取。

    需要注意的是,我的程序中的所有秩3实际数组 ALLOCATABLE 必须如此。

    PROGRAM mymod
    
        IMPLICIT NONE
    
        TYPE blk3d
            REAL,    DIMENSION(:,:,:), ALLOCATABLE :: values
        END TYPE blk3d
        TYPE(blk3d) :: A, B
    
        INTEGER, PARAMETER :: n = 2
        INTEGER :: i
    
        ALLOCATE(A%values(n,n,n))
        A%values = RESHAPE([(i/2.0, i = 1, PRODUCT(SHAPE(A%values)))], SHAPE(A%values))
        print *, A%values
    
        ! 1st way
        B = myfun(A)
        print *, B%values
    
        DEALLOCATE(B%values)
    
        ! 2nd way
        ALLOCATE(B%values(n,n,n))
        CALL mysub(A, B)
        print *, B%values
    
        DEALLOCATE(B%values)
    
        ! 3rd way
        ALLOCATE(B%values(n,n,n))
        CALL mysub2(A, B%values)
        print *, B%values
    
    CONTAINS
    
      FUNCTION myfun(Adummy) RESULT(Bdummy)                                                                                                              
        IMPLICIT NONE
    
        TYPE(blk3d), INTENT(IN) :: Adummy
        TYPE(blk3d)             :: Bdummy
    
        ALLOCATE(Bdummy%values, mold = Adummy%values)
        Bdummy%values(:,:,:) = 2*Adummy%values
    
      END FUNCTION myfun
    
      SUBROUTINE mysub(Adummy, Bdummy)
    
        IMPLICIT NONE
    
        TYPE(blk3d), INTENT(IN)    :: Adummy
        TYPE(blk3d), INTENT(INOUT) :: Bdummy
    
        Bdummy%values(:,:,:) = 2*Adummy%values
    
      END SUBROUTINE mysub
    
      SUBROUTINE mysub2(Adummy, Bdummy)
    
        IMPLICIT NONE
    
        TYPE(blk3d),            INTENT(IN)  :: Adummy
        REAL, DIMENSION(:,:,:), INTENT(OUT) :: Bdummy
    
        Bdummy(:,:,:) = 2*Adummy%values
    
      END SUBROUTINE mysub2
    
    END PROGRAM mymod
    

    编辑 在我的CFD程序中,我使用了几个排名3的大型阵列。这些数组在计算中相互作用(不仅仅是逐点的 + / - / * ,…)对其中一些对象执行,以获取其他对象。想想 B 其计算依据 A 通过示例中的4个过程之一,然后用于升级 A. 自身由 A = A + B 。我是否错误地认为上述4个选项可以完成相同的任务?从这个意义上说,我可以打电话 A = A + myfun(A) 如果我选择函数方法。

    编辑2 实际的派生类型以及大的秩3数组有半打其他字段(标量和小的静态数组);此外,这种类型的大多数变量都是数组, e、 g。 TYPE(blk3d), DIMENSION(n) :: A

    1 回复  |  直到 6 年前
        1
  •  5
  •   Steve Lionel    6 年前

    子例程或函数之间的选择通常应基于您将如何使用结果以及它对读者的清晰程度。

    您需要关心的是数据被不必要地复制了多少次。对于大型阵列,您可能希望减少这种情况。

    忽略过程中的实际工作,myfun再次复制数据,并(可能)进行两次分配。首先,分配函数结果变量,并将数据复制到其中。然后回到调用者中,如果B%值与结果的形状不同,并且再次复制数据,则会重新分配B%值,然后释放函数结果。

    mysub和mysub2没有这个额外的分配/复制,它们几乎是等价的,尽管调用mysub2可能需要在堆栈上设置描述符。如果子例程执行任何实际工作,我希望这是噪声。

    在mysub和mysub2之间进行选择实际上取决于它在实际应用程序中的清晰度。只有一个组件的派生类型似乎不现实,除非您希望拥有这些组件的数组。