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

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

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

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

3 ответа

Решение

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

Это означает, что 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) технически также передать по ссылке.)

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

А) ОП вопрос Re

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

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

Во-первых, чтобы быть ясно, 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 на всех уровнях, и, таким образом, "мусор" будет создан в "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' с 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, будут только некоторые аргументы Out, в то время как другие InOut (то есть было бы трудно иметь Pure без каждого из In, InOut и Out Intents).

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

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

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

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

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

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

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

Другие вопросы по тегам