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 :

Alignement mémoire avec GCC


Sujet :

C++

  1. #1
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut Alignement mémoire avec GCC
    Bonjour,

    Je cherche à réduire la mémoire de mon application qui alloue très souvent une structure (disons Test dans notre exemple) en heap.
    Voilà un mini-code qui montre le soucis :
    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
    #include <windows.h>
    #include <psapi.h>
    #include <iostream>
     
    static size_t getMemoryConsuption() {
        PROCESS_MEMORY_COUNTERS pmc;
        GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
        return pmc.WorkingSetSize;
    }
     
    struct Test {
        uint32_t* pointer; // 4 bytes
        unsigned char data; // 1 byte
    }__attribute__((__packed__));
     
    int main() {
        std::cout << sizeof(Test) << std::endl;
        for (unsigned int i=0; i<10000000; i++)
        {
            Test* n = new Test();
            n->data = 0;
        }
        std::cout << "getMemoryConsuption  = " << getMemoryConsuption()/(1024*1024) << " MB" << std::endl;
        system("pause");
        return 0;
    }
    En mettant __attribute__((__packed__)), j'ai bien 5 bytes comme taille pour ma structure Test au lieu de 8 (à cause du padding de structure).

    Mais c'est bizarre, en RAM quand je vérifie avec l'API windows et la fonction getMemoryConsuption(), j'ai 157 MB alloué pour 10 000 000 pointeurs sur Test.

    Si j'enlève __attribute__((__packed__)), ma structure Test a donc une taille de 8 bytes mais j'ai quand même 157 MB alloué en RAM.

    Pire encore, si j'enlève le membre data de la structure, la structure devient alignée sans avoir besoin de __attribute__((__packed__)) et a donc une taille de 4 octets. Et bien, j'ai aussi 157 MB de RAM alloué dans ce cas...

    Je compile en 32 bits sous Windows 10 avec GCC 4.9.2 (TDM-1)

    Qu'est ce qui se passe ?

    Merci d'avance
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  2. #2
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    L'opérateur new doit allouer de la mémoire qui fait à minima la taille souhaitée et est alignée à minima à l'alignement nécessaire.
    Au moment de la réservation, il doit aussi réserver des données supplémentaires pour sa gestion interne (au moins un pointeur et un size_t alignés).
    Pour éviter un phénomène dit "gruyère" les réservations ont aussi une taille minimum et un alignement minimum.

    Donc ici quelle que soit la taille de la structure (4,5 ou 8 octets), il y réservation de 32 octets.
    Essaie de réserver des tableaux de 10 Test, la consommation mémoire dépendra alors plus de la taille de Test.

  3. #3
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Citation Envoyé par dalfab Voir le message
    L'opérateur new doit allouer de la mémoire qui fait à minima la taille souhaitée et est alignée à minima à l'alignement nécessaire.
    Au moment de la réservation, il doit aussi réserver des données supplémentaires pour sa gestion interne (au moins un pointeur et un size_t alignés).
    Pour éviter un phénomène dit "gruyère" les réservations ont aussi une taille minimum et un alignement minimum.

    Donc ici quelle que soit la taille de la structure (4,5 ou 8 octets), il y réservation de 32 octets.
    Essaie de réserver des tableaux de 10 Test, la consommation mémoire dépendra alors plus de la taille de Test.
    Oui je me doutais bien que le new devait faire des réservations supplémentaires, c'est bien ce que je craignais

    Je ne peux pas allouer des tableaux de Test car j'ai besoin de pointeurs donc à la rigueur, un tableau de pointeurs comme cela :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    for (unsigned int i=0; i<1000000; i++)
        {
            Test** n = new Test*[10];
            for (unsigned int k=0; k<10; k++)
            {
                n[k] = new Test();
                n[k]->data = 0;
            }
        }
    Mias le problème est toujours le meme, ca ne dépend pas de la taille de Test...

    Une idée ?
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  4. #4
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Et,
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
       Test* alls = new Test[1000000];
     
       for (unsigned int i=0; i<1000000; i++)
       {
                n[i] = alls + i;
                n[i]->data = 0;
       }

  5. #5
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Hum c'est quoi la variable n dans ton exemple ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Test* alls = new Test[1000000];
    Et ce n'est pas un tableau de pointeurs mais un tableau de structure non ?

    PS: Bonnes fêtes de Noel
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  6. #6
    Expert confirmé
    Homme Profil pro
    Ingénieur développement matériel électronique
    Inscrit en
    Décembre 2015
    Messages
    1 599
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 62
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Ingénieur développement matériel électronique
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Décembre 2015
    Messages : 1 599
    Par défaut
    Citation Envoyé par Aspic Voir le message
    Hum c'est quoi la variable n dans ton exemple ?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Test* alls = new Test[1000000];
    Et ce n'est pas un tableau de pointeurs mais un tableau de structure non ?

    PS: Bonnes fêtes de Noel
    n c'est ta variable pointeur, ce qui permet d'avoir un tableau de pointeurs qui pointent tous vers le tableau de structures. C'est le seul moyen d'avoir des pointeurs et de limiter le surcoût du système d'allocation.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Test** n = new Test*[100000];
    Joyeuses fêtes à tous.

  7. #7
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Effectivement ca fonctionne en préallouant à l'avance les structures mais par contre cela implique de savoir à l'avance le nombre de structure souhaité.
    Or dans mon application, je ne connais pas à l'avance la taille du tableau de pointeurs...

    Y'a t-il une solution ? Ou alors il faut passer par une sorte de Memory Pool ?

    Merci
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  8. #8
    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
    Saut,

    Avant de commencer à répondre, j'aurais quelques questions supplémentaires :

    Tu dis "Je ne peux pas allouer des tableaux de Test car j'ai besoin de pointeurs <snip>". Très bien, mais
    1. à quoi correspond pointer dans ton exemple
    2. pourquoi une référence (éventuellement constante) n'est elle pas
    3. si le pointeur est réellement indispensable, est-ce qu'un phénomène d'allocation dynamique y est associé
    4. Si ce pointeur est associé à de la mémoire allouée de manière dynamique, qui sera responsable de cette ressource
    5. si l'idée est "simplement" de garder l'adresse d'un uint32_t, ne pourrais tu pas simplement envisager une référence (éventuellement constante), quitte à postposer la création de tes objets, et ce, malgré les restrictions que cela impose (entre autre, l'impossibilité de faire en sorte de référencer un objet différent)

    la meilleure solution que nous pourrons te proposer dépendra forcément des réponses apportées à toutes ces questions.

    Mais, il restera malgré tout une question importante : dans quelle situation te trouves-tu pour que le paddign par défaut s'avère problématique ... Car, en effet, l'alignement des données utilisé par défaut est prévu pour faciliter l'accès du processeur au données, en faisant en sorte qu'il ne doive pas commencer à chipoter pour les faire entrer dans différents registres.

    Les cas dans lesquels il est vraiment intéressant de réduire le padding par défaut sont donc relativement rare (en plus, sauf erreur de ma part, il me semble que la norme précise que l'on peut augmenter la taille du padding, mais pas la diminuer... Mais il faudrait confirmation sur ce point )
    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

  9. #9
    Membre Expert
    Avatar de Aspic
    Homme Profil pro
    Étudiant
    Inscrit en
    Août 2005
    Messages
    3 905
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hauts de Seine (Île de France)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Août 2005
    Messages : 3 905
    Par défaut
    Bonsoir Koala01,

    Je vais commencer par la fin, c'est vrai que c'est rare un programme où le padding par défaut est gênant mais je suis bel et bien dans un cas où j'ai besoin de consommer le moins de mémoire possible. Je m'explique : je travaille sur de l'IA appliquée aux jeux et j'ai une structure qui représente un état du jeu (la structure Test dans mon exemple). Cette structure contient bien un pointeur représentant un tableau de uint32_t (le membre pointer de type uint32_t dans l'exemple) alloué dynamiquement (car je ne connais pas la taille à la compilation) qui va stocker des données relatives à l'état courant du jeu. La taille de ce tableau est toujours la même au cours de l'algorithme mais impossible de la déterminer à la compilation, ce qui est fort dommage...
    Cette structure contient d'autres infos mais j'ai simplifié pour l'exemple.

    Quand je vais parcourir l'arbre de recherche (c'est à dire l'ensemble des états possibles du jeu), je vais allouer ma structure Test des millions de fois afin de créer des nouveaux états. Actuellement, je vais un new et ca marche très bien mais j'arrive vite à 4Go de RAM avec mon algorithme... ce qui est le maximum pour un programme 32 bits (je ne compile pas en 64 bits).

    L'idée est donc de réduire le nombre d'octets de la structure Test. En cherchant sur le net, j'ai trouvé __attribute__((__packed__)) et j'étais content car avec sizeof(Test) ca avait l'air de fonctionner mais en fait non à cause de l'alignement du new...

    Voilà l'histoire, j'espère que cela sera plus clair pour vous.

    Joyeuses fêtes,
    Qui ne tente rien n'a rien !
    Ce qui ne nous tue pas nous rends plus fort !!
    Mon projet ZELDA en C++/Allegro
    http://www.tutoworld.com - Le Forum -
    Mes ressources Dotnet (cours, sources, tutos)
    --------------------------------------------
    + + =

    Ne pas oublier le Tag !

  10. #10
    Rédacteur/Modérateur


    Homme Profil pro
    Network game programmer
    Inscrit en
    Juin 2010
    Messages
    7 152
    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 152
    Billets dans le blog
    4
    Par défaut
    Honnetement même en sauvant 3octets par structure, ton problème de mémoire ne disparaitra pas. C'est l'algorithme qui crée ces trop nombreuses structures qui doit être fixé.
    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.

  11. #11
    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
    Bonjour,

    Pour limiter l'espace mémoire nécessaire et si le fait d'avoir des instances de test non aligné en mémoire ne te gène pas ( ce qui posera problème plus tard en temps d'accès mémoire ) . Tu peux utiliser des allocateurs custom. ( 1 new et tu découpe l'espace mémoire alloué en n*sizeof(Test))
    Après avoir une IA qui te bouffe plusieurs Giga... il faut changer d'algorithme voir vérifier les delete

  12. #12
    Membre Expert
    Profil pro
    Inscrit en
    Mars 2007
    Messages
    1 415
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2007
    Messages : 1 415
    Par défaut
    Deux choses:
    • Ton problème de RAM ne va en effet pas se résoudre de cette manière, ton algo est probablement trop consommateur.
    • Pour allouer à l'avance les structures, il n'est pas nécessaire de forcément connaître le nombre à la compilation : si tu le connais à l'exécution, tu peux allouer un vector de la bonne taille et ranger tes structures dedans.

Discussions similaires

  1. Allouer un segment de mémoire avec "GNU GCC Compiler&qu
    Par damien99 dans le forum Autres éditeurs
    Réponses: 2
    Dernier message: 22/06/2006, 23h18
  2. adressage mémoire avec gcc
    Par fraisdos dans le forum C
    Réponses: 4
    Dernier message: 29/09/2005, 15h29
  3. define avec GCC.
    Par vagabon dans le forum Autres éditeurs
    Réponses: 4
    Dernier message: 12/06/2003, 14h04
  4. les .a avec gcc
    Par Groove dans le forum Autres éditeurs
    Réponses: 4
    Dernier message: 31/03/2003, 07h59
  5. getch() avec gcc
    Par Jorus dans le forum Autres éditeurs
    Réponses: 5
    Dernier message: 16/12/2002, 14h47

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