Fortran 2008:如何返回函数返回值?

时间:2016-06-21 14:22:13

标签: arrays fortran return-value return-by-reference return-by-value

现代Fortran中是否有可能从一个函数返回一个数组,其性能等同于将一个子例程填充为一个作为参数传递的数组?

考虑例如举个简单的例子

PROGRAM PRETURN 

  INTEGER :: C(5)
  C = FUNC()
  WRITE(*,*) C
  CALL SUB(C)
  WRITE(*,*) C

CONTAINS 

  FUNCTION FUNC() RESULT(X)
    INTEGER :: X(5)
    X = [1,2,3,4,5]
  END FUNCTION FUNC

  SUBROUTINE SUB(X)
    INTEGER :: X(5)
    X = [1,2,3,4,5]
  END SUBROUTINE SUB

END PROGRAM PRETURN

在从堆栈中丢弃返回的数组之前,行C = FUNC()将复制函数返回值中的值。子例程版本CALL SUB(C)将直接填充C,避免额外的应对步骤和与临时数组关联的内存使用情况 - 但在SUM(FUNC())这样的表达式中使用是不可能的。

但是,如果编译器实现选择在堆上分配所有数组,则只需更改C的基础指针即可分配返回值,从而在两个版本之间产生相同的性能。*

这些优化是由通用编译器完成的,还是有其他方法来获取函数语义而没有性能开销?

*使用可分配数组会更加明显,但这会遇到编译器支持问题。默认情况下,英特尔fortran在分配不同大小的数组时不会(重新)分配数组,但通过使用ALLOCATE(C, SOURCE=FUNC())语句可以产生相同的效果。 Gfortran同时执行赋值时的自动分配,但是有一个错误阻止ALLOCATE语句,其中形状是从SOURCE参数派生的,而修复程序尚未包含在二进制版本中。

2 个答案:

答案 0 :(得分:8)

Fortran标准没有提及实现该语言中几乎所有内容的实际机制。该语言的语义是在赋值开始之前完全评估函数结果。如果一个人将目标作为输出传递,那么如果函数由于某种原因没有完成,那么变量可能会被部分修改。编译器可能能够进行足够的重叠分析以进行一些优化。我很确定英特尔Fortran没有这样做 - 语义限制很重要。

您的示例是一个玩具程序 - 更有趣的问题是,如果有生产应用程序,这样的优化将适用并且值得。

我将评论英特尔Fortran将更改其分配给可分配数组的默认行为,以便从版本17开始,按照标准的规定进行自动重新分配。

答案 1 :(得分:0)

我有时也有相同的。当我停下来思考它的时候,我意识到功能是好的,而且子程序在Fortran方面也很好。

想象一下,能力就在那里,我们有以下功能:

function doThings(param) results(thing)
    integer :: thing
    integer, intent(in out) :: param
    ! Local variables
    integer :: genialUpdatedValue, onOfThePreviousResult
    ! some other declarations
    ! serious computation to do things
    ! and compute genialUpdatedValue and onOfThePreviousResult
    param = genialUpdatedValue
    thing = onOfThePreviousResult
end function doThings

我们有以下电话:

! some variables first
integer, parameter :: N_THINGS = 50 ! just love 50
integer :: myThing, myParam
integer, dimension(N_THINGS) :: moreThings
!
! Reading initial param from somewhere
! myParam now has a value
!
myThing = doThings(myParam)

这绝对没问题,以下是

!
! Reading initial param from somewhere
! myParam now has a value
!
moreThing = doThings(myParam)

结果是什么?应该是

integer :: i
do i = 1, N_THINGS
    moreThings(i) = doThings(myParam)
end do

或者应该是这个

integer :: i, thing
thing = doThings(myParam)
do i = 1, N_THINGS
    moreThings(i) = thing
end do

请记住,myParam会被函数更改。有人可以说这是一个简单的例子,但是结果不是整数的图像,而是具有大数组成员的用户定义类型。

如果你考虑一下,你一定会发现一些像这样的问题。当然,可以在这里添加更多限制以允许该功能,并且最终,当我们有足够的需求时,它将以必要的限制来实现。我希望有所帮助。