В чем явная разница между намерениями Фортрана (in, out, inout)?

StackOverflow https://stackoverflow.com/questions/1011604

  •  06-07-2019
  •  | 
  •  

Вопрос

После некоторого поиска в книгах, здесь, на stackoverflow и в общей сети, я обнаружил, что трудно найти прямое объяснение реальным различиям между намерениями аргументов Фортрана.Насколько я это понял, это так:

  • intent(in) -- Фактический аргумент копируется в фиктивный аргумент при вводе.
  • intent(out) -- Фиктивный аргумент указывает на реальный аргумент (оба они указывают на одно и то же место в памяти).
  • intent(inout) -- фиктивный аргумент создается локально, а затем копируется в фактический аргумент после завершения процедуры.

Если я правильно понимаю, то я также хочу знать, почему кто-то хочет использовать intent(out), поскольку intent(inout) требует меньше работы (нет копирования данных).

Это было полезно?

Решение

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

Это значит, что intent(in) не передается по значению.Вы все равно можете перезаписать исходное значение.

program xxxx  
    integer i  
    i = 9  
    call sub(i)  
    print*,i ! will print 7 on all compilers I checked  
end  
subroutine sub(i)  
    integer,intent(in) :: i  
    call sub2(i)  
end  
subroutine sub2(i)  
    implicit none  
    integer i  
    i = 7  ! This works since the "intent" information was lost.  
end

program xxxx  
    integer i  
    i = 9  
    call sub(i)  
end  
subroutine sub(i)  
    integer,intent(out) :: i  
    call sub2(i)  
end  
subroutine sub2(i)  
    implicit none   
    integer i  
    print*,i ! will print 9 on all compilers I checked, even though intent was "out" above.  
end  

Другие советы

  • intent(in) - выглядит как передача по значению (и изменения этого не отражаются во внешнем коде), но на самом деле передается по ссылке, и ее изменение запрещено компилятором.Но это еще можно изменить.
  • intent(out) — передать как-то по ссылке, по сути возвращаемый аргумент
  • intent(inout) - передача по ссылке, обычный входной/выходной параметр.

Использовать intent(out) если это просто, чтобы задокументировать ваш дизайн.Не беспокойтесь об очень небольшом приросте производительности, если таковой имеется.(Комментарии предполагают, что их нет, поскольку intent(in) технически также передается по ссылке.)

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

А) Вопрос ОП Ре

«Тогда я также хочу знать, почему кто-то хочет использовать намерение (out), поскольку намерение (inout) требует меньше работы (без копирования данных)».

возможно не ответил или по крайней мере слишком прямо/правильно.

Во-первых, чтобы внести ясность Intent атрибуты имеют как минимум ДВА назначения:вопросы «безопасности/гигиены» и «косвенные вопросы производительности» (а не проблемы «прямой производительности»).

1) Безопасность/гигиена:Помогать в создании «безопасного/разумного» кода с уменьшенной возможностью «испортить ситуацию».Таким образом, Intent(In) не может быть перезаписан (по крайней мере, локально или даже «глобально» при некоторых обстоятельствах, см. ниже).

Аналогично, Intent(Out) требует, чтобы Arg был назначен «явный ответ», что помогает уменьшить количество «мусорных» результатов.

Например, при решении, пожалуй, самой распространенной задачи вычислительной математики, т.е.так называемая «проблема Ax=b», «прямой результат/ответ», который нужно найти, - это значения вектора x.Это должно быть Intent(Out), чтобы гарантировать, что x присвоен «явный» ответ.Если бы x был объявлен, скажем, как Intent(InOut) или «no Intent», то Фортран присвоил бы x некоторые «значения по умолчанию» (вероятно, «нули» в режиме отладки, но, вероятно, «мусор» в режиме Release, будучи тем, что находится в памяти в месте указателя Args), и если пользователь затем не присвоил правильные значения x явно, он вернет «мусор».Intent(Out) «напомнит/заставит» пользователя явно присвоить значения x и тем самым избежать такого рода «(случайного) мусора».

