В чем явная разница между намерениями Фортрана (in, out, inout)? - PullRequest
15 голосов
/ 18 июня 2009

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

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

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

Ответы [ 3 ]

20 голосов
/ 08 января 2010

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

Это означает, что 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  
6 голосов
/ 18 июня 2009
  • intent(in) - выглядит как передача по значению (и изменения этого не отражаются во внешнем коде), но фактически передается по ссылке, и изменение запрещено компилятором. Но это все еще можно изменить.
  • intent(out) - передать как-нибудь по ссылке, фактически возвращаемый аргумент
  • intent(inout) - передача по ссылке, нормальный параметр ввода / вывода.

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

1 голос
/ 31 декабря 2016

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

А) Вопрос ОП Re

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

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

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

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

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

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

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

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

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

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

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

Например, если Sub1 () объявлен как Pure, то Sub2 () также должен быть объявлен как Pure, а затем потребуется объявить Intents на всех уровнях, и, таким образом, «мусор», произведенный в Пример Глейзер Парня не может произойти. То есть код будет:

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' с INTENT (IN) в контексте определения переменной (присваивании) в (1) |"

Конечно, sub2 не обязательно должен быть Pure, чтобы я объявил его 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 обеспечит (в основном) «глобальную безопасность / гигиену».

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

Тем не менее, последовательное использование Intents, и, по возможности, Pure и т. Д., Принесет много пользы и избавит от большого горя.

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

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

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

Действительно, некоторая информация может быть предложена с учетом «старых времен» (например, Фортран 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 () или что-то еще, что создаст дополнительные требования к работе / месту, но это другое дело).

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

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

Может помочь изменить формулировку на «Намерение всегда передается по ссылке». Это не то же самое, что «не передать по значению», и это важная тонкость. Примечательно, что Intent не только "byRef", но и Intent может PREVENT передавать по значению.

Несмотря на то, что существуют специальные / гораздо более сложные настройки (например, библиотеки Фортрана на разных языках и т. Д.), Где требуется много дополнительных обсуждений, для большей части "стандартного Фортрана" аргументы передаются с помощью Ref. Демонстрацию этой «тонкости намерений» можно увидеть в простом расширении примера «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 как «Value», они «byVal» только локально (опять же в стандартных настройках). Таким образом, любая попытка вызываемого s / r вернуть Arg, который объявлен как Value с любым видом Out Intent, потерпит неудачу из-за «столкновения» передаваемых стилей.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...