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

C++ Discussion :

Objets Non copiables et conteneur


Sujet :

C++

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut Objets Non copiables et conteneur
    Bonsoir à tous,

    J'ai un problème: pour éviter des problèmes de cohérence de mes données dans mon code, toutes mes classes sont non copiables (enfin, la majorité). De plus pour la majorité d'entre elles, elles n'ont pas de constructeur par défaut (toujours pour préserver au maximum la cohérence de mes données).
    Actuellement, je stocke mes objets dans un vecteur de pointeurs que j'initialise à la lecture d'un fichier xml. Mon problème est que je fais sans arrêt appel à new alors que j'ai l'information dans le fichier du nombre d'objets à créer. Cet appel a new provoque des problèmes de performance (j'ai plusieurs milliers d'objets à lire). Avez-vous une idée pour ce problème?

    Merci

  2. #2
    Membre expérimenté

    Profil pro
    Inscrit en
    Juin 2006
    Messages
    1 294
    Détails du profil
    Informations personnelles :
    Localisation : Royaume-Uni

    Informations forums :
    Inscription : Juin 2006
    Messages : 1 294
    Points : 1 543
    Points
    1 543
    Par défaut
    Salut,

    Peut-être en utilisant un allocateur plus performant ?
    Tu peux regarder par exemple nedmalloc.

    MAT.

  3. #3
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    Merci pour ce lien, ca a l'air prometteur, j'y regarderai plus en détail demain. Sinon j'avais tenté de mon côté l'utilisation du 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
     
    class A : public boost::noncopyable //classe de test
    {
    public:
    	A(int v) : val(v) {}
    	int val;
    };
     
    int main(void)
    {
    	size_t nbElements = 10000000;
    	{
    		std::allocator<A> aAlloc;
    		A* raw = aAlloc.allocate(nbElements*sizeof(A));
    		std::vector<A*> pt(nbElements);
    		size_t sa = sizeof(A);
    		for (int i=0; i<nbElements; ++i, raw+=sa)
    			pt[i] = new(raw) A(i);
    		for (int i=0; i<nbElements; ++i)
    			pt[i]->~A();
    		aAlloc.deallocate(raw, nbElements*sizeof(A));
    	}
    }
    Les performances sont carrément acceptable par rapport à l'allocation au fur et à mesure. Par contre c'est quand même pas très pratique de devoir préciser l'adresse d'allocation. Je n'y connais pas grand chose en allocateur, aurait-tu des liens vers des tutos pour créer son propre allocateur?

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Deux solutions :
    - Utilise les rvalue references pour pouvoir déplacer tes objets au lieu de devoir les copier
    - Utilise un conteneur qui n'a pas besoin de déplacer/copier les éléments quand tu le redimensionnes, éliminant tout besoin pour la capacité de déplacer ou de copier.
    Boost ftw

  5. #5
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Boost.In Place Factory peut aussi t'aider à décaler l'allocation de ton objet de son implémentation. Un tuto (en cours de rédaction) est disponible.

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    merci pour vos réponses. Par contre, de ce que j'ai lu sur les rvalue references, c'est pour la future norme et à priori, visual ne l'implémente pas encore (prévu pour la version 10). Je ne peux pas passer sous linux et utiliser gcc (j'ai lu qu'il implémentait cette partie de la norme) vu que c'est pour mon travail. J'ai regardé un peu Boost.In Place Factory, je connaissais pas et c'est plutôt pas mal. Au final, je sais pas si c'est possible de faire ce que j'aimerais: dans l'idéal, je voudrais, lors de la lecture du nombre d'éléments dans le fichiers, réserver l'espace mémoire pour la lecture des éléments. Un truc dans ce genre là:
    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
     
    template<typename T, typename InPlaceFactoryT> 
    void init(TiXmlElement *root, std::vector<T*>& container, InPlaceFactoryT const& usine)
    {
    	TiXmlElement* elem = root;
    	//on récupère le nombre d'éléments dans le fichier
    	size_t l_count = loadElement<size_t>(elem, "count");
    	//on réserve la place mémoire
    	T* beg = reinterpret_cast<T*>(new char[l_count*sizeof(T)]);
     
    	//iteration
    	T* it = beg;
    	elem = elem->FirstChildElement("item");
    	while (elem)
    	{
    		usine.template apply<T>(current);
    		container.push_back(it);
    		++it;
    		elem = elem->NextSiblingElement("item"); // iteration
    	}
    }
    L'idée ce serait de réserver la mémoire en une fois pour les perfs mais par contre de déléguer la libération de cette mémoire au vecteur paramètre de init. En gros, pour l'utilisateur, init serait équivalente à la méthode:
    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
     
    template<typename T> 
    void init(TiXmlElement *root, std::vector<T*>& container)
    {
    	TiXmlElement* elem = root;
    	//on récupère le nombre d'éléments dans le fichier
    	size_t l_count = loadElement<size_t>(elem, "count");
     
    	//iteration
    	elem = elem->FirstChildElement("item");
           	container.reserve(l_count);
    	while (elem)
    	{
    		usine.template apply<T>(current);
    		container.push_back(new T(/*ici les bons parametres equivalents a la In Place Factory*/);
    		elem = elem->NextSiblingElement("item"); // iteration
    	}
    }

  7. #7
    Expert éminent sénior
    Avatar de Luc Hermitte
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2003
    Messages
    5 275
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Août 2003
    Messages : 5 275
    Points : 10 985
    Points
    10 985
    Par défaut
    Citation Envoyé par CedricMocquillon Voir le message
    J'ai un problème: pour éviter des problèmes de cohérence de mes données dans mon code, toutes mes classes sont non copiables (enfin, la majorité). De plus pour la majorité d'entre elles, elles n'ont pas de constructeur par défaut (toujours pour préserver au maximum la cohérence de mes données).

    Actuellement, je stocke mes objets dans un vecteur de pointeurs que j'initialise à la lecture d'un fichier xml. Mon problème est que je fais sans arrêt appel à new alors que j'ai l'information dans le fichier du nombre d'objets à créer. Cet appel a new provoque des problèmes de performance (j'ai plusieurs milliers d'objets à lire). Avez-vous une idée pour ce problème?
    Je ne comprends pas les réponses faites à ton problème.
    Je décode que tu as un "std::vector<Data*>", et la lecture des noeud provoque un "v.push_back(new Data(nodeInfo));"

    Si un redimensionnement prend trop de temps, c'est que tu lis beaucoup, beaucoup d'objets. Ne peux-tu pas faire un reserve ?
    Le placement-new vaudra le coup uniquement si tu as beaucoup d'objets (hors quelques milliers, ce n'est rien du tout), et à condition qu'ils aient tous la même taille. Et attention à ne pas redimensionner, sans quoi cela pourrait te couter très cher.

    Ce qui me perturbe, c'est que la lecture du fichier a toutes les chances d'être bien plus longue que toutes tes allocations réunies. Ou alors tu as des allocations gigantesques? Mais est-ce que remplacer 10000 allocations gigantesques par une seule 10000 fois plus conséquente sera un gain de temps ?

    As-tu mesuré ton noeud d'étranglement ?
    Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
    Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...

  8. #8
    Rédacteur
    Avatar de 3DArchi
    Profil pro
    Inscrit en
    Juin 2008
    Messages
    7 634
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Juin 2008
    Messages : 7 634
    Points : 13 017
    Points
    13 017
    Par défaut
    Salut,
    Ca pourrait ressembler à ça:
    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
    34
    35
    36
    37
    38
    class A
    {
    public:
       A(int _i)
       {}
    };
     
    class B
    {
    public:
       B(int _NbrElts)
       {
          A *p_liste(m_vect_de_a.get_allocator().allocate(_NbrElts));
          {
             int i_cnt(_NbrElts);
             A *p_courant(p_liste);
             while(i_cnt>0){
                boost::in_place(_NbrElts).template apply<A>(p_courant);
                ++p_courant;
                --i_cnt;
             }
          }
     
          {// Libération:
             int i_cnt(_NbrElts);
             A *p_courant(p_liste);
             while(i_cnt>0){
                p_courant->A::~A();
                ++p_courant;
                --i_cnt;
             }
             m_vect_de_a.get_allocator().deallocate(p_liste,_NbrElts);
          }
       }
     
    protected:
       std::vector<A> m_vect_de_a;
    };
    Par contre, je ne sais pas comment affecter p_liste à m_vect_de_a.
    Si tu fais un assign ou un instert, alors ça n'a plus d'intérêt. Autant faire un reserve comme le dit Luc!

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    301
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 301
    Points : 345
    Points
    345
    Par défaut
    Alors effectivement après vérification, ce n'est pas mon goulot d'étranglement. Par contre pour ma culture perso, j'aimerai bien quand même trouver une solution à mon problème.

    @Luc: je veux bien faire un reserve, mais sur quoi? Si c'est sur mon std::vector<Data*> je vais pas gagner grand chose à réserver de la place pour des pointeurs. Si c'est sur un std::vector<Data> mon problème c'est que mes datas sont non copiables, non assignable et non constructibles par défaut donc je vois pas comment je vais pouvoir les initialiser...

    Au final, j'ai tenté de faire un truc assez proche de ce que proposent 3DArchi et loufoque à savoir: développer mon propre conteneur qui s'initialise à partir d'un fichier xml et d'une Factory, ce vecteur "maison" est une simple surcouche à un tableau C-style (ce qui fait que je n'ai plus de distinction entre p_liste et m_vect_de_a).

  10. #10
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

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

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Ma solution est assez différente de celle proposée par 3DArchi.

    Si c'est sur un std::vector<Data> mon problème c'est que mes datas sont non copiables, non assignable et non constructibles par défaut donc je vois pas comment je vais pouvoir les initialiser...
    Pour mettre des objets dans un vecteur, il faut juste qu'ils soient déplaçables. (draft C++0x actuel)
    A priori tout objet est déplaçable, mais si c'est trop utilise tout simplement un autre conteneur qui n'a pas besoin de déplacer les objets lorsqu'on le redimensionne.
    Ou alors tu peux aussi choisir d'utiliser une implémentation de vector qui ne nécessite pas que l'objet soit déplaçable si t'as fait un bon reserve avant (comme l'ancien draft C++0x).
    Boost ftw

  11. #11
    Membre expert
    Avatar de Klaim
    Homme Profil pro
    Développeur de jeux vidéo
    Inscrit en
    Août 2004
    Messages
    1 717
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur de jeux vidéo
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 1 717
    Points : 3 344
    Points
    3 344
    Par défaut
    CedricMocquillon> Est-ce que par hasard, utiliser un pool (par exemple boost::object_pool) à la place des new/delete ne règlerai pas ton problème? De cette manière tu limites de beaucoup les new/delete (parceque tu sais déjà quelle mémoire max allouer au départ) et tu gardes juste le vecteur de pointeurs pour avoir la "liste" de tes objets créés.
    Je suis pas sur que ça règle ton problème, mais si oui c'est une solution simple a mettre en place.

Discussions similaires

  1. Retourner un objet non-copiable
    Par Florian Goo dans le forum Langage
    Réponses: 12
    Dernier message: 06/03/2009, 10h46
  2. Réponses: 1
    Dernier message: 04/05/2006, 11h33
  3. [OBJET] - non-aggregate type error
    Par jacquesh dans le forum C++
    Réponses: 3
    Dernier message: 28/04/2006, 13h49
  4. [gcc/ld] comment "zapper" les objets non référéren
    Par jula dans le forum Autres éditeurs
    Réponses: 5
    Dernier message: 05/01/2006, 15h15
  5. [POO] balise ou objet non reconnu lors d'un deploiement serveur
    Par benssj5 dans le forum Général JavaScript
    Réponses: 7
    Dernier message: 02/01/2006, 17h26

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