Подтвердить что ты не робот

Какова явная разница между намерениями fortran (in, out, inout)?

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

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

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

4b9b3361

Ответ 1

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

Это означает, что 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  

Ответ 2

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

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

Ответ 3

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

A) Вопрос OP 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), и если пользователь не назначил правильные значения х явно, он возвратил бы "мусор" . Intent (Out) "напомнил/заставил" пользователя однозначно присваивать значения х и, таким образом, избавиться от такого рода "(случайного) мусора".

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

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

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

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

Реально, если использовать конструкции Pure и т.д., то с большой вероятностью будет существовать "глобальная безопасность/гигиена", так как Pure/Elemental s/p могут вызывать только другие Pure/Elemental s/p и поэтому НЕ МОЖЕТ прийти к ситуации, указанной в примере "The Glazer Guy's".

Например, если Sub1() объявлен как Pure, то Sub2() также должен быть объявлен как 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 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'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,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 и т.д. (например, многие настройки смешанного языка или когда вы полагаетесь на внешние библиотеки вне вашего контроля и т.д.).

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

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

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

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

В самом деле, некоторые идеи могут быть предложены путем рассмотрения "старых дней" (например, 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

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

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

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

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

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

Несмотря на наличие специальных/гораздо более сложных настроек (например, смешанного языка Fortran DLL и т.д.), где требуется много дополнительного обсуждения, для большей части "стандартного Fortran" Args передаются по 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, объявленная как значение с любым типом Out Intent, будет FAIL из-за "столкновения" стилей передачи.

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