IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

C++11 et utilisation de la mémoire


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 126
    Billets dans le blog
    149
    Par défaut C++11 et utilisation de la mémoire
    Bonjour,

    Je cherche à faire une pool mémoire permettant d'éviter toutes allocations de mémoire durant l'exécution du programme (enfin, plus précisément, durant la boucle de jeu).
    Ma première idée est de reposer sur std::vector et de jouer avec le reserve(), que j'appelle à la construction de ma pool. Ok, c'est bien, mais lorsque je veux mettre un élément dans la pool, cet élément va être créé puis placé dans la pool (surement à travers une copie).

    Heureusement, j'utilise emplace_back() permettant d'éviter la copie, si je ne dis pas de bêtises. Malheureusement, il y a toujours allocation de mémoire, notamment à la création de mon nouvel élément à mettre dans la pool.
    En théorie, pour que ma pool soit parfaite, il ne faudrait pas que les éléments soient créés lors de la boucle de jeu, mais simplement à la création de la pool.

    Pour ce faire, je devrais construire ma pool (mon std::vector) avec un resize(). Ainsi, les éléments seraient déjà prêt et je n'aurais qu'à les utiliser. Dans la boucle de jeu, je vais créer des objets qui seront dans la pool. En théorie, on les construiraient (avec des arguments), mais là, ce n'est pas exactement ce que je veux faire. Et là, je ne vois pas exactement comment faire, pour injecter de nouveaux éléments (ou du moins, des données pour construire les éléments) dans la pool, sans jamais faire d'allocation mémoire pour ces éléments.

    Du coup, ma question est : comment bien faire. Je pensais que la sémantique de déplacement pourrait faire ce type "d'injection".

    Voici mon implémentation de pool actuelle : https://github.com/LittleWhite-tb/Pa...ObjectPool.hpp

    Est-ce que la meilleure solution est bien d'allouer la mémoire avec resize() (pour que tous les éléments soient créé), puis, lorsque j'ai besoin de faire un get() pour récupérer l'élément. Mais dans ce cas là, comment je construis l'élément (comme une réinitialisation, ou autre ?). Normalement, j'utiliserai un constructeur, mais dans un tel cas, cela n'irait pas, je crois, notamment car on ne peux pas appeler le constructeur à la main.
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  2. #2
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 147
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : Canada

    Informations professionnelles :
    Activité : Network game programmer

    Informations forums :
    Inscription : Juin 2010
    Messages : 7 147
    Billets dans le blog
    4
    Par défaut
    Salut,

    j'en ai jamais vraiment créé, mais utilisé, et je suis surpris que tu n'aies pas de méthodes Get et Release ?
    Tel que je le vois, tous tes éléments sont créés au début, et l'utilisation de la pool par après est juste du Get/Release. Aucune construction d'objet, donc aucun constructeur appelé, on réutilise l'existant. Pas plus de destructeur.
    Du coup il faudrait non pas un vector<T> mais un vector<struct {T m; bool used;}>.

    A moins que tu ne parles que de réserver un espace mémoire pour tes allocations, donc ce serait plus l'utilisation d'un buffer qu'il te faudrait ? http://www.codeproject.com/Articles/...o-implement-it
    Pensez à consulter la FAQ ou les cours et tutoriels de la section C++.
    Un peu de programmation réseau ?
    Aucune aide via MP ne sera dispensée. Merci d'utiliser les forums prévus à cet effet.

  3. #3
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    403
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 403
    Par défaut
    Je ne sais pas si tu connais le livre "game programming pattern". Si ce n'est pas le cas, tu as un chapitre sur les object pool dedans, cela devrait t'aider : http://gameprogrammingpatterns.com/object-pool.html

  4. #4
    Responsable 2D/3D/Jeux


    Avatar de LittleWhite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mai 2008
    Messages
    27 126
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Mai 2008
    Messages : 27 126
    Billets dans le blog
    149
    Par défaut
    Citation Envoyé par mintho carmo Voir le message
    Je ne sais pas si tu connais le livre "game programming pattern". Si ce n'est pas le cas, tu as un chapitre sur les object pool dedans, cela devrait t'aider : http://gameprogrammingpatterns.com/object-pool.html
    C'est exactement comme ça que je l'ai appris. Malheureusement, je trouve ce design très C/C++, mais pas C++11. Là, j'essaie de faire le code le plus simple possible (en utilisant au mieux l'existant), tout en ayant exactement ce que je veux (et qu'il soit facile à utiliser (et difficile de mal utiliser aussi)).

    Par contre, une chose que cet exemple ne montre pas, c'est la technique du swap. En théorie, dans une pool, on a jamais besoin de garder un boolean pour chaque élément. Il suffit de swap chaque élément mort, à la fin de la liste (le swap se fait entre l'élément actuel et le dernier actif) et de diminuer le compteur d'élément vivant. Ainsi, les éléments vivant sont absolument toujours au début, ce qui permet de, balancer la liste des particules directement au GPU car le tampon est déjà prêt (le GPU s'en fout de ce qui est mort ou vivant, il veux juste un tampon contigu à afficher). Cela évite aussi le clearing (tout comme l'utilisation du boolean).
    C'est d'ailleurs grâce à cette technique que je pouvais reposer complètement sur std::vector, lors de mon implémentation, montrée ci-dessus.

    j'en ai jamais vraiment créé, mais utilisé, et je suis surpris que tu n'aies pas de méthodes Get et Release ?
    Exactement. Cela est "étonnant" et d'ailleurs c'est une des fautes de mon design. Par contre le Release, chez moi, c'est le purge(), car je ne veux pas l'utilisateur de la pool garde les instances dans un coin de sa mémoire pour pouvoir dire : "ah, tiens, maintenant je release cela". Le purge() peut être vu comme moins optimisé, sachant que le parcours pour savoir ce qu'il faut virer ou non, est fait sur toute la liste.

    Mon problème du Get() (ce que je n'aime pas pour le moment et je cherchais à faire mieux), c'est qu'il chaque classe que je vais mettre dans la pool() doit avoir une fonction init() (une fonction similaire à un constructeur, sans pour autant être un constructeur) afin de mettre les valeur d'initialisation de l'objet.

    Pour ce que vous appelez buffer, je connais aussi et c'est ultra cool pour le débogage et les systèmes ultra limités en mémoire. Par contre, je ne veux pas tomber jusqu'à ce niveau (ça me casse les pieds de refaire des allocateurs et une gestion de la fragmentation et ainsi de suite ).
    Vous souhaitez participer à la rubrique 2D/3D/Jeux ? Contactez-moi

    Ma page sur DVP
    Mon Portfolio

    Qui connaît l'erreur, connaît la solution.

  5. #5
    Membre Expert
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2011
    Messages
    760
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 760
    Par défaut
    Ne pas utiliser vector serait beaucoup mieux en fait, justement car il fait appel au constructeur. Pour le faire à la main: placement new.
    (note que emplace_back ou push_back revient au même dans ton code: les 2 appelleront le constructeur de mouvement).

    Un petit exemple avec le placement new:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
    #include <new>
    #include <utility>
    #include <algorithm>
     
    template<class T>
    struct Buffer
    {
      Buffer(std::size_t sz)
      : p(static_cast<T*>(::operator new(sz * sizeof(T))))
      , begin(p)
      , end(p + sz)
      {}
     
      ~Buffer()
      {
        std::for_each(begin, p, [](T & x) { x.~T(); });
        ::operator delete(begin);
      }
     
      template<class... Args>
      T & create(Args && ... args)
      {
        assert(p != end);
        new (p) T(std::forward<Args>(args)...);
        return *p++;
      }
     
    private:
      T * p;
      T * begin;
      T * end;
    };
    Par contre, tu ne pourras pas faire la technique du swap car elle modifie les références. Parmi les solutions qui me viennent en tête
    - Passer par un "buffer" (un peu comme le code au-dessus) + un tableau de T* sur le buffer. Le swap s'applique sur le tableau sans invalider les références.
    - Garantir que le dernier élément créé soit le premier supprimé .

  6. #6
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    403
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 403
    Par défaut
    Remarque pour le move : si ton type T est une structure qui ne contient pas des objets dynamiques (pointeurs, vector, string, etc), alors ton move fera une copie. Et comme en général, le but d'un pool object est d'éviter les allocations, c'est généralement le cas. Donc ta fonction add fera une copie.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    struct Data { int i, j, k; };
    Data d;
    pool.add(d); // copie(
    Quand a ta fonction create, tu crées un objet par défaut avant de l'utiliser (et donc potentiellement ne pas utiliser les valeurs par défaut que tu as utilisé.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    auto& d = pool.create();
    d.i = 1; // initialisation de i dans create est inutile
    Pour le add, remplace par le placement new de jo_link_noir (qui est en fait un emplace_back). Tu peux aussi utiliser vector + emplace_back. Si tu ne veux pas utiliser un vector, tu peux, mais il faudrait quand même créer une capsule RAII plutôt que de faire comme jo_link_noir (cf l'article de Stroustrup sur l'exception safe de vector). Regarde aussi std::get_temporary_buffer plutôt que new[size]

  7. #7
    Membre très actif

    Homme Profil pro
    Étudiant
    Inscrit en
    Novembre 2011
    Messages
    685
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Novembre 2011
    Messages : 685
    Par défaut
    Citation Envoyé par LittleWhite Voir le message
    Bonjour,

    Je cherche à faire une pool mémoire permettant d'éviter toutes allocations de mémoire durant l'exécution du programme (enfin, plus précisément, durant la boucle de jeu).
    Ma première idée est de reposer sur std::vector et de jouer avec le reserve(), que j'appelle à la construction de ma pool. Ok, c'est bien, mais lorsque je veux mettre un élément dans la pool, cet élément va être créé puis placé dans la pool (surement à travers une copie).
    Je trouve cette conversation intéressante mais je me pose quelques questions sur la finalité : tu parles de boucle de jeu, tu vas donc avoir un jeu où toutes les instances ont une durée de vie égale à la durée d'exécution du code séparant chaque instanciation de la boucle du jeu additionnée à la durée d'exécution de ta boucle de jeu. Pourquoi ne pas gérer cela sous forme d'un bête catalogue de ressources ? Tu cherches à éviter d'utiliser plus de mémoire ou plus d'objet ?

    Je demandes cela car je trouve ton code un peu compliqué (mais je soupçonne de ne pas comprendre la finalité totalement, le code est peut-être juste complexe)

  8. #8
    Membre émérite

    Profil pro
    Inscrit en
    Décembre 2013
    Messages
    403
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Décembre 2013
    Messages : 403
    Par défaut
    @Kaamui: le but premier d'un pool d'objets est d’éviter les allocations/libérations (très coûteux) durant la boucle de jeu. On pré-alloue un tableau d'objets et dans le boucle on fait simplement passer un objet d'un état "invalide" a "valide".

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Utilisation de la mémoire vive par un programme
    Par Pixcoder dans le forum C++
    Réponses: 13
    Dernier message: 25/09/2006, 12h36
  2. Réponses: 21
    Dernier message: 21/07/2006, 16h55
  3. Utilisation de la mémoire dynamique
    Par Stany dans le forum Windows
    Réponses: 17
    Dernier message: 27/04/2006, 11h39
  4. Utilisation de la mémoire
    Par jagboys dans le forum MFC
    Réponses: 1
    Dernier message: 12/11/2005, 16h30
  5. Utilisation de la mémoire vive....
    Par Neilos dans le forum Windows
    Réponses: 9
    Dernier message: 24/11/2003, 11h09

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo