Forum Programmation.c++ garbage colector C++ ?

Posté par  (site web personnel) .
Étiquettes : aucune
0
26
sept.
2006
Bonjour,

J'ai un projet, et après avoir essyé plusieurs langages (C et D surtout) je pense utiliser C++, surtout car je pense convertir mon projet perso en projet d'études (IUT) et que le langage est imposé. De plus, je connais mal C++, C n'est pas pratique et D n'est pas assez portable.

Mon projet : in langage de programmation inspiré d'une syntaxe List/Scheme (que je n'ai jamais utilisés :). Ce langage doit disposer d'un garbage collector pour libérer la mémoire, mais C et C++ ne le permettent pas facilement.

Bien sûr, il y a le Bohem garbage collector ¹. Le problème je trouve, c'est que ce n'est pas excessivement portable même si il doit pouvoir se trouver un peu partout. De plus, il est conservatif, et peut interprêter n'importe quoi comme un pointeur.

Alors j'ai décidé de créer un petit garbage collector en C++ directement. J'ai déjà écrit un peu de code et je ne sais pas trop ce que ça vaut, niveau performances, bugs, fiabilité ... Je pensait me lancer dans quelque chose de compliqué mais cela ne semble pas si compliqué en fait.

Ma méthode : une classe Object qui sera dérivée par les objets C++ collectables et devant redéfinir une méthode virtuelle permettant de parcourir les références pour l'algo mark&sweep. De plus, pour référencer les références racines sur la pile, un "smart pointer" est utilisé.
J'utilise aussi les smart pointers pour gérer une méthode simplez de comptage de référence en utilisant des poids (un objet à un poids et chaque référence sur cet objet dispose d'une partie du poids². Si on duplique la référence, le poids de la référence est partagé pour alimenter la copie mais le poids total ne change que lorsqu'une référence disparaît)

Qu'en pensez-vous ? Vais-je droit dans le mur ...? Cela me paraît trop simple pour que ce soit si facile.

