Quelle est la différence explicite entre les intentions de fortran (in, out, inout)?

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

  •  06-07-2019
  •  | 
  •  

Question

Après avoir cherché pendant un moment dans les livres, ici sur stackoverflow et sur le Web en général, j’ai trouvé qu’il était difficile de trouver une explication simple aux vraies différences entre les intentions de l’argument fortran. Voici comment je l’ai compris:

  • intent (in) - L'argument actuel est copié dans l'argument factice à l'entrée.
  • intent (out) - L'argument factice pointe sur l'argument actuel (les deux pointent au même endroit en mémoire).
  • intent (inout) - l'argument factice est créé localement, puis copié dans l'argument réel à la fin de la procédure.

Si ma compréhension est correcte, alors je veux aussi savoir pourquoi on veut jamais utiliser intention (sortie) , puisque intention (inout) nécessite moins de travail ( pas de copie de données).

Était-ce utile?

La solution

Les intentions ne sont que des indices pour le compilateur, et vous pouvez les jeter et les violer. L'intention existe presque entièrement pour vous assurer que vous ne faites que ce que vous avez prévu de faire dans un sous-programme. Un compilateur peut choisir de vous faire confiance et d’optimiser quelque chose.

Cela signifie que intent (in) n'est pas transmis par valeur. Vous pouvez toujours écraser la valeur d'origine.

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  

Autres conseils

  • intent (in) - ressemble à passe par valeur (et les modifications de celle-ci ne sont pas reflétées dans le code extérieur) mais est en fait passe par référence et sa modification est interdite par le compilateur. Mais cela peut encore être changé.
  • intent (out) - passe d'une manière ou d'une autre par référence, en fait un argument de retour
  • intent (inout) - passe par référence, paramètre normal in / out.

Utilisez intent (out) s'il est clair, pour documenter votre conception. Ne vous souciez pas du tout petit gain de performance, le cas échéant. (Les commentaires suggèrent qu'il n'y en a aucun, car intent (in) est techniquement également transmis par référence.)

Il n'est pas clair si certaines parties des questions du PO ont reçu une réponse. En outre, il semble qu’il y ait beaucoup de confusion et d’erreurs diverses dans les réponses / discussions qui suivent qui pourraient bénéficier de certaines clarifications.

A) La question du PO concernant

" alors je veux aussi savoir pourquoi on veut jamais utiliser l'intention (out), puisque l'intention (inout) nécessite moins de travail (pas de copie des données). "

n'a peut-être pas répondu, ou du moins trop directement / correctement.

Tout d’abord, les attributs Intention ont au moins deux objectifs: "sécurité / hygiène" et "performance indirecte". problèmes (pas "problèmes de performance directe").

1) Sécurité / Hygiène: pour vous aider à produire "sans danger / raisonnable". code avec possibilité réduite de "gâcher les choses" up. Ainsi, une intention (In) ne peut pas être écrasée (au moins localement, ou même "globalement" dans certaines circonstances, voir ci-dessous).

De même, Intent (Out) requiert l'attribution d'une "réponse explicite" à Arg, contribuant ainsi à réduire les "déchets". résultats.

Par exemple, dans la solution du problème le plus courant en mathématiques informatiques, c’est-à-dire le "problème Ax = b", le "résultat direct / réponse". on cherche sont les valeurs pour le vecteur x. Celles-ci doivent avoir l’intention (Out) de s’assurer que x est assigné un "explicite" réponse. Si x était déclaré comme étant, par exemple, Intent (InOut) ou "Pas d’intention", Fortran affecterait alors à x certaines "valeurs par défaut". (probablement des "zéros" en mode débogage, mais probablement des "ordures" en mode Release, tout ce qui est en mémoire à l'emplacement du pointeur Args), et si l'utilisateur n'attribue pas explicitement les valeurs correctes à x, il retournera "ordures". L’intention (sortie) serait "rappeler / forcer" l’utilisateur d’attribuer explicitement des valeurs à x, évitant ainsi ce type de "déchets (accidentels)".

Au cours du processus de résolution, on produirait (presque sûrement) l'inverse de la matrice A. L'utilisateur peut souhaiter renvoyer l'inverse à l'appelant s / r à la place de A, auquel cas A doit être Intent (InOut). .

Vous pouvez également vous assurer qu'aucune modification n'est apportée à la matrice A ou au vecteur b, auquel cas elles seraient déclarées Intent (In), et ainsi éviter que les valeurs critiques ne soient écrasées.

2 a) "Performance indirecte" (et "sécurité globale / hygiène"): Bien que les intentions ne visent pas directement à influer sur les performances, elles le font indirectement. Certains types d'optimisation, et en particulier les constructions Fortran Pure et Elemental, peuvent notamment améliorer considérablement les performances. Ces paramètres exigent généralement que tous les Args voient leurs intentions déclarées explicitement.

En gros, si le compilateur sait à l’avance l’intention de tous les vars, il peut alors optimiser et "vérifier la stupidité". le code avec plus de facilité et d'efficacité.

De manière cruciale, si l’on utilise des constructions pures, etc., alors, avec une probabilité élevée, il y aura un "type de sécurité / d’hygiène globale". de même, étant donné que les s / p de Pure / Elemental ne peuvent appeler que d’autres s / p de Pure / Elemental et qu’on ne peut donc pas arriver à une situation du type indiqué dans "The Glazer Guy" Exemple.

