sizeof(int) 4 bytes, im Taskmanager ~17 bytes



  • Hallo,

    ich habe mich über den hohen Speicherverbrauchs meines Graphen gewundert, und angefangen rumzuprobieren.
    Einer der Tests war eine Millionen integer zu erstellen und zu gucken was der Taskmanager dazu sagt:

    for (int i = 0; i < 1000000; i++)
    {
    	new int();
    }
    

    ergibt im Taskmanager 17208 kb, obwohl

    std::cout<<sizeof(int)<<std::endl;
    

    4 ergibt.

    Vielleicht ein paar Infos die helfen könnten: Win32, Athlon64, kompiliertes Programm 8 kb groß

    Kann mir jemand erklären warum der Speicherverbrauch 4 mal so groß ist wie er sein sollte? 😕
    Zeigt der Taskmanager einfach Kappes an, wenn ja, könnt ihr mir etwas zum Speicher messen empfehlen?

    Vielen Dank im Voraus.



  • Halbiere doch mal die Anzahl der allokierten Integer und schau nach, wie groß das Programm dann ist. Ich prognostiziere, dass es nicht um die Hälfte schrumpft, sondern um ca. 2 MB


  • Administrator

    Naja, das System legt halt zu jedem allokiertem Speicher noch zusätzliche Informationen bei. Das dies bei kleinen Objekten meistens ein vielfaches ihrer eigenen Grösse ist, ist durchaus bekannt. Deshalb verwendet man in solchen Fällen oft auch eine eigene Speicherverwaltung. Man allokiert grössere Mengen an Speicher im voraus und verteilt diese dann selber.

    So ein Verfahren wird zum Beispiel in Modern C++ Design erklärt. Gibt aber sicher noch bessere Bücher für dieses Themengebiet, da man dazu eigentlich ein eigenes Buch schreiben könnte 🙂

    Damit du es mal selber siehst, mach sowas:

    #include <iostream>
    #include <algorithm>
    
    int main()
    {
      int* field = new int[1000000];
    
      std::fill_n(field, 1000000, 9);
    
      std::cin.get();
    
      delete[] field;
    
      return 0;
    }
    

    Du wirst bemerken, dass der Speicherverbrauch deutlich gesunken ist, obwohl genau gleichviele int Objekte angelegt wurden.

    Grüssli



  • Tatsache, 5452 kb laut Taskmanager.

    Vielen Dank, komme von Python und Java, daher ist mir Speicherverwaltung noch neu.

    Ich werde zusehen, dass ich mir etwas über Memory Allocation anlese. boost pool scheint sich da anzubieten, wenn ich das richtig sehe.



  • muuh schrieb:

    Ich werde zusehen, dass ich mir etwas über Memory Allocation anlese. boost pool scheint sich da anzubieten, wenn ich das richtig sehe.

    Meistens ist std::vector, std::list, etc der perfekte Pool 😉



  • Naja, was ich zur Zeit mache ist:

    std::list<Node*> nodes;
    
    Node* Graph::addNode(int x, int y, int z)
    {
        Node* node = new Node(x, y, z);
        nodes.push_back(node);
        return node;
    }
    

    Aber da ich Probleme mit dem Speicher bekomme scheints falsch zu sein.



  • Gibst du den Speicher wieder frei? Und wieso nimmst du keine std::list<Node> ?

    Brauchst du die Zeiger? Falls ja, bieten sich Smart Pointer und Pointer Container aus Boost an...



  • Ja ich gebe den Speicher wieder frei, aber wenn ich mich nicht irre wärs auch egal wenn nicht, denn zur Zeit lass ichs nur einmal durchlaufen.

    Die Zeiger brauche ich, Smart Pointer und Pointer Container gucke ich mir mal an.

    Ich habe keine std::list<Node> benutzt, weil ich mir nicht sicher war, ob so etwas richtig ist:

    std::list<Node> nodes;
    
    Node* Graph::addNode(int x, int y, int z)
    {
        nodes.push_back(*(new Node(x, y, z));
        return &(nodes.back());
    }
    

    Ich habe das Gefühl das ist nicht so gut...


  • Administrator

    muuh schrieb:

    Ja ich gebe den Speicher wieder frei, aber wenn ich mich nicht irre wärs auch egal wenn nicht, denn zur Zeit lass ichs nur einmal durchlaufen.

    Du irrst dich! Zu jedem new kommt ein delete und zu jedem new[] kommt ein delete[] . Du kannst dich nicht darauf verlassen, dass irgendjemand anderes deinen Speicher freigibt.

    muuh schrieb:

    Die Zeiger brauche ich, ...

    Keine sinnvolle Antwort.

    muuh schrieb:

    Ich habe keine std::list<Node> benutzt, weil ich mir nicht sicher war, ob so etwas richtig ist:

    ...
    

    Ich habe das Gefühl das ist nicht so gut...

    omg 😮
    Das ist wirklich nicht gut, aber zeigt schön deine Herkunft auf. In C++ muss man nicht jedes Objekt mit new anlegen. Dies ist nur nötig, wenn man es auf dem Heap haben will und die Speicherverwaltung übernimmt. Das folgende wäre vielleicht eher was du suchst:

    std::list<Node> nodes;
    
    // ...
    
    Node& Graph::addNode(int x, int y, int z)
    {
        nodes.push_back(Node(x, y, z));
        return nodes.back();
    }
    

    Ganz ehrlich, wenn du dich in Python oder Java auskennst, dann solltest du trotzdem ein C++ Buch kaufen. Die Sprachen sind VIEL ZU VERSCHIEDEN. Es ist ein Fehlschluss, wenn du meinst, dass du mit deinem Java Wissen auch C++ programmieren kannst. Das führt zu nichts!

    Empfohlene Bücher:
    - C++ Primer
    - Thinking in C++ 1&2
    - Die C++ Programmiersprache

    Grüssli



  • Naja, ich habe gelesen, dass der Heap viel größer ist als der Stack, und da ich vor habe über eine Millionen Knoten anzulegen, zu jedem Knoten mehrere Kanten anzulegen und dann zu durchsuchen... Nunja, ich dachte da sei es eine bessere Idee das auf den Heap zu packen.

    Ich werde mich mal um ein wenig Bücher zu c++ bemühen.

    Danke.


  • Administrator

    muuh schrieb:

    Naja, ich habe gelesen, dass der Heap viel größer ist als der Stack, und da ich vor habe über eine Millionen Knoten anzulegen, zu jedem Knoten mehrere Kanten anzulegen und dann zu durchsuchen... Nunja, ich dachte da sei es eine bessere Idee das auf den Heap zu packen.

    Die Sache ist aber, dass std::list einen Defaultallokator intern benutzt. Das std::list Objekt bleibt immer gleich gross, egal wieviele Objekte du reinstopfst, denn std::list setzt diese intern über den Defaultallokator auf den Heap. Aber sowas lernt man in einem guten C++ Buch 😉

    Grüssli



  • muuh schrieb:

    Naja, ich habe gelesen, dass der Heap viel größer ist als der Stack, und da ich vor habe über eine Millionen Knoten anzulegen, zu jedem Knoten mehrere Kanten anzulegen und dann zu durchsuchen... Nunja, ich dachte da sei es eine bessere Idee das auf den Heap zu packen.

    std::list und die anderen STL-Container verwenden alle intern den Heap, du bekommst davon einfach nichts mit und hast dich nicht ums Freigeben zu kümmern.

    Aber ich kann Dravere nur zustimmen: Die Konzepte von C++ sind in vieler Hinsicht sehr speziell. Ein Neu-Lernen bringt mehr als ein Umlernen, denn die Gemeinsamkeiten hören schon bald einmal auf...



  • muuh schrieb:

    Naja, ich habe gelesen, dass der Heap viel größer ist als der Stack, und da ich vor habe über eine Millionen Knoten anzulegen, zu jedem Knoten mehrere Kanten anzulegen und dann zu durchsuchen... Nunja, ich dachte da sei es eine bessere Idee das auf den Heap zu packen.

    Wenn du sowas schreibst:

    myList.push_back(Node(x,y,z));
    

    passiert folgendes:

    1. ein Node-Objekt wird auf dem Stack angelegt.
    2. die Memberfunktion push_back wird auf myList aufgerufen, ihr wird dieses Node-Objekt per Referenz übergeben
    3. push_back erzeugt einen Listenknoten auf dem Heap und kopiert das übergebene Node-Objekt dort rein
    4. das in Schritt 1 erzeugte Node-Objekt wird, da es ein temporäres Objekt ist, am Ende des Statements zerstört (sonst bei Verlassen des Scopes)

    es existiert jetzt nur noch ein Node als Teil eines Listenknotens auf dem Heap.



  • Advanced C++ hat nen schönes Beispiel mit Erklärung für einen Small-Object-Allocator.



  • Ah, das ist cool, danke für eure Hilfe.



  • Eine Frage hätte ich doch noch:

    Test 1:
    Eine Millionen Knoten in einen vector speichern.
    Knoten haben 6 integer (24 byte).
    Speicherverbrauch ~25 mb.

    Test 2:
    Eine Millionen Knoten in einen vector speichern.
    Knoten haben einen vector (24 byte).
    Speicherverbrauch ~41 mb.

    Woran liegt das?



  • Sehrwahrscheinlich liegt es daran, dass der std::vector ein wenig Speicher auf Vorrat allokiert, um besseres Laufzeitverhalten zu erzeugen. Bei vielen kleinen Containern macht das etwas aus.

    Um mehr zu sagen, müsstest du uns den Code zeigen und sagen, wieviel bei dir sizeof des jeweiligen Elementtypen ausgibt. Eventuell spielt Alignment auch noch eine Rolle.



  • Test 1:

    class Node
    {
    private:
    	int a;
    	int b;
    	int c;
    	int d;
    	int e;
    	int f;
    
    public:
    	Node();
    	~ Node();
    };
    
    Node:: Node()
    {
    
    }
    
    Node::~ Node()
    {
    
    }
    
    int main(int agc, char* argv[])
    {
    	std::vector< Node > field;
    	for (int i = 0; i < 1000000; i++)
    	{
    		field.push_back(Node());
    	}
    	std::cout<<sizeof(Node)<<std::endl;
    	std::cout<<sizeof(int)<<std::endl;
    	std::cin.get();
    	return 0;
    }
    

    Die Ausgabe ist:
    24
    4

    taskmanager: 25192 kb

    Und Test 2:

    class Node
    {
    private:
    	std::vector<int> myVec;
    
    public:
    	Node();
    	~ Node();
    };
    
    Node:: Node()
    {
    
    }
    
    Node::~ Node()
    {
    
    }
    
    int main(int agc, char* argv[])
    {
    	std::vector< Node > field;
    	for (int i = 0; i < 1000000; i++)
    	{
    		field.push_back(Knoten());
    	}
    	std::cout<<sizeof(Node)<<std::endl;
    	std::cout<<sizeof(std::vector<int>)<<std::endl;
    	std::cin.get();
    	return 0;
    }
    

    Die Ausgabe ist:
    24
    24

    Taskmanager: 40856 kb



  • Was denkst Du mit sizeof(std::vector<int>) zu messen?
    Simon



  • Den Speicher den myVec belegt.
    myVec.capacity() ist übrigens 0.


Log in to reply