Forum Programmation.autre Permutation "sure" de pointeurs en Fortran

Posté par (page perso) . Licence CC by-sa.
Tags : aucun
0
10
jan.
2014

Bonjour à tous !

Une question de programmation pas directement propre à Linux ou au logiciel libre mais je ne trouve pas la réponse ailleurs et je sais que des experts en HPC traînent parfois ici, et peut-être ont-ils déjà été confrontés à ce problème. En plus, j’utilise gfortran, bien qu’il ne soit pas le fautif ici.

Je cherche à permuter deux pointeurs (appelons les p1 et p2), ce qui s’écrirait naïvement :
ptemp => p1
p1 => p2
p2 => ptemp

Sauf que ceci ne fonctionne qu’avec un niveau d’optimisation 0 ("-O0" à la compilation).
Le soucis provient du fait que la norme Fortran autorise le terme de droite à contenir une copie temporaire des variables utilisées, et que cette solution peut donc potentiellement faire pointer vers un espace temporaire, perdu par la suite.

Une solution pour d’autres situations consiste à envelopper les assignations dans une subroutine (voir la description d’un bug subtil à ce propos). Avec cette astuce, mon code survit aux niveaux d’optimisation 1 et 2, mais échoue à nouveau pour "-O3". En fait, si le terme de droite peut toujours contenir une copie temporaire, je ne vois pas ce que la solution suggérée apporte, à part qu’en l’enveloppant dans une subroutine, le compilateur est moins tenté de le faire pour optimiser le code.

Du coup, ma conclusion à ce jour est qu’il est impossible de permuter deux pointeurs de manière sûre en Fortran. Je reste toutefois très étonné par ce résultat et attends impatiemment d’être contredit par un expert, car je n’aimerait vraiment pas devoir copier leur contenu, vu la taille de ces tableaux.

Une idée ?