В процессе решения можно (почти наверняка) получить обратную матрицу А.Пользователь может захотеть вернуть этот обратный вызов s/r вместо A, и в этом случае A должно быть Intent(InOut).

Альтернативно, пользователь может пожелать гарантировать, что в матрицу A или вектор b не вносятся изменения, и в этом случае они будут объявлены Intent(In), и, таким образом, гарантируя, что критические значения не будут перезаписаны.

2 а) «Косвенная деятельность» (и «глобальная безопасность/гигиена»):Хотя намерения не оказывают прямого влияния на производительность, они оказывают это косвенное влияние.Примечательно, что некоторые типы оптимизации, в частности конструкции Fortran Pure и Elemental, могут значительно повысить производительность.Эти настройки обычно требуют, чтобы все аргументы Args явно объявляли свои намерения.

Грубо говоря, если компилятор заранее знает Intent всех переменных, то он сможет оптимизировать и «проверить на глупость» код с большей легкостью и эффективностью.

Важно отметить, что если кто-то использует конструкции Pure и т. д., то с высокой вероятностью также будет существовать «своего рода глобальная безопасность/гигиена», поскольку Pure/Elemental s/p могут вызывать только другие Pure/Elemental s/p и, следовательно, один НЕ МОЖЕТ прийти к ситуации, показанной в примере «Глейзера».

Например, если Sub1() объявлен как Pure, то Sub2() также должен быть объявлен как Pure, и тогда потребуется объявлять намерения на всех уровнях, и поэтому «выброс мусора», произведенный в «The Glazer Guy's "Пример НЕ мог произойти.То есть код будет такой:

Pure subroutine sub_P(i)
    integer,intent(in) :: i
    call sub2_P(i)
end  subroutine sub_P

Pure subroutine sub2_P(i)
    implicit none
!        integer i          ! not permitted to omit Intent in a Pure s/p
    integer,intent(in) :: i
    i = 7   ! This WILL NOT WORK/HAPPEN, since Pure obviates the possibility of omitting Intent, and Intent(In) prohibits assignment ... so "i" remains "safe".
end  subroutine sub2_P

...при компиляции это приведет к чему-то вроде

" ||Ошибка:Фиктивные аргументы 'i' с намерением (in) в контексте определения переменных (назначение) в (1) | "

Конечно, sub2 не обязательно должен быть Pure, чтобы объявить i как Intent(In), что опять же обеспечит искомую «безопасность/гигиену».

Обратите внимание, что даже если бы я был объявлен Intent(InOut), это все равно не помогло бы с Pure.То есть:

Pure subroutine sub_P(i)
    integer,intent(in) :: i
    call sub2_P(i)
end  subroutine sub_P

Pure subroutine sub2_P(i)
    implicit none
    integer,intent(inOut) :: i
    i = 7   ! This WILL NOT WORK, since Pure obviates the possibility of "mixing" Intent's.
end  subroutine sub2_P

...при компиляции это приведет к чему-то вроде

"||Ошибка:Фиктивный аргумент 'i' с INTENT(IN) в контексте определения переменной (фактический аргумент INTENT = OUT/INOUT) в (1)|"

Таким образом, строгая или широкая зависимость от Чистых/Элементальных конструкций обеспечит (в основном) «глобальную безопасность/гигиену».

Не во всех случаях можно будет использовать Pure/Elemental и т. д. (например,множество смешанных языковых настроек или использование внешних библиотек, находящихся вне вашего контроля, и т. д.).

Тем не менее, последовательное использование Intents, а также, когда это возможно, Pure и т. д. принесет много пользы и избавит от многих неприятностей.

Можно просто взять в привычку объявлять Намерения везде и всегда, когда это возможно, Чистые или нет...это рекомендуемая практика кодирования.

...это также выдвигает на первый план еще одну причину существования ОБА Intent(InOut) и Intent(Out), поскольку в Pure должны быть объявлены все намерения Arg, некоторые Args будут только Out, а другие - InOut (т.е.было бы трудно иметь Pure без каждого из намерений In, InOut и Out).

