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

SL & STL C++ Discussion :

allocation dynamique par blocs


Sujet :

SL & STL C++

  1. #21
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 18
    Par défaut
    Citation Envoyé par Pierre Dolez Voir le message
    J'ai fait quelque changements, par exemple la longueur du mot n'est pas passée en paramètre.
    elle est en paramètre par souci d'optimisation: pas besoin de faire appel
    à la fonction size (ou length) m*n fois si m est le nombre de mots à insérer
    et n est la longueur moyenne de chaque mot. On l'appellera une fois pour chaque mot c'est à dire "seulement" m fois.

    Citation Envoyé par Pierre Dolez Voir le message
    Pour être tout à fait franc, je n'ai pas compris le but de ce calcul, donc je ne peux pas vraiment vérifier qu'il est bon.
    Un exemple d'application : trouver tous les plus longs mots qui commencent par "aba". Par exemple, on doit retourner "abattre" mais pas "abat" car ce dernier est contenu dans un plus long mot. Pour cela, il suffit de récupérer toutes les branches (de la racine jusqu'à une feuille) de l'arbre dont le prefixe est "aba".

    Merci bien

  2. #22
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Bonjour,
    Citation Envoyé par tachaout Voir le message
    Mon problème est que les multiples appels à "new" retardent énormément l'exécution.
    Est-ce que tu peux détailler comment tu en es arrivé à cette conclusion ?

    Car, pour ma part, avec le code donné dans ton premier post, si je remplis une liste définit comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::list<std::string> LesMots; //exemple: les mots du dictionaire
    avec les mots du dico français trouvé ici (22740 mots), la construction et remplissage de l'arbre se font en moins de 50ms, donc je ne peux pas profiler le code. (pas assez d'échantillon). Il faut que je réitère environ 100 fois la construction de l'arbre pour que le profileur commence à donner des résultats probants, et il ne semble pas du tout pointer vers le new.

    Quel est ta plate-forme et compilateur ? Tu es certain de bien compiler en release ?

    EDIT :
    Tiens, avant de rentrer dans les complications sur la gestion mémoire, le simple fait de remplacer dans mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef std::list<noeud *> NoeudList;
    par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef llvm::SmallVector<noeud*, 5> NoeudList;
    et pouf, le temps de construction et de remplissage de l'arbre a été divisé par 2 !
    (Il suffit d'inclure dans le projet SmallVector.h disponible ici, ainsi que type_traits.h dispo ici et SmallVector.cpp dispo ici)

  3. #23
    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
    Par défaut
    Citation Envoyé par Pierre Dolez Voir le message
    Bon, alors je répond. Voici le code
    J'utilise la bibliothèque Borland (VCL)
    Certaines lignes de l'entête sont peut-être inutiles.
    J'utilise 2 classes de Borland
    AnsiString, très voisin de string, mais il faut vérifier si le 1er caractère est de rang 0 ou de rang 1.
    TList, c'est une liste de pointeurs.
    La structure NOEUD définit le lattre concernée et un pointeur sur la liste éventuelle des fils. Ce pointeur existe toujours, mais la liste peut être vide.
    Cette structure est traitée pas le compilateur comme une classe.
    Elle comporte un constructeur qui crée la liste, et un destructeur.
    J'ai fait quelque changements, par exemple la longueur du mot n'est pas passée en paramètre.
    Pour être tout à fait franc, je n'ai pas compris le but de ce calcul, donc je ne peux pas vraiment vérifier qu'il est bon.
    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
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    #include <vcl.h>
     
    #include <vcl\Classes.hpp>
    #include <vcl\SysUtils.hpp>
    #include <vcl\Windows.hpp>
    #include <vcl\System.hpp>
    #include <conio.h>
    #include <stdio.h>
     
    typedef struct NOEUD
    {
    	char lettre;
    	TList *les_fils; // la liste des fils
            NOEUD(char L)
            {
              lettre = L;
              les_fils = new TList();
            }
            ~NOEUD(){/* à faire*/}
    }TabNOEUD;
    typedef TabNOEUD* ptrNoeud;
     
    int inserer_dans_arbre(unsigned int rang,
                           const AnsiString &v,
                           TList *racine)
    {
            int i=rang;
    	// i est la position dans le mot  (rang)
    	// v est le mot
    	// racine est la racine du sous-ararbre dans lequel on insère
    	unsigned int k=v.Length();// k est la taille du mot
            char Lettre=v[i];
            int ir;
            ptrNoeud it=NULL;
            for ( ir=0; ir< racine->Count; ir++)
            {
              it=(ptrNoeud)racine->Items[ir];
              if (it->lettre == Lettre) break;
            }
            if (ir < racine->Count)
            {
              if (++i < k && it != NULL)
                inserer_dans_arbre(i, v, it->les_fils);
            }
    	else
            {//il n'y est pas
    		//////////////////////////////////////////////////////////////////////
    	  ptrNoeud p=new NOEUD(Lettre); //c'est la partie couteuse
    		/////////////////////////////////////////////////////////////////////
              racine->Add(p);
              if (++i < k )
                inserer_dans_arbre(i, v, p->les_fils);
    	}
            return 0;
    }
     
    voidl Afficher(ptrNoeud noeud)
    {
      TList *Fils=noeud->les_fils;
      if (Fils != NULL)
      {
        printf("\n Lettre=%c %d fils",noeud->lettre, Fils->Count );
        for (int i=0; i<Fils->Count; i++)
        {
          ptrNoeud unFils =(ptrNoeud)Fils->Items[i];
          Afficher(unFils);
        }
      }
    }
    int main(){
    	//On récupère la liste des mots. Ensuite on crée l'arbre comme suit
            ptrNoeud arbreP = new NOEUD(0); // le premier niveau de noeuds
            for (int i=0; i< 11; i++)
            {
              AnsiString v;
              switch(i)
              {
                case 0:  v="zero"; break;
                case 1:  v="un";  break;
                case 2:  v="deux";  break;
                case 3:  v="trois";  break;
                case 4:  v="lundi"; break;
                case 5:  v="mardi"; break;
                case 6:  v="mercredi"; break;
                case 7:  v="jeudi"; break;
                case 8:  v="ventredi"; break;
                case 9:  v="samedi"; break;
                case 10:  v="dimanche"; break;
              }
              inserer_dans_arbre(1, v, arbreP->les_fils);
              // le premier caractères est 1 et non 0 pour un AnsiString
            }
            Afficher(arbreP);
            while (!kbhit()); // ou system("pause");
    	return 0;
    }
    La fonction kbhit() attend la frappe d'un caractère. On peut remplacer cette ligne pas "system("pause");
    Ce code est bourré de bugs et mauvaises pratiques. On a inventé le C++ depuis...
    Pourquoi utiliser la VCL de borland alors que le standard propose ce qu'il faut ?
    Pourquoi faire des typedef struct NOEUD ? N'importe quel cours de débutant C++ indique que cela n'a pas lieu d'être en C++
    Tu gères un new dans le constructeur, j'imagine que le delete tu le laisses à la charge du posteur. Mais où sont les politiques de copie et les gestions d'exception ??? Il serait préférable de comprendre le RAII puis d'apprendre les outils qui vont avec : std::vector, pointeurs intelligents, etc.
    Quel intérêt d'utiliser des chaînes de caractères non standard ???
    Il existe des équivalents C++ pour les en-têtes standard (<cstdio> à la place de <stdio.h>). Ce sont ceux là qu'il faut utiliser.
    conio.h :
    Citation Envoyé par wikipedia
    conio.h is a C header file used in old MS-DOS compilers to create text user interfaces. It is not described in The C Programming Language book, and it is not part of the C standard library, ISO C nor is it required by POSIX.

  4. #24
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 18
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Est-ce que tu peux détailler comment tu en es arrivé à cette conclusion ?
    Je n'ai pas voulu embêter les gens avec des détails qui nous auraient sorti du sujet (c'est déjà le cas j'ai l'impression ). En réalité, les données sont sous la forme de chaines d'entiers par exemple, ceci. C'est un fichier de 1,5 million de mots et il existe ~ 6 millions de caractères (entiers différents). Ce qui fait que les mots sont très longs.

    Citation Envoyé par Arzar Voir le message
    Quel est ta plate-forme et compilateur ? Tu es certain de bien compiler en release ?
    VS 10 Express en release et /O2
    EDIT :

    Citation Envoyé par Arzar Voir le message
    Tiens, avant de rentrer dans les complications sur la gestion mémoire, le simple fait de remplacer dans mon code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef std::list<noeud *> NoeudList;
    par :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef llvm::SmallVector<noeud*, 5> NoeudList;
    et pouf, le temps de construction et de remplissage de l'arbre a été divisé par 2 !
    (Il suffit d'inclure dans le projet SmallVector.h disponible ici, ainsi que type_traits.h dispo ici et SmallVector.cpp dispo ici)
    llvm !!! on en apprend des choses

    Merci

  5. #25
    Invité
    Invité(e)
    Par défaut
    @3DArchi
    Je vous prie d'accepter toutes mes excuses.
    Mais je vais tout de même répondre à vos questions.
    1- et c'est le plus important, il ne s'agissait en aucun cas pour moi de donner une solution. Mais pour voir, peut-être par curiosité, j'ai voulu l'écrire.
    2- je n'ai pas vu d'autre réponses que "va donc chez boost", j'ai cherché à être plus constructif, mais à l'évidence c'était une erreur.
    3- j'utilise la VCL parce que c'est ma bibliothèque. Le standars des voitures en France c'est Renault, me reprocherez-vous d'utiliser une Fiat ?
    4- Je ne connais pas le cours de débutant qui indique que "typedef struct ..." n'a pas lieu d'être en C++, alors disons que je ne fais pas du C++, j'utilise un langage qui via mon compilateur est bien compris par ma machine. D'ailleurs, je n'ai jamais dit que je faisais du C++, je me contente de développer et d'utiliser un langage, dont j'ai oublié le nom, pour communiquer avec ma machine.
    5- effectivement je laisse le delete "à faire", mais comme il ne s'agit pas de quelque-chose à utiliser, cela ne m'a pas paru très grave, mais je me rend compte qu'encore une fois, je me suis trompé.
    6- Wijipédia ne constitue pas pour moi une référence en la matière.

  6. #26
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Je me cite :
    Citation Envoyé par Arzar Voir le message
    t que je réitère environ 100 fois la construction de l'arbre pour que le profileur commence à donner des résultats probants, et il ne semble pas du tout pointer vers le new.
    Bon pour avoir le cœur net, vu que je n'avais pas confiance dans le profileur, j'ai quand même créer rapidos un allocator de type arène très très crade :
    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
     
    #include <vector>
     
    template <typename T>
    struct RingBufferAllocator
    {
       RingBufferAllocator(size_t nbOject) : 
       buffer((noeud*)malloc(nbOject * sizeof(T))),
       bump_pt(buffer),
       counter(0),
       {
       }
     
       ~RingBufferAllocator()
       {
          free(buffer);
       }
     
       T* construct()
       {
          T* t = new (bump_ptr) T;
          bump_ptr++;
          counter++;
          return t;
       }
     
       T* buffer;
       T* bump_ptr;
       int counter;
    };
     
    // dans le main
    RingBufferAllocator<noeud> g_allocator(100000); // en global ...
     
    //...
    // dans inserer_dans_arbre()
       //noeud * p = new(noeud); //c'est la partie couteuse
       noeud* p = g_allocator.construct();
    Il n'y a donc plus aucune allocation mémoire durant la construction de l'arbre, le new étant remplacé par la fonction construct extrêmement cheap (une incrémentation de pointeur + un appel au constructeur de noeud)...

    A peine 5% de gain dans les timings !
    Le profileur avait raison...

    EDIT :

    Je n'ai pas voulu embêter les gens avec des détails qui nous auraient sorti du sujet (c'est déjà le cas j'ai l'impression ). En réalité, les données sont sous la forme de chaines d'entiers par exemple, ceci. C'est un fichier de 1,5 million de mots et il existe ~ 6 millions de caractères (entiers différents). Ce qui fait que les mots sont très longs.
    Des détails !?
    C'est sur qu'avec de telles volumes la donne est complètement changée... Vaut donc mieux ne pas trop tenir compte de ce que j'ai pu dire dans les posts précédent.

  7. #27
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 18
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Je me cite :


    Bon pour avoir le cœur net, vu que je n'avais pas confiance dans le profileur, j'ai quand même créer rapidos un allocator de type arène très très crade :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
     
    template <typename T>
    struct RingBufferAllocator
    {
       RingBufferAllocator(size_t nbOject) : 
       buffer((noeud*)malloc(nbOject * sizeof(T))),
       bump_pt(buffer),
       counter(0),
       {
       }
     
       ~RingBufferAllocator()
       ....
    à la compilation, j'ai une erreur au niveau des deux accolades dont voici le message

    1>mon_prog.cpp(53): error C2059: erreur de syntaxe*: '{'
    1> mon_prog.cpp(49)*: lors de la compilation de la fonction membre 'RingBufferAllocator<T>::RingBufferAllocator(size_t)' de la classe modèle
    1> with
    1> [
    1> T=noeud
    1> ]
    1> mon_prog.cpp(74)*: voir la référence à l'instanciation de la classe modèle 'RingBufferAllocator<T>' en cours de compilation
    1> with
    1> [
    1> T=noeud
    1> ]
    ========== Génération*: 0 a réussi, 1 a échoué, 0 mis à jour, 0 a été ignoré ==========

  8. #28
    Membre Expert

    Inscrit en
    Mai 2008
    Messages
    1 014
    Détails du profil
    Informations forums :
    Inscription : Mai 2008
    Messages : 1 014
    Par défaut
    Il y a une virgule en trop après "counter(0),"

  9. #29
    Membre averti
    Profil pro
    Inscrit en
    Octobre 2006
    Messages
    18
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2006
    Messages : 18
    Par défaut
    Citation Envoyé par Arzar Voir le message
    Il y a une virgule en trop après "counter(0),"
    Merci à tous et spécialement à Arzar,

    ta solution m'a permis de gagner 8 secondes pour la construction d'un arbre qui prenait auparavant 45 secondes. Mais ce qui est encore plus intéressant c'est que le traitement que je fais sur l'arbre prend maintenant 3mn au lieu de 4 auparavant.

    Mon explication est que cela est dû à la proximité "spaciale" des noeuds qui rend l'utilisation du cache mémoire et le prefetch plus performant.

    Question subsidiaire en abusant de votre patience: comment augmenter dynamiquement la taille allouée ?

    thanx

  10. #30
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Il est préférable de ne pas le faire, parce que la logique se base sur bump_ptr qui avance dans l'espace mémoire alloué à la base, et qu'il est utilisé pour calculer l'adresse d'autres éléments.

    Comme malloc renverrait sans doute une adresse différente pour le début du premier élément, toutes les relations seraient invalidées, et tu devrais donc reconstruire littéralement tout l'arbre existant à chaque élargissement de la zone allouée.

    Je disais dans mon intervention précédente que je ne savais plus pourquoi c'était déconseillé, et je m'en suis rappelé juste après avoir posté une énorme bêtise ... je l'ai donc supprimée aussi vite
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

Discussions similaires

  1. Réponses: 8
    Dernier message: 14/07/2012, 18h45
  2. Réponses: 3
    Dernier message: 09/05/2012, 09h21
  3. Réponses: 2
    Dernier message: 24/04/2011, 16h24
  4. Réponses: 3
    Dernier message: 01/03/2009, 15h53
  5. Réponses: 4
    Dernier message: 03/12/2002, 16h47

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