Merci beaucoup de votre aide !

  • # Quel est l'intéret de la manoeuvre ?

    Posté par . Évalué à 4. Dernière modification le 10/01/14 à 10:22.

    Je suis curieux de savoir pourquoi tu voudrais permuter deux pointeurs. Je ne connais pas Fortran, donc je ne peux pas t'aider, mais par curiosité, j'aimerais savoir ce que tu veux faire.

    (Quand on commence à avoir l'habitude des variables à affectations unique, on en vient à se dire que permuter des variables, c'est mal : ça complique le debuggage et le cerveau apprend vite à faire autrement et vient même à oublier l'intéret de ce genre de pratique).

    • [^] # Re: Quel est l'intéret de la manoeuvre ?

      Posté par (page perso) . Évalué à 1. Dernière modification le 10/01/14 à 11:33.

      Je modifie un tableau de grande taille de façon itérative et je ne peux pas faire la modification sur place car les données ne sont pas indépendantes. Du coup, j’ai le tableau aux itérations n et n+1 et après chaque itération, je voudrais les permuter.

      Une solution pour éviter de permuter les pointeurs c’est avoir un indice (comme le numéro de l’itération) et de piocher dans le bon tableau en fonction de cet indice. Je finirai par adopter cette solution s’il est effectivement impossible de faire la permutation, mais elle pose plusieurs soucis :

      • mes subroutines vont dépendre d’un paramètre supplémentaire qui n’a pas véritablement de sens (ou devenir impures ce que je ne souhaite pas non plus) ;
      • je ne peux pas réutiliser l’espace de stockage pour un usage temporaire entre mes itérations, à moins de communiquer l’indice partout, ce qui modifie tout le reste du code ou me fait gaspiller plein de mémoire.

      Addendum :
      Une solution intermédiaire serait de ne pas permuter les pointeurs mais juste permuter la destination vers laquelle ils pointent (avec a1 et a2 les vrais tableaux) :

      if (mod(iteration, 2) .eq. 0) then
        p1 => a1
        p2 => a2
      else
        p1 => a2
        p2 => a1
      end if
      

      Ceci a sûrement plus de chance de survivre à l’optimisation mais ne résout pas le problème décrit dans ma question vis-à-vis de la norme. Car si le terme de droite peut toujours être une copie dans le contexte d’une subroutine, n’importe quelle association de pointeur est dangereuse si elle est faite dans une subroutine et que le pointeur peut être utilisé ailleurs que dans une subroutine appelée depuis celle où a lieu d’assignation. Dans mon cas, ça forcerait à faire l’association dans le programme principal, ce qui va à l’encontre du découpage en subroutine pour la clarté du code.

      • [^] # Re: Quel est l'intéret de la manoeuvre ?

        Posté par . Évalué à 2.

        Je modifie un tableau de grande taille de façon itérative et je ne peux pas faire la modification sur place car les données ne sont pas indépendantes. Du coup, j’ai le tableau aux itérations n et n+1 et après chaque itération, je voudrais les permuter.

        Ca me fait vaguement penser à un algorithme de tri … J'avais oublié comment on faisait avec un langage classique :)

        mes subroutines vont dépendre d’un paramètre supplémentaire qui n’a pas véritablement de sens (ou devenir impures ce que je ne souhaite pas non plus) ;

        Ca veut dire quoi "devenir impures" ? Est-ce un concept propre à Fortran ?

        je ne peux pas réutiliser l’espace de stockage pour un usage temporaire entre mes itérations, à moins de communiquer l’indice partout, ce qui modifie tout le reste du code ou me fait gaspiller plein de mémoire.

        Ca fait plaisir de voir des développeurs qui s'occupent de l'utilisation de la mémoire à l'heure ou tout le monde s'en moque :). Par curiosité, et si ce n'est pas indiscret, pour quelle(s) raison(s) ? Contrainte mémoire réelle ou juste par principe ?

        Ceci a sûrement plus de chance de survivre à l’optimisation mais ne résout pas le problème décrit dans ma question vis-à-vis de la norme. Car si le terme de droite peut toujours être une copie dans le contexte d’une subroutine, n’importe quelle association de pointeur est dangereuse si elle est faite dans une subroutine et que le pointeur peut être utilisé ailleurs que dans une subroutine appelée depuis celle où a lieu d’assignation. Dans mon cas, ça forcerait à faire l’association dans le programme principal, ce qui va à l’encontre du découpage en subroutine pour la clarté du code.

        Ca a l'air d'être un langage assez particulier, le Fortran. En tout cas merci pour ces précisions.

        • [^] # Re: Quel est l'intéret de la manoeuvre ?

          Posté par . Évalué à 2.

          Ca fait plaisir de voir des développeurs qui s'occupent de l'utilisation de la mémoire à l'heure ou tout le monde s'en moque :).

          Pas moi!!! Et je peste après les devs qui s'en moquent, java et son gc ont fait beaucoup de mal de ce point de vu la, et je crains (du moins de ce que je vois actuellement sur le code que je vois) que les pointeurs intelligents en c++ incitent les devs à suivre la même voie que les développeurs java.

          pour ce genre de boucle j'aurais plutôt tendance à faire un truc du genre (mon fortran est rouillé je vais plutôt faire du c ;)

          // n est le nombre d'itération 
          for(int i=1 ; i < n ; i+=2 )
          {
             fonction(a1, a2);
             fonction(a2, a1) ;
          }
          if( n%2 ) fonction(a1, a2);

          Cela me parait plus propre conceptuellement ;)

          Il ne faut pas décorner les boeufs avant d'avoir semé le vent

          • [^] # Re: Quel est l'intéret de la manoeuvre ?

            Posté par (page perso) . Évalué à 1.

            Ça revient à écrire les itérations deux par deux. C’est gentil mais en l’occurrence le code en question est une simulation d'écoulement multiphasique turbulent (LES), et les interactions entre les différents modèles sont déjà assez compliquées comme ça au cours d’une seule itération pour je ne multiplie pas par deux les risques d’erreur.

            • [^] # Re: Quel est l'intéret de la manoeuvre ?

              Posté par . Évalué à 2.

              Ce que je recommande c'est plutôt qu'avoir une boucle du genre

              tant que itération
              faire
              a
              b
              c
              d
              eraif

              avoir
              tant que iteration
              faire
              fonction
              eraif

              avec la fonction qui fait
              a
              b
              c
              d

              si tu préfères tu peux aussi faire :

              bool pair=0;
              while( iteration){
                if( pair)
                   iteration = fonction(a1, a2);
                 else 
                   iteration = fonction(a2, a1);
                 pair = !pair;
              }

              C'est juste que j'ai tendance à éviter les tests en coeurs de boucle.

              Il ne faut pas décorner les boeufs avant d'avoir semé le vent

  • # Permutation sans variable intermédiaire

    Posté par (page perso) . Évalué à 1. Dernière modification le 10/01/14 à 14:21.

    Je n'y connais absolument rien en Fortran, donc ce qui va suivre est peut-être, voire probablement, totalement inapplicable.

    Le fait est qu'il y a moyen de permuter deux entiers sans passer par un troisième, avec l'opérateur XOR. En reprenant l'exemple en question, à la sauce C (^ étant l'opérateur XOR bit à bit) :

            p2 = p1 ^ p2;
            p1 = p2 ^ p1;
            p2 = p1 ^ p2;
    

    Suite à une recherche rapide sur le Web, Il semblerait que gfortran ai une opérateur XOR bit à bit ('IEOR', d'après ce que j'ai trouvé). Évidemment, cela ne suffit pas, car, s'il n'est pas possible d'appliquer cet opérateur directement sur des pointeurs (ce qui ne m'étonnerait pas outre mesure), il faut réussir à 'convertir' des pointeurs en entiers (sous condition que ces derniers ai la bonne taille) et vice-versa…

    Toolkit Atlas : pour facilement ajouter une interface graphique à vos programmes (voir page perso) !

  • # Résolu

    Posté par (page perso) . Évalué à 1.

    Bon, après un débogage intensif, j’ai trouvé d’autres bugs qui n’avaient rien à voir avec le problème que j’ai décrit. C’est amusant de voir comme des choses qui n’ont rien a voir a priori sont mélangées lors de la compilation et peuvent avoir des interactions complexes à cause de l’optimisation faite par le compilateur.

    Mon problème n’est qu’à moitié résolu, car je n’ai pas trouvé de réponse claire vis-à-vis de l’affectation des pointeurs et de la possible copie du terme de droite en Fortran standard. Par contre, gfortran semble maintenant produire un exécutable qui fonctionne bien avec la permutation enveloppée dans des subroutines. Je reste donc sur cette solution pour le moment en croisant les doigts pour qu’un autre compilateur n’ait pas une politique d’optimisation à laquelle cette solution ne survivrait pas.

    • [^] # Re: Résolu

      Posté par (page perso) . Évalué à 2.

      J'ai déjà eu des problèmes de fonctions C qui ne marchaient plus avec certains niveaux d'optimisation. Ça c'était résolu avec des pragma indiquant au compilateur de ne pas optimiser ces fonctions spécifiquement.

      Q? Est-ce qu'il y a de telles options de compilation (en ligne dans le source) en Fortran ?

      J'ai trouvé ça:
      http://docs.oracle.com/cd/E19957-01/806-3591/E_directives.html

      Et cette directive:
      C$PRAGMA SUB OPT=n

      Sinon, à mon avis tu auras une réponse sur des newgroups comp.lang.fortran ou fr.comp.lang.fortran, où tu as beaucoup plus de chance de trouver des développeurs pointus sur ce langage.

      Python 3 - Apprendre à programmer en Python avec PyZo et Jupyter Notebook → https://www.dunod.com/sciences-techniques/python-3

    • [^] # Re: Résolu

      Posté par (page perso) . Évalué à 2.

      Ceci dis, Fortran évite au maximum les pointeurs et a ajouté pas mal de possibilités via les ALLOCATABLE. Dans de très nombreux cas, cela suffit et pour la performance, c'est 100 fois mieux car les pointeurs sont en général une horreur lorsqu'on veut optimiser un code.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.