¹ http://www.hpl.hp.com/personal/Hans_Boehm/gc/
² http://en.wikipedia.org/wiki/Reference_counting#Weighted_ref(...)
  • # GC++

    Posté par  . Évalué à 6.

    Bonjour !

    je pense utiliser C++, surtout car je pense convertir mon projet perso en projet d'études (IUT) et que le langage est imposé.


    Bon, ça veut dire quoi ? Que le C++ t'est imposé ?

    Qu'en pensez-vous ? Vais-je droit dans le mur ...? Cela me paraît trop simple pour que ce soit si facile.


    Pas forcément. Contrairement à une impression répandue, plus on se rapproche du système (et donc plus on élimine des couches), plus on réduit la complexité. La difficulté réside alors plutôt dans le fait qu'il faut tout reconstruire, ce qui est complètement différent.

    L'exemple-type est celui de l'assembleur, que la plupart des nouveaux-venus considèrent comme étant le langage inaccessible par définition, alors qu'il est beaucoup plus rapide d'en faire le tour que pour n'importe quel autre langage.

    Par contre, il est clair que l'unique intérêt d'un garbage collector en C++ est algorithmique. C'est effectivement un excellent projet d'études, mais il risque de te demander beaucoup de temps, surtout si tu connais mal le C++, et de t'empêcher de rendre ton projet dans les temps.

    D'autre part, à moins d'y consacrer par la suite une bonne partie de ton temps et de ton énergie pour le maintenir et en faire quelque chose d'aussi propre que complet, ton GC risque de ne jamais être utilisé.

    En fait, c'est un codeur C++ d'une trentaine d'années qui te parle. Tous les projets sont passionnants, mais c'est à chaque fois une petite partie de sa vie que l'on y consacre et au bout d'un moment, il faut se demander s'il en vaut vraiment la peine.
    • [^] # Re: GC++

      Posté par  (site web personnel) . Évalué à 2.

      En fait, on a un projet à faire en C++ (c'est imposé) et un autre en Java (imposé aussi). Le thème du projet C++ est "créer un interpreteur". Et comme je travaille en ce moment sur un projet personnel visant à créer un langage de script du style de scheme, j'ai pensé que cela pourrait rentrer dans le cadre du projet à l'IUT.

      Le GC, je le crée car j'en ai besoin pour mon langage ... etque je n'aime pas la solution GC de Bohem (car conservatif). Et ce que j'ai trouvé surprenant c'est qu'en quelques heures hier soir, je l'ai fini (sans aucun test et sans compiler non plus :). Je trouve ça assez suspect quand meme.

      Les sources sont là : http://bzr.mildred632.free.fr/viewsource/Projects/gc++/

      5 fichiers aps très gros ... et je pense déja avoir fini (sauf si je tente l'implémentation d'une version concurrente).

      Sinon, je suis complètment d'accord avec vous que les projets ça peut prendre du temps pour finalement pas grand chose ... comme découvrri que ce qu'on a fait existait déjà. Cela permet aussi de faire ses expériences et dans mon cas, apprendre le C++. De plus, ce langage, j'en ai déjà une version presque fonctionnelle en C et un peu moins finie en D ... et j'ai à coté plein de projets qui sont morts-nés.

      Et en plus, je tiens vraiment à le créer ce langage, ce serait à la fois un langage de programmation complet (style scheme) mais aussi un langage qui permettrait d'écrire du texte un peu comme LaTeX ... et ce texte sera transformé en XML ... et ensuite une stylesheet DSSSl me fera un joli PDF ou autre document convertible en PDF :) Il me reste à apprendre DSSSL.
      gros programme, non ?
  • # ...

    Posté par  (site web personnel) . Évalué à 2.

    Côté portabilité, le GC de boehm s'en sort bien à voir la liste des plateformes supportées. On ne peut pas en dire autant de toutes les bibliothèques C++.

    Mais ... le GC demandé, c'est pour ton nouveau langage? Pas pour le C++ si je ne m'abuse. Du coup, je ne suis pas sûr qu'un GC pour le C++ te soit vraiment utile, au contraire d'un GC pour ton langage, mais codé en C++.

    Pour ton smart-ptr, ce peut être un peu inutilement compliqué à implémenter -- ton langage sera-t-il multi-thréadé/multi-processus/multi-machines/... ? Vois si tu auras le temps de faire ça plus tout le reste : ton langage. D'autant qu'il existe déjà des smart-ptr prêts à l'emploi dans divers frameworks, à commencer par boost (voire std::tr1 suivant la génération de compilo visé).

    Sinon, beaucoup de discussions dernièrement au sujet des GCs dans news:comp.lang.c++.moderated.
    news:fr.comp.lang.c++ peut être un endroit intéressant où poser ce genre de questions.

    PS: je mitouille avec Robin des Bulles comme quoi ce peut être passionant. Mais attention, tu risques d'y passer beaucoup de temps alors qu'il te reste l'utilisation et l'intégration d'un outil pour parser ton langage (lex/yak, ANTLR, boost.spirit, huile-de-coude, ...)
    • [^] # Re: ...

      Posté par  (site web personnel) . Évalué à 2.

      Mais ... le GC demandé, c'est pour ton nouveau langage? Pas pour le C++ si je ne m'abuse. Du coup, je ne suis pas sûr qu'un GC pour le C++ te soit vraiment utile, au contraire d'un GC pour ton langage, mais codé en C++.

      Oui, mais ca existe ça ? des GC directement disponnibles pour mon langage ?
      C'est pour ça que j'en fais un ... que je compte accésoirement utiliser pour autre chose que les objets de mon langage.

      Sinon, pour la solution de parsing, je pense que l'huile de coude est plus simple vu la syntaxe très simple du langage (pas de priorité d'opérateurs à gérer ... que des S-expressions) alors qu'apprendre et utiliser lex/bison/yacc/whatever serait plus compliqué
      • [^] # Re: ...

        Posté par  (site web personnel) . Évalué à 2.

        a- Oui, mais ca existe ça ? des GC directement disponnibles pour mon langage ?
        b- C'est pour ça que j'en fais un ... que je compte accésoirement utiliser pour autre chose que les objets de mon langage.

        a- Pas sûr. Tu en auras pour les variables C++, mais pas pour les variables de ton langage. Il y aura peut-être moyen de créer u adaptateur pour utiliser un GC C++ pour les données de ton langage. Mais là, j'avoue qu'il est trop tard pour que j'ai une idée précise et juste sur la chose.

        b- Méfie toi juste pour un truc, c'est un sujet complexe qui prendra du temps à réaliser. Beaucoup de temps. Et le reste de ton sujet n'est pas non plus trivial. D'autant que tu dis toi même ne pas connaitre très bien le C++. Ceci dit, ton premier essai n'est pas si mal, bien qu'il reste encore des gros détails à bien fignoler.


        Bon. Partons d'en haut et descendons. Tu veux implémenter un interpréteur pour un langage disposant d'un GC. OK.
        - Pourrais-tu nous montrer des exemples de code de ton langage?
        - Exemples qui mettent en évidence l'utilisation d'un GC?
        - Et à quoi penses-tu que ressemblerait ton binding (le mot français ne me revient plus) C++ .


        Concernant ton bout code, j'ai un peu de mal à voir où tu vas.
        P.ex. la classe Object. Le nom me gêne, il ne dit pas grand chose sur le rôle de la classe. Je ne sais pas trop s'il s'agit de données de ton langage, ou d'une tentative de reproduire le design d'un langage qui a des oeillières OO.
        De sûr, cela sent la sémantique d'entité. J'interdirai la copie (par dérivation de boost::noncopyable p.ex.) histoire de blinder.

        Ma gêne doit venir en partie du fait que je ne connais pas ta modélisation.
        • [^] # Re: ...

          Posté par  (site web personnel) . Évalué à 3.

          J'ai appelé ma classe 'Object' car c'est une classe qui doit servir de base à toutes les autres classes voulant faire partie du GC. ce n'est peut être pas un nom idéal, enfin.

          J'ai déjà deux implémentation de mon langage, une en C¹ utilisant le garbage collector Bohem, l'autre² en D³ (très peu testée et ne doit pas bien marcher).

          Je me doute bien que c'est un sujet un peu complexe mais après avoir créé quelque chose en C qui marche, je n'ai pas l'impression que ce soit si difficile que ça. Et le garbage collector à l'air aussi d'être très simple.
          La complexité du sujet est la raison pour laquelle je viens demander des avis car je n'ai pas une grande formation pour l'instant, juste 1 an d'IUT derrière moi.

          Un exemple de programme : http://bzr.mildred632.free.fr/Projects/moon-c-gc/test1.moon
          Trace attendue :
          display : ("Test squared brackets " (test abc) " done")
          display : (test-env: true)
          display : (test-env: false)
          display : (function: #function<anonymous moon function> "test-"string")
          display : (call: (f: de upvalue-test-env: false))
          display : (sub-environment: test-env = error)
          display : (sub-environment: f = #function<anonymous moon function>)
          display : (call: (f: test-lexical-scoping upvalue-test-env: false))
          display : (f2: test-f2)
          display : (abc (de #function . hi))
          test1.moon:23:5 Runtime error (not found error) : The symbol cannot be evaluated : variable not found
          Stack traceback :
          test1.moon:23:5 : symbol unknown-symbol
          test1.moon:2:5 : pair (pair (symbol display ... ...
          /home/mildred/Projects/moon/moon-cgc/stdmoon/macros.c:209:0 : macro begin
          test1.moon:1:1 : pair (symbol begin ...
          /home/mildred/Projects/moon/moon-cgc/moonexe/main.c:108:0 : buffer


          Il n'utilise pas spécialement le GC, il faudrait que j'en fasse un autre. Et pour en faire un autre, il faut que crée d'autres fonctions/macros.

          Voici comment sont implémentées les fonctions ... et donc comment on fait un binding : http://bzr.mildred632.free.fr/viewsource/Projects/moon-c-gc/(...)

          La fonction C prend en paramètre la pile d'appels/d'évaluation, une référence sur l'objet fonction/macro, une référence sur son paramètre et un pointeur sur un objet résultat à remplir.
          La fonction retourne un booléen, c'est pour avoir une optimisation de la récursion terminale. Si elle retourne vrai, alors le paramètre résultat est évalué après la fin de l'appel.
          *popn est un peu plus compliqué et n'intervient qu'en cas ou la fonction retourne vrai. C'est le nombre d'éléments à faire sauter de la pile après l'évaluation du paramètre résultat. Valeur par défaut = 0.

          ¹ http://bzr.mildred632.free.fr/viewsource/Projects/moon-c-gc
          ² http://bzr.mildred632.free.fr/viewsource/Projects/moon-d
          ³ http://www.digitalmars.com/d/
  • # et ça compile ?

    Posté par  . Évalué à 2.

    quand je lis des choses comme ça, je me dit que c'est un miracle si ça compile:
    class Object {
        ...
        bool _mark;
        ...
        inline void gcMark() const {
                if(_mark) return;
                _mark = true;
                this->gcIterate();
            };
        ...
    };
    
    soit cette méthode ne peut pas être 'const', soit l'attribut _mark doit être déclaré 'mutable', soit j'ai rien compris (ce qui n'est pas à exclure) ...
    • [^] # Re: et ça compile ?

      Posté par  (site web personnel) . Évalué à 2.

      Je n'ai pas encore essayé de compiler ... et c'est mon premier bout de code en C++ alors il y a probablement plein d'autres erreurs :)

      Bon, ca, c'est une grosse erreur, je suis d'accord :) Cela doit venir d'un copier/coller fait à 1h du matin alors c'est peut être normal.
      • [^] # Re: et ça compile ?

        Posté par  . Évalué à 2.

        ah ok, c'est pour ça!
        en fait, en relisant ton implémentation, je ne suis pas bien sûr de comprendre: tu repères les objets 'racine' en utilisant des pointeurs intelligents (smart pointers). Ca me pose un problème conceptuel, outre le fait que ces pointeurs n'ont d'intelligent que le nom:
        - ces pointeurs ne permettent pas de repérer les références cycliques
        - si ils étaient vraiment intelligent et qu'ils n'avaient pas le problème précédent, alors ils suffiraient à eux seuls à faire un GC.

        donc le mark-sweep en lui même n'est pas compliqué, c'est plutôt le reste, à savoir:
        - trouver les objets racine
        - faire le lien entre un objet les autres objets qu'il référence.

        Pour le point 1, je n'ai pas compris comment tu comptes faire
        Pour le point 2, tout repose sur l'implémentation de la méthode `gcIterate()' qui va devoir être redéfinie (correctement) par chaque classe. La problématique est donc très différente avec le GC de Hans Boehm , ce dernier étant moins contraignant pour le programmeur. Mais comme le programmeur c'est toi, essayes pour voir!
        • [^] # Re: et ça compile ?

          Posté par  (site web personnel) . Évalué à 2.

          bon, maintenant ca compile :)

          pour le nom des smart pointers, je n'ai fait que reprendre le concept que j'ai trouvé dans Crystal Space¹ du même nom qui gère un comptage de références.
          Je n'avais même pas réalisé que smart voulait dire intelligent :)

          1) Le programmeur doit lorsqu'il crée une référence racine vers un objet utiliser les 'smart pointers' (RootRef). Cela va automatiquement renseigner un ensemble contenant la liste des pointeurs racine.

          Il est aussi recommandé d'utiliser des 'smart pointers' simples (Ref) pour les autres références vers des objets, ainsi une métode de comptage des références permet de libérer au plus tôt les objets.

          Pour les cycles, c'est là que l'algo mark & sweep est utile.

          2) Tout repose sur la redéfinition de gcIterate, effectivement, mais ce devrait être fort simple :
          void gcIterate() {
          ref1.gcMark();
          ...
          refN.gcMark();
          }


          C'est vrai que c'est pour une utilisation dans mon application, donc je peux me permettre de telles contraintes que je ne trouve pas très élevés.

          ¹ http://www.crystalspace3d.org
  • # Je comprends pas...

    Posté par  (site web personnel) . Évalué à 2.

    Salut,

    Il y a quelque chose que je ne comprends pas. Tu dis que tu dois créer un nouveau langage (qui ressemble à Lisp) et qui supporte le garbage collector.

    Si je ne me trompe pas... ce n'est pas ton interpreteur qui a besoin d'un garbage collector mais le "lisp" non ?

    Tu n'as donc pas besoin de garbage collector en C++ non ?

    Je vais essayer de prendre un exemple pour expliquer...

    Disons que ton interpreteur comprenne la chose suivante.
    {
    int a = 5;
    a += 2;
    print a;
    }

    (je sais c'est pas du lisp :))

    donc quand tu entres dans ton bloc, tu crées la variables a que tu stoque dans un dictionnaire en c++:
    MonDico["a"] = 5;
    ensuite
    MonDico["a"] = (MonDico["a"] + 1);

    et quand tu quittes le bloc, tu fais un MonDico.remove("a").

    Je sais pas si tu vois ce que je veux dire... mais je me trompe peut-être :)
    • [^] # Re: Je comprends pas...

      Posté par  (site web personnel) . Évalué à 2.

      Cet exemple est très simple et n'a effectivement pas besoin de GC. Au début je pensait de même mais je me suis rendue compte que je devais détecter lorsque des objets ne sont plus utilisés. J'ai pensé à un système de comptage par référence avant de me rendre compte que je devais mettre en place un système de garbage-collector complet (le comptage de références ne libérant pas les cycles)

      par exemple (en suivant ta syntaxe) :

      {
          int tab[] = {1, 2, 3};
          {
              int a = 5;
              a += 2;
              print a; // 7
              tab[3] = a;
          }
          print tab;
      }

      tu traduirais comme ça ?

      MonDico["tab"] = {1, 2, 3};
          MonDico["a"] = 5;
          MonDico["a"] += 2;
          MonDico["a"].print();
          MonDico["tab"].append(MonDico["a"]);
      MonDico.remove("a");
          // Je suppose que je détruit l'objet a ... sinon il ne le sera jamais => fuite mémoire
      MonDico["tab"].print();
      

      Et là, c'est bête mais tu avais dans le tableau "tab" une référence vers l'objet "a" que tu as détruit....

      Peut être que si je fais un langage fonctionnel pur, je n'aurais pas ce problème (car il me serait impossible de modifier des variables déjà créées) mais ce n'est pas (encore) mon but.

Suivre le flux des commentaires

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