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 :

Conteneur personnalisé pour optimisation mémoire, héritage ou réécriture


Sujet :

Langage C++

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut Conteneur personnalisé pour optimisation mémoire, héritage ou réécriture
    Bonjour,

    J'utilise un conteneur dans un reader de données que je remplis au fil du temps.
    Le problème est que la taille finale de la totalité des objets lus fait que l'application consomme beaucoup trop de mémoire.

    Étant donnée que mes conteneurs ne sont que des attributs de ma classe de stockage, il me semble que la mémoire utilisée soit la stack.

    Que puis je envisager pour me libérer de ces contraintes de taille ?

    1- Allouer dynamiquement mes conteneurs pour utiliser une autre zone mémoire ?
    2- Redéfinir un conteneur qui selon sa taille maximale autorisée (configuré par l'utilisateur) écrira tout l'excès mémoire dans des binaires sur disque.

    Merci d'avance !

  2. #2
    Membre régulier
    Homme Profil pro
    Cocher moderne
    Inscrit en
    Septembre 2006
    Messages
    50
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : Oman

    Informations professionnelles :
    Activité : Cocher moderne

    Informations forums :
    Inscription : Septembre 2006
    Messages : 50
    Points : 118
    Points
    118
    Par défaut
    Bonjour,

    Qu'est-ce que tu appelles beaucoup trop de mémoire, et sur quel matériel?

    Étant donnée que mes conteneurs ne sont que des attributs de ma classe de stockage, il me semble que la mémoire utilisée soit la stack.
    Cela dépend de la nature de tes attributs, non ? Si ce sont des pointeurs, par exemple, il y a de grandes chances qu'ils pointent sur le tas.

    Un peu de code à se mettre sous la dent ?

  3. #3
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut
    Bonjour,

    Oui pardon je me suis mal exprimé, et en plus je me suis trompé.
    ma classe de stockage étant créée dynamiquement, je suis déja sur la pile.

    Le principe est simple je n'ai listé que la partie concernée:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    class stockageParams
    {
    vector<myValues> mVectValues;
    bool setVectorValues(vector<myValues>& arVector);
    bool addValues(myValues & aVal);
    myValues getValues(unsigned int aId);
    }
    En gros ma classe de stockage est créée dynamiquement lorsque mon reader parse mon fichier d'entrée.
    Le reader fournit un gros vecteur de "myValues".

    Le problème c'est que cette classe de stockage est utilisée pour chaque paramètre.
    Avec plusieurs milliers de paramètre de plusieurs milliers de "myValues", l'appli finit dans certains cas en saturation mémoire et crash.

    L'idée est donc de spécifier une taille max mémoire pour chaque "stockageParams" et ne garder que la plage autorisée.
    (l'autre étant par exemple ecrite dans des binaires sur disque).
    La méthode getValues renverrai donc directement les valeurs stockées dans la l'instance "stockageParams" si l'id correspond à une plage mémoire sauvée dans la pile, sinon elle irait lire dans des binaires pour le reconstruire à la volée.

    Merci !

  4. #4
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Ta classe me semble très bizarre, à cause de sa gestion des const.

    pourquoi setVectorValues (qui pourrait s'appeler setValues ou même operator=) ne prend pas son argument en référence constante?
    pourquoi addValues ne prend pas une référence constante?
    pourquoi getValues n'est pas const et ne renvoie pas une référence constante?

    En dehors de cela, il s'agit simplement d'encapsuler un vecteur et ca ne doit pas être plus cher que le vecteur lui-même.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  5. #5
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut
    Merci pour le retour,

    Oui réécriture rapide, les set et add sont constants.
    Pour le get c'est vrai qu'on peut retourner une reférence constante et laisser le choix à l'utilisateur de la recopier s'il veut la modifier, c'est plus cohérent.
    Quand tu dis pas bien plus cher que le vecteur lui même tu parles de quoi ?

  6. #6
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    Question consommation, le vecteur est au même endroit que l'objet le contenant, il n'y a pas de surcout spécial.

    Par contre, si tu essaie de charger énormément de donnée, essaie de réécrire le reader facon itérateur.

    operator++ parse une ligne
    operator* renvoie (une référence constante vers) la valeur lue lors de l'analyse.

    à la construction, ou durant ++, tu lis une ligne, et si tu ne peux pas, tu prétends être end.
    end n'étant qu'une valeur arbitraire.

    Tu peux aussi utiliser une interface du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    class Reader<T> {
    private:
        T last;
        bool valid
        <le stream> stream
    public:
        Reader(...) : stream(...), valid(stream >> last) {}
     
        T const& value() const {return last;}
     
        bool next() {
            return valid ? stream>>last : false;//false ou valid, c'est pareil
        }
    };
    stream étant probablement un ifstream ou une référence non constante sur un ifstream. Les deux choix sont négociables.

    Comme le code est finalement simple, tu n'as même pas vraiment besoin du reader, mais d'un istream& operator>>(istream&, myValues &).

    Tout cela demande cependant que le format du fichier soit compatible avec la lecture séquentielle.
    C'est à dire, que les entrées ne soient pas entrelacées.
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut
    Ok merci pour ton retour je vais m'en inspirer pour revoir la partie du read justement.
    Il faut que je regarde justement cette lecture car je ne sais pas si elle est séquentielle

    Par contre pour la partie conteneur (ma classe stockageParams),

    -> Je fais des set de myValues qui inserent dans mon vecteur tant que la taille du vecteur est inférieur à la taille max spécifiée
    -> Si la taille devient trop importante je l'insere dans un fichier binaire
    -> Sur un get si l'id est plus petit que la taille du vecteur je le récupère directement dedans
    -> Sinon je vais le récupérer dans mon binaire

    Vois tu un loup la dessus ou y a t il une manière plus propre pour éviter de saturer la mémoire de mon vecteur ?

  8. #8
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    En fait, l'idée de lire en permanence dans un fichier est très vieille, et peut coûter très cher.

    Tout dépend de la fréquence d'accès aux valeurs.
    Il vaudrait mieux que tu stocke une file des options les plus récemment lues.
    L'idée serait de réduire l'accès au fichier.

    L'idée d'écrire une partie de la ram dans un fichier, c'est le rôle même du fichier d'échange (swap file).
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  9. #9
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Plusieurs milliers de plusieurs milliers, ça fait un ordre de grandeur de quelques dizaines de millions. Du coup, la question*:
    - c’est quoi la taille unitaire d’un myValue ?
    - c’est quoi l’archi cible

    Quelques dizaines de millions, si l’objet est petit, sur une machine moderne, ça devrait passer, sauf s’il y a quelque part un gros gaspillage (première chose à vérifier avant d’envisager de travailler sur un fichier).

  10. #10
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut
    Bonjour et merci,
    Je n'ai pas connaissance de la config de la machine cible mais je pourrais voir ça,
    la recherche a déjà été effectuée et il en résultait que la taille mémoire de ces vecteurs de stockage était responsable de ce débord.
    (débord qui amène un crash)
    La tache qui en résulte est de bénéficier d'un conteneur limité en taille mémoire.

    Pour ce conteneur j'imagine grossièrement quelque chose de ce genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    template <typename T> class myVector
    {
    public:
        std::vector<T> mV;
        unsigned long mMaxSize;
     
        bool set(const T& arValue);
        const T& get(unsigned int aId) const;
        myLimitedVector(void);
        ~myLimitedVector(void);
    };
    Du coup je me demande comment distinguer dans ce cas la libération mémoire du vecteur pour un type T alloué dynamiquement ou non.

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Février 2005
    Messages
    5 069
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Conseil

    Informations forums :
    Inscription : Février 2005
    Messages : 5 069
    Points : 12 113
    Points
    12 113
    Par défaut
    Beaucoup de données => Base de Données

  12. #12
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Un conteneur limité en mémoire, c’est std::array<T,size>.

    Après tu as simplement en plus à gérer l’indice du dernier élément pour garder la taille « réelle » du conteneur.

    Qu’entends-tu par « type T alloué dynamiquement » ? Si tu veux gérer la durée de vie d’un objet alloué dynamique, std::unique_ptr est probablement ce que tu cherches.

  13. #13
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut
    Ok je vais regarder ça de plus près merci.

    Non je me suis peut etre perdu sur ce template en fait dans le cas ou je l'utilise avec des pointeurs,
    Sur qui gère la destruction de l'objet.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    {
        myVector<unsigned long> v1;
        v1.set(50);
    } // à la destruction ok
    {
        myVector<char *> v2;
        char * p = new char[10]
        v2.set(p);
    } // ?
    car si je veux placer le clean total via
    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
    struct Delete  
    {  
        template <class T> void operator ()(T*& p) const  
        {  
            delete p; 
            p = NULL; 
        } 
     
        ...
     
        ~myVector(void)
        {
            std::for_each(mVector.begin(), mVector.end(), Delete());
            mVector.clear();
        }
    };
    J'aurais un problème selon le type déclaré pour T non ? (cas pointeur ou non)
    Comment faire la distinction ?

  14. #14
    Expert éminent sénior

    Femme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2007
    Messages
    5 189
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Juin 2007
    Messages : 5 189
    Points : 17 141
    Points
    17 141
    Par défaut
    A tout hasard, si tu veux juste un vecteur de pointeur, il y a boost::ptr_vector que tu trouveras dans boost pointer containers.

    Ca peut donner des idées.

    Par ailleurs, si tu utilises une liste chainée, tu pourras utiliser de la mémoire non contigüe
    Mes principes de bases du codeur qui veut pouvoir dormir:
    • Une variable de moins est une source d'erreur en moins.
    • Un pointeur de moins est une montagne d'erreurs en moins.
    • Un copier-coller, ça doit se justifier... Deux, c'est un de trop.
    • jamais signifie "sauf si j'ai passé trois jours à prouver que je peux".
    • La plus sotte des questions est celle qu'on ne pose pas.
    Pour faire des graphes, essayez yEd.
    le ter nel est le titre porté par un de mes personnages de jeu de rôle

  15. #15
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut
    En fait je ne veux pas juste un vecteur de pointeur.
    Et je ne peux pas utiliser boost.

    Cette classe de stockage est pluggé sur un reader qui fournis des données de type standard long float etc, et également de type char * pour retourner des buffers de valeurs.

    Du coup dans cette classe de stockage ou je voulais ne garder en mémoire que la capacité max autorisée,
    ce template fonctionnerait bien sur tous ces types primitifs.

    Par contre en l 'utilisant avec des char * je bloque (ou tout autre pointeur)

  16. #16
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Utilise des unique_ptr (ou une autre classe d’encapsulation) pour gérer ta mémoire allouée dynamiquement, plutôt que de vouloir le faire dans le conteneur, ce qui est une mauvaise idée.

    Au fait, pourquoi char* plutôt que std::string ?

  17. #17
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut
    Ok donc mon conteneur ne s'occupe pas de gérer cette désallocation.
    Oui je suis en train de regarder du coté de unique_ptr.

    Pour les char * le souci c'est que c'est fournit par une lib que je ne peux modifier.
    Et les données ne sont pas utilisées en tant que string mais en tant que mot binaire.

    Je pourrais les convertir en string pour le stockage et les réutiliser en char pour les traitements,
    Ou alors les transformer en list de char.

  18. #18
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Si le buffer est fourni par une lib, il doit y avoir une fonction particulière pour le désallouer. Dans ce cas, utilise plutôt un objet maison dont le constructeur va prendre la propriété du buffer, et le destructeur se charge de libérer le buffer en appelant la fonction adéquate.

    Quelque chose du genre :

    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
     
    class BufOwner
    {
       char * buf;
    public:
        BufOwner() : buf(nullptr) {}
        explicit BufOwner(char* b) : buf(b) {}
        BufOwner(BufOwner const&)=delete;
        BufOwner & operator=(BufOwner &)=delete;
        BufOwner(BufOwner&& other) noexcept : buf(other.buf) { other.buf = nullptr; }
        BufOwner& operator=(BufOwner&& other) noexcept { buf = other.buf; other.buf = nullptr; }
        ~BufOwner() { if(buf) lib_free_buffer(buf);
     
       char * get() { return buf; }
    };
    (tapé tel quel, au erreurs / typo près).

  19. #19
    Membre régulier
    Profil pro
    Inscrit en
    Novembre 2006
    Messages
    366
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2006
    Messages : 366
    Points : 116
    Points
    116
    Par défaut
    Ok merci bien,
    j'ai regardé un peu plus en détail le pré traitement.
    La lib peut fournir en fait un T*.

    Actuellement la partie du code que l'on plug sur la lib et qui parfois fait défaut récupère ce T* et le copie dans un vecteur sur lequel on travaillera dans le reste des traitements
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    template<typename T> copyArray(T* apT, unsigned long N, vector<T>& arOutVect)
    {
        arOutVect.clear();
        arOutVect.assign(t, t+N);
    }
    Apres la copie on libère le T* via justement une méthode fournit pour la lib.

    En fait je pourrais me baser sur ta proposition:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class BufOwner<T>
    {
        T * buf;
    public:
        explicit BufOwner(T* b) : buf(b) {}
        ~BufOwner() { if(buf) lib_free_buffer(buf);
        ...    
    }
    Et l'enrichir avec des get set comme std::vector (car le reste de l'appli se base sur actuellement sur le std::vector créé )

    Ou alors je me base sur ta proposition et j'encapsule directement un vector
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    class BufOwner<T>
    {
        vector<T> buf;
    public:
        BufOwner() : {}
        explicit BufOwner(T* b, unsigned long size) : { buf.assign(b, b+size); lib_free_buffer(b); }
        ...
    }
    Bon le probleme c'est qu'actuellement c'est justement lors de la copie de la zone mémoire de la lib vers le vect que ca crash sur certains cas (200M d'éléments).
    Donc peut être garder la première qui ne recopie rien.

  20. #20
    Membre émérite
    Avatar de white_tentacle
    Profil pro
    Inscrit en
    Novembre 2008
    Messages
    1 505
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2008
    Messages : 1 505
    Points : 2 799
    Points
    2 799
    Par défaut
    Si tu as des problèmes d’occupation mémoire, effectivement, il vaut mieux éviter de recopier le buffer, surtout s’il fait 200Mo.

    Pa rapport au fait que le reste du code se base sur des vectors, il est probable que tu puisses le « templatiser » pour qu’il s’adapte au conteneur voulu (vector dans certains cas, buffer dans d’autres).

Discussions similaires

  1. Conteneur pour optimisation de temps de calcul
    Par Kaluza dans le forum SL & STL
    Réponses: 5
    Dernier message: 04/04/2010, 00h33
  2. Réponses: 6
    Dernier message: 23/02/2007, 21h20
  3. [glut] vmanque d'inspiration pour optimiser
    Par khayyam90 dans le forum OpenGL
    Réponses: 7
    Dernier message: 01/09/2004, 13h41
  4. [DirectDraw] Que faire pour optimiser le rendu ???
    Par mat.M dans le forum DirectX
    Réponses: 8
    Dernier message: 12/12/2003, 18h02

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