2 б) Комментарии ОП, ожидающие «улучшения производительности», поскольку копирование не требуется», указывают на неправильное понимание Фортрана и его широкое использование передачи по ссылке.Передача по ссылке означает, что, по сути, требуются только указатели, и на самом деле часто требуется только указатель на первый элемент массива (плюс небольшая скрытая информация о массиве).

Действительно, некоторое понимание можно получить, рассмотрев «старые времена» (например,Fortran IV, 77 и т. д.), при передаче массива можно было бы закодировать следующим образом:

Real*8 A(1000)

Call Sub(A)

Subroutine Sub(A)

Real*8 A(1) ! this was workable since Fortran only passes the pointer/by ref to the first element of A(1000)
                ! modern Fortran may well throw a bounds check warning

В современном Фортране «эквивалентом» является объявление A как Real(DP) A(:) в s/r (хотя, строго говоря, существуют различные настройки, которые выигрывают от передачи границ массива и явного объявления границ, но это было бы длинное отступление на другой день).

То есть Фортран не передает по значению и не «создает копии» для переменных Args/Dummy.A() в вызывающем s/r — это «тот же самый A», который использовался в s/r (конечно, в s/r можно сделать копию A() или что-то еще, что создало бы дополнительные требования к работе/пространству, но это уже другой вопрос).

Именно по этой причине намерения Intent не влияют напрямую на производительность в значительной степени, даже для Arg большого массива и т. д.

Б) Что касается путаницы с «передачей по значению»:Хотя различные ответы выше подтверждают, что использование Intent «не передается по значению», может быть полезно прояснить этот вопрос.

Возможно, будет полезно изменить формулировку на «Намерение всегда передается по ссылке».Это не то же самое, что «не передавать по значению», и это важная тонкость.Примечательно, что намерения не только являются «byRef», но и могут ПРЕДОТВРАТИТЬ передачу по значению.

Хотя существуют специальные/гораздо более сложные настройки (например.смешанные языковые библиотеки Fortran DLL и т. д.), где требуется много дополнительного обсуждения, для большей части «стандартного Fortran» Args передаются по ссылке.Демонстрацию этой «тонкости намерения» можно увидеть в простом расширении примера «The Glazer Guys», например:

subroutine sub(i)
    integer, intent(in) :: i, j
    integer, value     :: iV, jV
    call sub2(i)
    call sub3(i, j, jV, iV)
end
subroutine sub2(i)
    implicit none
    integer i
    i = 7  ! This works since the "intent" information was lost.
end
subroutine sub3(i, j, jV, iV)
    implicit none
    integer, value, Intent(In)          :: i    ! This will work, since passed in byRef, but used locally as byVal
    integer, value, Intent(InOut)       :: j    ! This will FAIL, since ByVal/ByRef collision with calling s/r,
                                                ! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
    integer, value, Intent(InOut)       :: iV   ! This will FAIL, since ByVal/ByRef collision with calling s/r,
                                                ! ... in spite of "byVal" in calling s/r
                                                ! ||Error: VALUE attribute conflicts with INTENT(INOUT) attribute at (1)|
    integer, value, Intent(Out)         :: jV   ! This will FAIL, since ByVal/ByRef collision with calling s/r
                                                ! ... in spite of "byVal" in calling s/r
                                                ! ||Error: VALUE attribute conflicts with INTENT(OUT) attribute at (1)|
    jV = -7
    iV = 7
end

То есть все, что имеет аспект «Out», должно быть «byRef» (по крайней мере, в обычных настройках), поскольку вызывающий s/r ожидает «byRef».Таким образом, даже если все s/r объявляют Args как «Значение», они являются «byVal» только локально (опять же в стандартных настройках).Таким образом, любая попытка вызываемого s/r вернуть Arg, объявленный как Value с любым типом Out Intent, завершится ОШИБКОЙ из-за «конфликта» передаваемых стилей.

Если это должно быть «Out» или «InOut» и «Value», то использовать Intent нельзя:это нечто большее, чем просто сказать: «это не передается по значению».

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top