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 :

C++ différence entre déclaration static et dynamic


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Août 2008
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 27
    Par défaut C++ différence entre déclaration static et dynamic
    Bonjour,

    Je ne comprends pas pourquoi mon programme n'utilise pas la même quantité de mémoire selon que j'alloue de la mémoire dynamiquement ou statiquement.

    Voici mon exemple :

    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
    #include <iostream>
    #include <vector>
     
    void with_pointer(int size){
    	std::vector<int*> v(size);
    	for(int i = 0; i < size; ++i){
    		//v[i] = (int*)malloc(sizeof(int));
    		v[i] = new int();
    	}
    	system("PAUSE");
    }
    void without_pointer(int size){
    	std::vector<int> v(size);
    	system("PAUSE");
    }
     
    int main(int argc, char* argv[]){
    	int size = 1000000;
     
    	//with_pointer(size);//67 400KB
     
    	without_pointer(size);//4 800KB
    	return 0;
    }
    Mon but étant de voir l'espace mémoire utilisé (avec le gestionnaire des tâches) sur une allocation de 1 000 000 de int (soit 4Mo). En static, donc la version without_pointer utilise bien une quantité de mémoire de l'ordre de 4Mo. En dynamique, c'est beaucoup plus! Je comprendrais que la mémoire double car j'utilise un tableau de pointer qui pointent vers des int. Mais là, le rapport est très différent.

    D'où cela vient-il ?

    Merci

  2. #2
    Membre Expert
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Par défaut
    Ta comparaison n'a pas beaucoup de sens:
    • à ma connaissance, un vector alloue sur la heap quoi qu'il arrive
    • tu ne peux comparer des valeurs int a des pointeurs sur int, en fonction des architecture un pointeur peut être plus petit (EDIT: peu probable), ou plus grand (EDIT: cf adressage 64 bits...) qu'un int en mémoire
    • tu ne peux pas comparer non plus un vector seulement réservé (ton cas dit "sur la pile") avec un vector réservé ET rempli (ton cas dit "sur le heap")

  3. #3
    Membre chevronné
    Inscrit en
    Décembre 2010
    Messages
    290
    Détails du profil
    Informations forums :
    Inscription : Décembre 2010
    Messages : 290
    Par défaut
    Il faut savoir que malloc() ne dit pas qu'il alloue exactement la taille demandée. Beaucoup d'implémentations allouent un peu plus, ne serait-ce que pour du "book-keeping" ou prévoir une réallocation ultérieure.
    Faire 1 millions d'allocations de 4 octets est assez suspect, ça ne correspond pas à ce que font la plupart des programmes, donc ça se comprend que la bibliothèque standard ne soit pas optimisée pour ce cas-là.

    J'ai une question toutefois : quel outil as tu utilisé pour obtenir les chiffres que tu donnes ?

  4. #4
    r0d
    r0d est déconnecté
    Membre expérimenté

    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    4 290
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Août 2004
    Messages : 4 290
    Billets dans le blog
    2
    Par défaut
    Citation Envoyé par therwald Voir le message
    à ma connaissance, un vector alloue sur la heap
    Sauf si tu spécifie ton propre allocator (2eme type template de la classe vector).

    En fait, un int[N] et un vector<int>(N) ont la même taille en mémoire (à quelques bits près), que soit sur la pile ou sur le tas. De même, un (int*)[N] vector<int*>(N) occuperont le même espace mémoire.

    En revanche, et pour paraphraser therwald, stocker un int et un int* ce n'est pas la même chose. Un int* est un pointeur sur un int, il faut donc ajouter la mémoire utilisée pour le pointeur en lui-même.

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Août 2008
    Messages
    27
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2008
    Messages : 27
    Par défaut
    Merci de pour vos réponses

    Citation Envoyé par therwald Voir le message
    tu ne peux comparer des valeurs int a des pointeurs sur int, en fonction des architecture un pointeur peut être plus petit (EDIT: peu probable), ou plus grand (EDIT: cf adressage 64 bits...) qu'un int en mémoire
    Je compare pour une architecture donnée donc si .. il suffit de connaitre la taille d'un pointeur et d'un int. Pour moi, une allocation via un pointeur va allouer un espace pour ce pointeur + un espace pour la valeur stockée.

    Citation Envoyé par therwald Voir le message
    tu ne peux pas comparer non plus un vector seulement réservé (ton cas dit "sur la pile") avec un vector réservé ET rempli (ton cas dit "sur le heap")
    pourquoi ?


    Citation Envoyé par phi1981 Voir le message
    Il faut savoir que malloc() ne dit pas qu'il alloue exactement la taille demandée. Beaucoup d'implémentations allouent un peu plus, ne serait-ce que pour du "book-keeping" ou prévoir une réallocation ultérieure.
    Faire 1 millions d'allocations de 4 octets est assez suspect, ça ne correspond pas à ce que font la plupart des programmes, donc ça se comprend que la bibliothèque standard ne soit pas optimisée pour ce cas-là.

    J'ai une question toutefois : quel outil as tu utilisé pour obtenir les chiffres que tu donnes ?
    J'ai obtenu ces chiffres avec le gestionnaire des tâches de windows. Je comprends que le vector peut être plus gros pour du book keeping mais de là à avoir une taille 7 fois supérieur..

    Citation Envoyé par r0d Voir le message
    En revanche, et pour paraphraser therwald, stocker un int et un int* ce n'est pas la même chose. Un int* est un pointeur sur un int, il faut donc ajouter la mémoire utilisée pour le pointeur en lui-même.
    Comme dit dans mon premier message, ça ne m'étonnerait pas de retrouver la taille occupé en mémoire des pointeurs, cela doublerait l'espace occupé en mémoire (mes pointeurs font 4octets, un int faisant 4octets sous mon OS).
    Ce que je me demande, c'est que ce passe-t-il pour allouer 7 fois plus de mémoire qu'il n'en a besoin ?

  6. #6
    Membre Expert
    Homme Profil pro
    Inscrit en
    Décembre 2010
    Messages
    734
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Décembre 2010
    Messages : 734
    Par défaut
    Citation Envoyé par lovoo Voir le message
    Je compare pour une architecture donnée donc si .. il suffit de connaitre la taille d'un pointeur et d'un int. Pour moi, une allocation via un pointeur va allouer un espace pour ce pointeur + un espace pour la valeur stockée.
    +/- les différences dues aux stratégies d'alignement (entre autres). Je n'ai pas dit que c'est variable à architecture donnée, juste que ce n'est pas nécessairement aussi simple que tu le supposes.

    Citation Envoyé par lovoo Voir le message
    Citation Envoyé par therwald
    tu ne peux pas comparer non plus un vector seulement réservé (ton cas dit "sur la pile") avec un vector réservé ET rempli (ton cas dit "sur le heap")
    pourquoi ?
    Parce que le vector n'alloue pas forcément ajout par ajout pour des raisons d'optimisation, mais plutôt par blocs, donc le fait d'ajouter n éléments, surtout s'ils sont nombreux, change la taille du vector.

    Citation Envoyé par lovoo Voir le message
    J'ai obtenu ces chiffres avec le gestionnaire des tâches de windows. Je comprends que le vector peut être plus gros pour du book keeping mais de là à avoir une taille 7 fois supérieur..
    Et là tu as encore entre toi et ce qui se passe la stratégie du système dont on ne sait pas grand chose à part qu'il se donne le droit de préréserver pour éviter de passer sa vie à chercher des blocs de mémoire disponibles (et pour limiter la fragmentation), + les temps de mise à jour de l'affichage de gestionnaire de tâche qui font que tu n'as qu'un ordre de grandeur lissé dans le temps de la conso mémoire...

  7. #7
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Citation Envoyé par lovoo Voir le message
    Comme dit dans mon premier message, ça ne m'étonnerait pas de retrouver la taille occupé en mémoire des pointeurs, cela doublerait l'espace occupé en mémoire (mes pointeurs font 4octets, un int faisant 4octets sous mon OS).
    Ce que je me demande, c'est que ce passe-t-il pour allouer 7 fois plus de mémoire qu'il n'en a besoin ?
    Il y a aussi un surcoût quand compilé en debug (avec VS2012, ~2 fois plus de mémoire utilisées)

    Sinon (release, 64 bits)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void with_pointer(int size){
    	std::vector<int*> v(size);
    	system("PAUSE");
    } // -> 8200 Ko
     
    void without_pointer(int size){
    	std::vector<int> v(size);
    	system("PAUSE");
    } // -> 4288 Ko
    Il est peut être impossible de réserver seulement 4 octets sur le tas, d'où le surcoût.

    edit: ça semble être le cas
    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
    #include <iostream>
    #include <vector>
     
    template <class T>
    void alloc(int size) {
    	std::vector<T*> v(size);
    	for(int i = 0; i < size; ++i){
    		v[i] = new T();
    	}
    	system("PAUSE");
    }
     
    int main(int argc, char* argv[]){
    	const int size = 1000000;
     
    	//alloc<char>(size); // 70 820 Ko
    	alloc<double>(size); // 70 824 Ko
     
    	return 0;
    }

  8. #8
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 395
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 395
    Par défaut
    Entre les histoires d'alignement et celles de bookkeeping, new int() bouffera bien plus de quatre octets. Et encore plus en Debug, avec les gardes au début et à la fin de chaque zone allouée...

    Voici un code testé sous Visual Studio 2010:
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #include <iostream>
     
    inline void      * to_void(void      * p) { return p; }
    inline void const* to_void(void const* p) { return p; }
     
    int main(void)
    {
    	int *p1 = new int;
    	int *p2 = new int;
    	ptrdiff_t diff = reinterpret_cast<char*>(p2) - reinterpret_cast<char*>(p1);
    	std::cout << "p1: " << to_void(p1) << " - p2: " << to_void(p2) << " - diff: " << diff << " bytes" << std::endl;
    	delete p1, p1=NULL;
    	delete p2, p2=NULL;
    	return 0;
    }
    Code X, sortie Debug : Sélectionner tout - Visualiser dans une fenêtre à part
    p1: 001B1F30 - p2: 001B1F60 - diff: 48 bytes
    Code X, sortie Release : Sélectionner tout - Visualiser dans une fenêtre à part
    p1: 00311EB0 - p2: 00311EC0 - diff: 16 bytes
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  9. #9
    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,
    Citation Envoyé par Iradrille Voir le message
    Il y a aussi un surcoût quand compilé en debug (avec VS2012, ~2 fois plus de mémoire utilisées)

    Sinon (release, 64 bits)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    void with_pointer(int size){
    	std::vector<int*> v(size);
    	system("PAUSE");
    } // -> 8200 Ko
     
    void without_pointer(int size){
    	std::vector<int> v(size);
    	system("PAUSE");
    } // -> 4288 Ko
    Il est peut être impossible de réserver seulement 4 octets sur le tas, d'où le surcoût.
    C'est, surtout, parce qu'un int est, classiquement (bien que l'on pourrait trouver une architecture sur laquelle ce ne soit pas le cas ) codé sur 32 bits (4 bytes), alors qu'un pointeur ne l'est pas forcément vu qu'il doit être codé sur une taille qui permette de représenter "au minimum l'ensemble des adresses mémoire accessible".

    sur une architecture 64 bits, la taille d'un pointeur est souvent (bien qu'il me semble qu'elle soit un peu inférieur sous MacOs ) de... 64 bits, soit 8 bytes.

    Après, la somme de l'espace mémoire réservé pour les différents éléments + (dans le cas d'un pointeur) l'espace mémoire utilisé par les éléments pointés + l'espace mémoire utilisé par la structure interne du tableau ainsi que l'endroit où sont placés les éléments (pile vs tas) est strictement dépendant de l'implémentation.

    On pourrait ainsi parfaitement envisager une implémentation qui calculerait la taille de l'espace mémoire nécessaire pour représenter les différents éléments (+, pourquoi pas la taille du tableau) et qui déciderait d'utiliser la pile ou le tas en fonction de cette valeur.

    En effet, une structure proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    template <typename Type, int nb>
    struct StackHolded{
        enum{ size= nb};
        T data[size];
    };
    pourrait parfaitement être utilisée tant que l'espace mémoire total utilisé est inférieur à, mettons, 140 (bytes).

    Le compilateur ne se plaindra jamais de ne pas pouvoir utiliser size car les énumérations sont des constantes de compilation.

    Et l'on pourrait parfaitement utiliser sizeof(Type)* nb pour évaluer l'espace mémoire nécessaire (et donc savoir s'il est inférieur ou non à 140 selon l'exemple) parce que le résultat de sizeof est lui aussi une constante de compilation

    Et l'on pourrait même parfaitement envisager de faire en sorte que size soit directement inclus dans l'array (au début ou à la fin, selon notre bon vouloir) dés le moment où la taille d'un size_t est un multiple de la taille de Type (autrement dit, pour char, short int et size_t et similaire)

    Mais ca, c'est du seul ressort du développeur de la classe vector
    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. JQuery différence entre déclaration
    Par Pidev1302 dans le forum jQuery
    Réponses: 3
    Dernier message: 05/10/2014, 19h11
  2. Différences entre déclarations avec Set et LinkedHashSet
    Par Sinakhine dans le forum Débuter avec Java
    Réponses: 1
    Dernier message: 22/10/2012, 00h16
  3. Différence entre public static
    Par moooona dans le forum Débuter avec Java
    Réponses: 1
    Dernier message: 24/05/2008, 15h23
  4. Réponses: 2
    Dernier message: 16/12/2007, 01h35
  5. Quelle est la différence entre ces deux déclarations ?
    Par sidahmed dans le forum Débuter
    Réponses: 15
    Dernier message: 04/10/2007, 19h59

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