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 :

Consommation de la mémoire d'une unordered_map


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 302
    Billets dans le blog
    3
    Par défaut Consommation de la mémoire d'une unordered_map
    Bonjour les devs,
    Ce qui me fait aimer le C++ à d'autres langages c'est son côté bas niveau. En effet, on peut calculer l'espace en octet d'une variable avec sizeof(<var ou type>).
    Comme je suis indécis quant au choix de ma structure de données, j'hésite entre un tableau pur ou une unordered_map. La rapidité d'accès n'est pas un soucis dans mon cas, ce qui me préoccupe le plus c'est l'espace mémoire.
    J'ai tenté de comparer un tableau de N structures bidons avec une unordered_map qui a comme clef un entier et comme valeur une structure bidon.
    En faisant sizeof(unordered_map), j'obtiens une valeur inférieur à sizeof(tableau_normal) ce qui me paraît bizarre mais surtout une valeur fixé sur 56 quelque soit la taille de la hashmap.

    J'ai déniché un code qui apparemment calcule la mémoire utilisé par une unordered_map, mais je ne suis pas très convaincu :
    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
    #include <iostream>
    #include <unordered_map>
    using namespace std;
     
    struct S{
    char a;
    };
     
    #define SIZE 1000
    int main() {
      S array[SIZE];
      cout << "size array " << sizeof(array)<<endl;
      std::unordered_map<int, S> u;
      for(int i = 0; i< SIZE; ++i){
          S s;
          u[i] = s;
      }
      cout << "1)size unordered map " << sizeof(u)<<endl;
     
     
      size_t count = 0;
      for (unsigned i = 0; i < u.bucket_count(); ++i) {
        size_t bucket_size = u.bucket_size(i);
        if (bucket_size == 0) {
          count++;
        }
        else {
          count += bucket_size;
        }
      }
     
      cout << "2)size unordered map " << count<<endl;
     
      return 0;
    }
    Pour une taille de 1000 j'ai naturellement un tableau qui occupe 1000 octets (structure d'un char..), pour l'unordered map 1109.
    Ma question :
    Comment déterminer facilement et efficacement la taille mémoire de n'importe quelle variable de n'importe quel type?

  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
    Citation Envoyé par ABD-Z Voir le message
    Comment déterminer facilement et efficacement la taille mémoire de n'importe quelle variable de n'importe quel type?
    C'est impossible.
    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 Expert Avatar de Astraya
    Homme Profil pro
    Consommateur de café
    Inscrit en
    Mai 2007
    Messages
    1 048
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 39
    Localisation : France

    Informations professionnelles :
    Activité : Consommateur de café
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mai 2007
    Messages : 1 048
    Par défaut
    Pour compléter la response de @Bousk, c'est très compliqué voir impossible. Cela rentre dans le détail d'implémentation et ne sera pas directement consultable.
    En effet, le sizeof te retourne la taille d'un type sur la pile (stack) et non sur le tas (heap). Sizeof retourne une constante de compilation égale à la taille du type en octets.
    Cela marche bien mais tu ne peut pas obtenir la taille de l'allocation faite sur le tas car elle est dynamique ( grossie ou diminue en fonctionne du nombre d'élément dans ta std::unordered_map par exemple) et cette allocation dynamique n'est pas récupérable sauf si le containeur/class/struct te retourne exactement la taille qu'elle a alloué dynamiquement.
    Dans le cas d'un sizeof(std::unordered_map), le sizeof te retournera la taille du ou des pointeurs qui pointent vers l'allocation dynamique.

  4. #4
    Membre chevronné
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 302
    Billets dans le blog
    3
    Par défaut
    Bon d'accord, donc ce n'est pas trop faisable...
    Cependant je suis tomber sur ça : http://jsteemann.github.io/blog/2016...container-use/
    Et apparemment l'unordered map n'est pas très souhaitable pour mon cas car elle prend trop de place.
    Vous en pensez quoi de l'article?

  5. #5
    Membre expérimenté
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juillet 2018
    Messages
    104
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Tarn (Midi Pyrénées)

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

    Informations forums :
    Inscription : Juillet 2018
    Messages : 104
    Par défaut override de new / delete
    Bonjour,

    Je ne poste pas pour donner mon avis sur l'article (je ne saurais pas quoi dire dessus), mais juste parce que je me disais que pour ta question de test de mémoire d'un objet quelconque, on peut chercher à overrider les opérateurs new et delete.
    En effet, l'allocation dans le tas se fait principalement avec ces deux opérateurs new / delete en C++. On peut toujours utiliser également malloc / free, mais je compte sur le fait que les objets de la STL ne les utilisent pas (j'espère).

    Du coup, pour connaître l'usage de la mémoire directement dans ton code, on peut se débrouiller avec un code comme celui qui suit (qui ne permet pas de calculer l'usage de la mémoire d'un objet spécifiquement, mais plutot celui entre deux lignes de code):
    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
     
    #include <iostream>
    #include <unordered_map>
     
    static std::unordered_map<void*, int> alloc_sizes;
    static bool use_overrided_new_delete = false;
    static size_t heap_usage = 0;
    void* operator new(size_t size) {
        void* ptr = malloc(size);
        if (use_overrided_new_delete) {
            use_overrided_new_delete = false;
            //std::cout << "allocation of size: " << size << std::endl;
            alloc_sizes[ptr] = size;
            heap_usage += size;
            use_overrided_new_delete = true;
        }
        return ptr;
    }
    void operator delete(void* ptr) {
        if (use_overrided_new_delete) {
            size_t size = alloc_sizes[ptr];
            //std::cout << "deallocation of size " << size << std::endl;
            heap_usage -= size;
        }
        free(ptr);
    }
     
    #define SIZE 1000
     
    int main(int, char**) {
        use_overrided_new_delete = true;
        std::unordered_map<int, char> u;
        for(int i = 0; i < SIZE; ++i){
            u[i] = 0;
        }
        std::cout << "stack size: " << sizeof(u) << std::endl;
        std::cout << "heap usage: " << heap_usage << std::endl;
        //  On doit désactiver les overrides pour permettre la désallocation de
        // alloc_sizes sans seg fault
        use_overrided_new_delete = false;
        return 0;
    }
    Puisqu'on ne connaît pas "à haut niveau" la taille de l'objet lors d'un delete, je dois l'enregistrer (ici dans alloc_sizes) dans une map lors du new. Par contre, puisque la manipulation de cette map entraine également l'usage du new, j'utilise le booléen use_overrided pour éviter des appels infinis.

    Et j'obtiens pour la création d'un std::unordered_map<int, char> de 1000 éléments une taille de 27944 octets, ce qui est nettement plus que tes 1109 octets

  6. #6
    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
    Citation Envoyé par AbsoluteLogic Voir le message
    Et j'obtiens pour la création d'un std::unordered_map<int, char> de 1000 éléments une taille de 27944 octets, ce qui est nettement plus que tes 1109 octets
    En même temps, le type de la clef et de la valeur ne sont pas les mêmes: int/char vs void*/int. Mais le calcul de ABD-Z est foireux, il prend en compte le nombre d'élément, mais pas leur taille.

    Sinon, pour calculer à peu près la taille utilisée, il faut mettre un allocateur personnalisé au container (paramètre template) et faire la somme. Ça ne prendra pas en compte la mémoire utilisée par les fonctions d'allocations, car cela dépend de l'implémentation, mais ce sera au moins une approximation.

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 493
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Loire Atlantique (Pays de la Loire)

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

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 493
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par AbsoluteLogic Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int main(int, char**) {
        use_overrided_new_delete = true;
        std::unordered_map<int, char> u;
        for(int i = 0; i < SIZE; ++i){
            u[i] = 0;
        }
        std::cout << "stack size: " << sizeof(u) << std::endl;
        std::cout << "heap usage: " << heap_usage << std::endl;
        //  On doit désactiver les overrides pour permettre la désallocation de
        // alloc_sizes sans seg fault
        use_overrided_new_delete = false;
        return 0;
    }
    Et j'obtiens pour la création d'un std::unordered_map<int, char> de 1000 éléments une taille de 27944 octets, ce qui est nettement plus que tes 1109 octets
    Ca donne quoi en faisant les std::cout << après use_overrided_new_delete = false; ?

  8. #8
    Membre chevronné
    Avatar de ABD-Z
    Homme Profil pro
    Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site
    Inscrit en
    Septembre 2016
    Messages
    302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 28
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Ingé. webapps embarquées – Admin/mainteneur serveur/BDD – Formateur WordPress – Desiger : logo/site

    Informations forums :
    Inscription : Septembre 2016
    Messages : 302
    Billets dans le blog
    3
    Par défaut
    Citation Envoyé par AbsoluteLogic Voir le message
    Bonjour,

    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
     
    #include <iostream>
    #include <unordered_map>
     
    static std::unordered_map<void*, int> alloc_sizes;
    static bool use_overrided_new_delete = false;
    static size_t heap_usage = 0;
    void* operator new(size_t size) {
        void* ptr = malloc(size);
        if (use_overrided_new_delete) {
            use_overrided_new_delete = false;
            //std::cout << "allocation of size: " << size << std::endl;
            alloc_sizes[ptr] = size;
            heap_usage += size;
            use_overrided_new_delete = true;
        }
        return ptr;
    }
    void operator delete(void* ptr) {
        if (use_overrided_new_delete) {
            size_t size = alloc_sizes[ptr];
            //std::cout << "deallocation of size " << size << std::endl;
            heap_usage -= size;
        }
        free(ptr);
    }
     
    #define SIZE 1000
     
    int main(int, char**) {
        use_overrided_new_delete = true;
        std::unordered_map<int, char> u;
        for(int i = 0; i < SIZE; ++i){
            u[i] = 0;
        }
        std::cout << "stack size: " << sizeof(u) << std::endl;
        std::cout << "heap usage: " << heap_usage << std::endl;
        //  On doit désactiver les overrides pour permettre la désallocation de
        // alloc_sizes sans seg fault
        use_overrided_new_delete = false;
        return 0;
    }
    Puisqu'on ne connaît pas "à haut niveau" la taille de l'objet lors d'un delete, je dois l'enregistrer (ici dans alloc_sizes) dans une map lors du new. Par contre, puisque la manipulation de cette map entraine également l'usage du new, j'utilise le booléen use_overrided pour éviter des appels infinis.

    Et j'obtiens pour la création d'un std::unordered_map<int, char> de 1000 éléments une taille de 27944 octets, ce qui est nettement plus que tes 1109 octets
    Cette réponse me convient, ça a l'air d'être bien plus simple à comprendre que les Allocator même si c'est pas top de modifier les opérateurs globaux new et delete.
    En tout cas, là j'ai bien un ordre de grandeur quant aux unordered_map... Ça prend trop de place!
    Après l'avantage, peut-être, des unordered_map dans mon application c'est qu'il se peut que le tableau soit très peux remplis.
    Si je prends le cas où la taille maximale du tableau est de 255, il se peut que le tableau ne contienne rien! C'est une possibilité, et donc j'aurais quand même 255 * sizeof(<type dans le tableau>) alors que dans une unordered_map, j'aurais une consommation de la mémoire qui serait bien moindre.
    D'ailleurs en farfouillant sur Internet, j'ai appris que l'accès au tas est bien plus coûteux en temps comparé à la pile, ce qui semble être bien normal. Donc, comme les unordered_map allouent dynamiquement de la mémoire, comme une bonne majorité des structures de données de la STL, il me semble vrai de dire que l'accès direct à un tableau nature est bien plus rapide que l'accès direct à une unordered_map.
    Soit t un tableau de N éléments et u une unordered_map de N éléments aussi, l'accès à l'élément se trouvant à n par t[n] est plus rapide que u[n]. Mon affirmation est-elle vraie?

Discussions similaires

  1. Réponses: 2
    Dernier message: 21/10/2017, 22h14
  2. Une lecture de fichier midi qui consomme trop de mémoire
    Par padodanle51 dans le forum Général Java
    Réponses: 6
    Dernier message: 12/04/2008, 11h52
  3. [Images] Erreur liée à une consommation excessive de mémoire
    Par cyrill.gremaud dans le forum Bibliothèques et frameworks
    Réponses: 15
    Dernier message: 04/11/2007, 22h55
  4. Utilisation Mémoire d'une application
    Par scorplex dans le forum Composants VCL
    Réponses: 8
    Dernier message: 21/05/2005, 03h01
  5. Problème mémoire avec une dll par chargement dynamique
    Par widze19 dans le forum C++Builder
    Réponses: 6
    Dernier message: 15/12/2003, 13h20

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