Par exemple, si Sub1 () est déclaré en tant que Pure, Sub2 () doit également être déclaré en tant que Pure, et il sera alors nécessaire de déclarer les Intentions à tous les niveaux, ainsi le "garbage out". produit dans "The Glazer Guy's" exemple ne pourrait pas arriver. C'est-à-dire que le code serait:

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

... lors de la compilation, cela produirait quelque chose comme

" || Erreur: argument fictif 'i' avec INTENT (IN) dans un contexte de définition de variable (affectation) en (1) | "

Bien sûr, il n’est pas nécessaire que sub2 soit pur pour avoir déclaré i comme Intent (In), ce qui, là encore, fournirait le paramètre "sécurité / hygiène". on cherche.

Notez que même si j’étais déclaré Intent (InOut), il échouerait toujours avec Pure. C'est-à-dire:

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

... lors de la compilation, cela produirait quelque chose comme

"|| Erreur: argument fictif" i "avec INTENT (IN) dans le contexte de définition de variable (argument réel pour INTENT = OUT / INOUT) à (1) |"

"

Ainsi, une dépendance stricte ou étendue des constructions pures / élémentaires assurera (la plupart du temps) "la sécurité / l'hygiène globale".

Il ne sera pas possible d'utiliser Pure / Elemental, etc. dans tous les cas (par exemple, de nombreux paramètres de langue mélangés, ou lorsque vous utilisez des bibliothèques externes indépendantes de votre volonté, etc.).

Néanmoins, une utilisation cohérente des intentions, et chaque fois que cela est possible pure, produira de nombreux avantages et éliminera beaucoup de chagrin.

On peut simplement prendre l'habitude de déclarer ses intentions partout, tout le temps possible, que ce soit pur ou non ... c'est la pratique de codage recommandée.

... cela met également en évidence une autre raison de l'existence de DEUX intentions et d'Intentions, puisque Pure doit avoir toutes les intentions déclarées de Arg, il y aura des args qui sont uniquement sortants, alors que d'autres sont InOut (c'est-à-dire qu'il serait difficile d'avoir Pure's sans chacune des intentions In, InOut et Out Intents).

2 b) Les commentaires du PO attendaient des "améliorations de performances", car aucune copie n'est requise " indique une incompréhension de Fortran et son utilisation intensive de passe par référence. Passé par référence signifie que, essentiellement, seuls les pointeurs sont requis, et en fait, souvent, seul le pointeur sur le premier élément d'un tableau (plus quelques informations de tableau cachées) est requis.

En effet, il serait peut-être utile de prendre en compte "les vieux jours". (par exemple, Fortran IV, 77, etc.), lors du passage d’un tableau, il aurait pu être codé comme suit:

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

Dans le Fortran moderne, l’équivalent "équivalent" est de déclarer A comme réel (DP) A (:) dans le s / r (bien que, à proprement parler, il soit avantageux de passer les limites du tableau et de les déclarer explicitement, mais ce serait une longue digression pour un autre jour) .

C’est-à-dire que Fortran ne passe pas en valeur, ni "faire des copies". pour Args / Dummy vars. Le A () dans l'appelant s / r est le "même A" comme celui utilisé dans le s / r (bien sûr, dans le s / r, on pourrait faire une copie de A () ou autre, ce qui créerait des exigences de travail / espace supplémentaires, mais c'est une autre affaire).

C’est principalement pour cette raison que les intentions n’ont pas d’impact direct sur les performances, même pour les arguments de grande taille, etc., de type Arg.

B) En ce qui concerne le " passer par Valeur " confusion: bien que les diverses réponses ci-dessus confirment que l’utilisation de l’intention n’est pas "passe pour valeur", il peut être utile de clarifier la question.

Il peut être utile de modifier le libellé en "L’intention passe toujours par référence". Ce n'est pas la même chose que "ne pas passer par la valeur" et c'est une subtilité importante. Notamment, non seulement les intentions "byRef", les intentions peuvent empêcher de passer par valeur.

Bien qu'il existe des paramètres spéciaux / beaucoup plus complexes (par exemple, les DLL de Fortran en plusieurs langues, etc.) qui nécessitent une analyse approfondie, une grande partie de "Fortran" standard ", les args sont transmis par la réf. Des démonstrations de cette "Intention subtilité" peut être vu dans une simple extension de "The Glazer Guys" exemple, comme:

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

C'est-à-dire tout ce qui a un " Out " son aspect doit être "byRef". (du moins dans les réglages normaux), car l'appelant s / r attend "byby ref". Ainsi, même si tous les s / r déclarent que les args sont des "valeurs", ils sont "deVal". uniquement localement (encore une fois dans les paramètres standard). Ainsi, toute tentative du s / r appelé de renvoyer un Arg déclaré comme valeur avec une sorte d'intention sortante, échouera en raison de la "collision" des styles de passage.

S'il doit être " Out " ou " InOut " et "Valeur", on ne peut pas utiliser Intention: ce qui est un peu plus que simplement dire "ce n'est pas transmis par la valeur".

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top