Здесь есть два вопроса:
- Могу ли я повысить производительность в некоторых ситуациях, используя подход подпрограмм вместо функционального подхода?
- Почему, если производительность хуже, мне нужно использовать функцию?
По первому вопросу важно сказать, что вам лучше всего проверять что-то на себе: в этом есть много специфических аспектов.
Однако я быстро придумал кое-что, что может помочь вам.
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_sub1
на inout
.
[Обратите внимание, что для этого div_sub2
подхода предполагается, что размеры не меняются; кажется, это подтверждается вашим текстом.]
В заключение по первому вопросу: проверьте сами, но задайтесь вопросом, просто ли вы «скрываете» выделения, используя производные типы, а не удаляя их. И вы можете получить очень разные ответы, используя параметризованные производные типы.
Переходя ко второму вопросу, почему функции обычно используются? Вы заметите, что я рассмотрел очень конкретные случаи:
q = div_fun(f,g)
call div_sub2(f,g,q) ! Could be much faster
Из текста вопроса и ссылок (и предыдущих вопросов, которые вы задавали) я предполагаю, что у вас есть что-то, что перегружает оператор /
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) процедура должна быть функцией. В ответ на ваш комментарий к предыдущей редакции этого ответа
разве двоичные операторы div_sub1 и div_sub2 не похожи на div_fun?
ответ - «нет», по крайней мере, с точки зрения того, что Фортран определяет как бинарные операторы (см. ссылку выше). [Кроме того, div_fun
сам по себе не является бинарным оператором, это комбинация функции и универсального интерфейса, образующего операцию.]
Привлекательность функционального подхода заключается в том, что двоичная операция может быть частью выражения:
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
для обозначения / регулирования соответствия стандартам, то вы можете узнать, что люди думают об этом.]
person
francescalus
schedule
30.01.2015