Какова наилучшая практика, когда функция Fortran возвращает массив?

Скажем, я хочу написать функцию, которая принимает в качестве входных данных массив x одного измерения и возвращает на его основе другой массив y того же измерения (чтобы проиллюстрировать это, я с помощью функции, которая умножает его на 2). У меня есть два варианта этого кода:

function times2(x) result(y)
    real, intent(in) :: x(:)
    real, allocatable :: y(:)

    allocate(y(size(x))
    y = 2*x
end function

or

function times2(x,n) result(y)
    real, intent(in) :: x(n)
    integer, intent(in) :: n
    real  :: y(n)

    y = 2*x
end function

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


person Manuel Pena    schedule 22.02.2019    source источник
comment
Большинство людей, которых я знаю, использовали бы подпрограмму, а не функцию. Функции, которые изменяют свои аргументы, просто зло - они вызывают путаницу, и, насколько я понимаю, не всегда точно определено, что именно они делают при использовании в более сложных выражениях. Поэтому, если вы должны использовать функцию, используйте первый способ, но я думаю, что большинство людей скажут, что подпрограмма - это путь.   -  person Ian Bush    schedule 22.02.2019
comment
Почему нужно быть проще для звонящего?   -  person francescalus    schedule 22.02.2019
comment
Я согласен с комментарием @IanBush до определенного момента. Дело в том, Зачем определять функцию для умножения массива на 2?, если эта возможность встроена в язык.   -  person High Performance Mark    schedule 22.02.2019
comment
умножение массива на два было просто для того, чтобы сделать код примера короче. Фактический вопрос, который у меня есть, скажем, у меня есть подпрограмма, которая берет массив неизвестного измерения, делает кучу вещей и генерирует другой массив того же размера, как мне это сделать?. (Я использую функцию, а не подпрограмму, потому что целью моей подпрограммы является создание другого массива на основе первого, а не изменение первого)   -  person Manuel Pena    schedule 22.02.2019
comment
Пропущенное время редактирования - в комментарии выше, конечно, немного об изменении аргументов здесь не имеет значения, я неправильно прочитал код.   -  person Ian Bush    schedule 22.02.2019
comment
На самом деле у меня есть вопрос.... Обычно лучше задать вопрос, на который вы хотите получить ответ.   -  person High Performance Mark    schedule 22.02.2019
comment
Под капотом результаты функции обычно реализуются путем создания подпрограммы со скрытым аргументом. Тем не менее, вы можете получить временное значение в стеке, если используете функцию внутри сложного выражения. Но это может произойти в любое время, когда вы используете сложные выражения с массивами.   -  person Vladimir F    schedule 22.02.2019
comment
Я не думаю, что преобразование его в подпрограмму - лучшая практика @roygvib. У меня есть несколько причин сделать это как функцию. I функция может быть вызвана на месте. sqrt — это функция, это означает, что вы можете вычислять такие выражения, как sqrt(1/(1 + sqrt(x)). Представьте, что sqrtбыла подпрограммой...   -  person Manuel Pena    schedule 22.02.2019
comment
@VladimirF Я так понимаю, вы говорите, что с точки зрения памяти особой разницы нет, верно?   -  person Manuel Pena    schedule 22.02.2019
comment
@francescalus Я думаю, что первый вариант проще для вызывающего, потому что ему не нужно следить за тем, чтобы x и n были согласованы, вызывающий передает x и внутри функции все согласовано.   -  person Manuel Pena    schedule 22.02.2019
comment
Я думаю, что я бы использовал автоматически возвращаемый массив для таких функций, как cross(u, v) (для перекрестного произведения двух векторов, возвращающих другой вектор), а для таких функций, как matmul(A, B), если A или B может быть довольно большим (поэтому они могут не поместиться в стеке).   -  person roygvib    schedule 22.02.2019
comment
поэтому вы бы написали cross(u,v,n) ? (извините, если я вас не понял @roygvib)   -  person Manuel Pena    schedule 22.02.2019
comment
Но вы можете переписать второй так, чтобы y(size(x)) оставался с x(:) (что, я признаю, я думал, что вы сделали: я, вероятно, пропустил фактический код, основываясь на самой минимальной разнице между автоматическим и отложенным).   -  person francescalus    schedule 22.02.2019
comment
спасибо @francescalus, я не знал, что могу использовать функцию при объявлении формы автоматического массива   -  person Manuel Pena    schedule 22.02.2019
comment
@ManuelPena Я бы, вероятно, придерживался массивов предполагаемой формы (т. Е. Cross (u, v), а не cross (u, v, n)), потому что можно передавать несмежные массивы с меньшими накладными расходами (по сравнению с копированием может произойти для манекен явной формы). Но могут быть случаи, когда эксплицитная форма лучше (сейчас я не могу вспомнить такой пример, извините...)   -  person roygvib    schedule 22.02.2019
comment
Я думаю, что один исключительный (и важный) случай, когда фиктивные массивы явной формы полезны, - это когда кто-то хочет использовать эту процедуру из C (или других языков).   -  person roygvib    schedule 24.02.2019


Ответы (1)


Вероятно, ни то, ни другое, хотя, как обычно в таких случаях, ответ зависит от конкретных обстоятельств.

Предполагая неэлементарную операцию, я бы написал такую ​​функцию (в модуле) как:

function times2(x) result(y)
  real, intent(in) :: x(:)
  real :: y(size(x))

  y = 2*x
end function

Вышеупомянутое имеет фиктивный аргумент предполагаемой формы с автоматическим результатом функции. Это:

  • доступен при записи на стандарт Fortran 95;

  • явно указывает в исходном коде зависимость размера результата функции от аргументов функции, что может (или не может) помочь читателям вашего кода понять, что происходит (одним из таких читателей является сам компилятор, который может помочь ему с оптимизацией );

  • может (или не может) избегать промежуточных копий значения массива;

  • может (или не может) требовать места для результата функции или временного эквивалента в стеке.

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

elemental function times2(x) result(y)
  real, intent(in) :: x
  real :: y

  y = 2*x
end function

Обычно я использую результаты выделяемой функции с отложенной формой, когда форма (или какой-либо другой атрибут) результата функции не может быть описана простым выражением спецификации. Распределяемые результаты функции:

  • требует написания по крайней мере стандарта Fortran 2003;

  • может потребоваться дополнительная пара выделения/освобождения кучи сверх того, что строго необходимо, что может (или не может) повлиять на производительность;

  • может не требовать того же использования стека, что и случай с автоматическим результатом, что может (или не может) избежать проблем с переполнением стека во время выполнения.

Детали реализации компилятора (включая параметры компилятора) влияют на сравнение. В частности, различия в том, как компилятор управляет памятью для временных объектов, могут привести к сближению этих двух подходов с точки зрения их требований к выделению памяти в стеке и куче.

Избегайте явных фиктивных аргументов массива форм, если у вас нет особых требований.

person IanH    schedule 22.02.2019
comment
Спасибо, я не знал, что вы можете указать форму вывода как размер (x), но зная, что я также буду использовать это. Я говорил о неэлементарном, все равно спасибо. - person Manuel Pena; 25.02.2019