Fortran 2008: Как возвращаются возвращаемые значения функции?

Возможно ли в современном Фортране вернуть массив из функции с производительностью, эквивалентной подпрограмме, заполняющей массив, переданный в качестве аргумента?

Рассмотрим, например, как простой пример

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, что приводит к эквивалентной производительности между двумя версиями.*

Проводятся ли такие оптимизации обычными компиляторами или есть какой-то другой способ получить семантику функций без снижения производительности?


* Это было бы более очевидно с распределенными массивами, но это столкнулось бы с проблемами поддержки компилятора. Intel fortran по умолчанию не перераспределяет массивы при назначении массива другого размера, но позволяет тот же эффект, используя ALLOCATE(C, SOURCE=FUNC()) заявление. Gfortran тем временем выполняет автоматическое распределение при назначении, но имеет ошибку, которая предотвращает ALLOCATE заявления, где форма получается из SOURCE аргумент и исправление еще не были включены в бинарные выпуски.

2 ответа

Решение

В стандарте Фортрана ничего не говорится о реальном механизме реализации чего-либо на языке. Семантика языка состоит в том, что результат функции полностью оценивается перед началом присваивания. Если кто-то передал пункт назначения в качестве вывода, то если функция по какой-то причине не завершилась, переменная может быть частично изменена. Компилятор может выполнить достаточный анализ перекрытий, чтобы несколько оптимизировать это. Я вполне уверен, что Intel Fortran не делает этого - семантические ограничения значительны.

Ваш пример - игрушечная программа - более интересный вопрос, есть ли производственные приложения, где такая оптимизация была бы применима и целесообразна.

Я прокомментирую, что Intel Fortran изменит свое поведение по умолчанию для назначений для выделяемых массивов, так что, начиная с версии 17, автоматическое перераспределение будет происходить, как указано в стандарте.

У меня иногда бывает то же самое. Когда я останавливаюсь и думаю об этом на мгновение, я понимаю, что функции хороши такими, какие они есть, и подпрограммы хороши такими же, как и в случае с 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 изменяется функцией. Можно утверждать, что это простой случай, но представьте, что результатом был не целое число, а пользовательский тип с большими элементами массива.

Если вы думаете об этом, вы обязательно найдете некоторые проблемы, подобные этим. Конечно, здесь и там можно добавить больше ограничений, чтобы разрешить эту функцию, и в конце концов, когда у нас будет достаточно спроса, она будет реализована с необходимыми ограничениями. Надеюсь, это поможет.

Другие вопросы по тегам