Speicherplatzverschwendung/-verbrauch?



  • Hallo,
    also ich habe gerade mit (großen) Graphen zu tun. Naja und ich hab mir jetzt mal den Speicherverbrauch meines Programs im Taskmanager angeguckt und war etwas erstaunt.
    Als Fallbeispiel folgendes:
    100 Arrays der Größe 100.000, mit Zeigern. Also insgesamt 107 Zeiger die abzuspeichern wären. Pro Zeiger 4 Byte, weiß nicht genau ob bei 64bit Systemen die Zeiger 8 Byte groß sind, aber das wäre ja nur ein Faktor von 2. Das wären also insgesamt 40MB. Ich habe sonst nichts weiter an Objekten angelegt, die Zeiger sind alle NULL-Zeiger. Im Taskmanager werden mir aber !!220MB!! angezeigt.
    So nun die Frage, kann mir das jemand erklären?
    Danke schonmal im Voraus


  • Mod

    Wie allokierst Du das ganze. Wenn Du Zeiger speicherst, auf was zeigen die denn?
    Sind das wiedermit new allokiete Objekte?



  • also wie gesagt die Zeiger sind nur NULL-Pointer zeigen also auf keine Objekte, der Speicherplatz für die Zeiger wird aber mit malloc() allokiert. das hab ich deshalb gemacht, weil es da die realloc() funktion gibt um das array nachträglich vergrößern zu können, gibts ja für new() nicht soweit ich weiß.
    und da mir hier im forum mal gesagt wurde, dass es nicht festgelegt ist, das new und malloc im gleichen speicher (adressraum) arbeiten, kann man wohl auf new[]-zeiger kein realloc anwenden. was ich also habe ist sowas wie

    float ***a;
    a = (float***) malloc (100 * sizeof(float**));
    for(int i = 0; i < 100; i++)
    {
      a[i] = (float**) malloc (100000 * sizeof(float*));
      for(int j = 0; j < 100000; j++)
        a[i][j] = 0;
    };
    

    und dieses feld verbraucht an die 210MB, obwohl eigentlich nur 40MB zu veranschlagen wären, plus den programm-offset



  • ich seh gerade so wie es oben steht ist es noch ok, aber folgendes liefert mir sogar über 500MB

    float ***a;
    	a = (float***) malloc (100 * sizeof(float**));
    	for(int i = 0; i < nscc; i++)
    	{
    		a[i] = (float**) malloc (100000 * sizeof(float*));
    		for(int j = 0; j < n; j++)
    			a[i][j] = (float*) malloc (sizeof(float));
    	};
    

    obwohl es eigentlich nur 80MB veranschlagen dürfte



  • realloc heißt in C++ "std::vector<T>::resize"
    außerdem ist es logisch, dass dein Programm RAM und CPU frisst wie Sau, wenn du für jedes float eine Allokation ausführst (welche selber Zeit und Speicher-overhead kostet). Müsste in diesem (und jedem halbwegs populärem C++ -- ) Forum schon x-mal durchgekaut worden sein.



  • Du hast ein overhead pro Allokation:

    - Verwaltung des Heaps (variiert, kann im Debug-Build höher sein)
    - Alignment: malloc garantiert, daß der zurückgegebene Zeiger korrektes alignment für alle Typen hat. Das sollten unter x86 16 Byte für die SSE-Typen sein (mindestebns 8 für die dem compiler bekannten)
    - Unter Debug-Builds kommen noch Guard Bytes hinzu

    So oder so ist es nicht sinnvoll, für jeden einzelnen float eine Allokation durchzuführen. Wieso machst du das?



  • das mit dem float war nur stellvertretend eigentlich ist es ein beliebiger typ.
    gut dann komme ich wohl nicht drumherum es noch genauer zu schildern.

    template<class t>
    class carray
    {
      long l;
      t **_data;
      //t = (t**) malloc (l * sizeof(t*));
      //t[i] = (t*) malloc (sizeof(t));
    };
    

    was ich nun habe ist etwas folgender art

    carray<carray<ttyp> a;
    

    das beispiel oben ist quasi für ttyp = float
    die doppelten zeiger haben den hintergrund, dass wenn ich zwei elemente im array vertauschen muss ich dann nur die zwei zeiger umhängen muss und mir somit das umkopieren eines evtl großen objektes sparen kann.
    ich dachte mir, dass das bei großen objekten relevant sein könnte.

    obiges beispiel hätte a die dimension 100x100000, wenn ich dann also zwei 100000er arrays vertauschen will müsste ich normal alles in eine dummyvariable kopieren usw. kennt man ja, so muss ich nur die zwei zeiger vertauschen.



  • achso, im Debugmodus hab ich ca 500MB und im Releasemodus ca 200MB Speicherverbrauch


  • Mod

    FreakyBKA schrieb:

    achso, im Debugmodus hab ich ca 500MB und im Releasemodus ca 200MB Speicherverbrauch

    Auch das ist logisch, denn im Debug-Modus wird noch gespeichert wer die Allokation durchgeführthat um Leaks leicht entdecken zu können.



  • die doppelten zeiger haben den hintergrund, dass wenn ich zwei elemente im array vertauschen muss ich dann nur die zwei zeiger umhängen muss und mir somit das umkopieren eines evtl großen objektes sparen kann.

    Klingt sinnvoll, aber damit bestrafst du viele Instanzen kleiner Typen ziemlich heftig.

    Gibt es einen Grund, daß du kein C++ verwendest?

    Du kämst wahrscheinlich insgesamt besser, wenn du größere Objekte in einen smart pointer stopfst, oder zumindest eine "effiziente, non-throwing" swap-Implementaiton forderst.

    Alternativ kannst du kleine Objekte auch auf einem separaten heap anlegen. Wenn du den Graphen immer nur im ganzen wieder freigibst, würde ein non-freeing heap praktisch kein Overhead bedeuten.



  • Klingt sinnvoll, aber damit bestrafst du viele Instanzen kleiner Typen ziemlich heftig.

    richtig mir ist aber nichts eingefallen was ich sonst hätte machen können. bin für andere vorschläge sehr dankbar.

    Gibt es einen Grund, daß du kein C++ verwendest?

    was genau meinst du damit?

    Du kämst wahrscheinlich insgesamt besser, wenn du größere Objekte in einen smart pointer stopfst, oder zumindest eine "effiziente, non-throwing" swap-Implementaiton forderst.

    was sind smart-pointer? meinst du damit, dass ich dann als template-typ einen zeiger des template-typs verwende also:

    //statt
    carray<tobject>
    //lieber
    carray<tobject*>
    

    Alternativ kannst du kleine Objekte auch auf einem separaten heap anlegen. Wenn du den Graphen immer nur im ganzen wieder freigibst, würde ein non-freeing heap praktisch kein Overhead bedeuten.

    was ist ein non-freeing heap?



  • peterchen schrieb:

    - Alignment: malloc garantiert, daß der zurückgegebene Zeiger korrektes alignment für alle Typen hat. Das sollten unter x86 16 Byte für die SSE-Typen sein (mindestebns 8 für die dem compiler bekannten)

    Für alle Typen die reinpassen 🙂
    Also wenn man 1 Byte (=char) alloziert, dann muss die Adresse nicht geeignet sein, um nen int abzulegen (angenommen int hat > 1 Byte, was in C++ ja nicht sein muss).

    Heisst konkret: wenn der Allocator es kann (und es gibt einige die das können), dann kann man schon bei 1 Mio. Anforderungen von jeweils 1 Byte, mit einem Speicherverbrauch von 1 MB auskommen.


Anmelden zum Antworten