Speicherverbrauch shared_ptr und normaler *



  • Darf ich nochmals ergänzen

    Meine Klasse beinhaltet 2 Maps. Eine TreeCollection und eine FlatCollection.
    Eine TreeCollection besitzt nur die Parent Elemente. Die Parentelemente wiederum die Kinderelemente

    Die FlatCollection beinhaltet alle Symbole als flache Struktur. Man konnte hier nun zwar nur für die Rückgabe die FlatCollection aus der TreeCollection extrahieren. Aber es ist Performanter wenn wir beim Laden beide Collections erzeugen und später nicht immer extrahieren müssen.

    Speicher ich nun in der TreeCollection die einzelnen Elemente als unique_ptr und in der FlatCollection dann den Rohzeiger?
    Hätte jetzt eher gesagt in beiden einen shared_ptr.

    Als Rückgabe meiner Find Methode kann ich dann das Element dann eventuell als Rohzeiger zurückgeben.



  • Wenn du die FlatCollection nur aus Perfomance Gründen verwendest und die TreeCollection sowieso immer hast, würde ich Unique und Raw Pointer verwenden.
    Wenn es sein kann, das du die TreeCollection mal nicht mehr brauchst/oder zerstört werden kann, du aber auf den FlatCollection Elementen noch arbeiten möchtest (und vice versa) dann ist der Shared Pointer angebracht.



  • @Schlangenmensch

    Ok verstanden. Habe nun viel über smart Pointer gelernt. Dachte bisher die shared_ptr sind eine komfortable Art dass man die Speicherverwaltung nicht selbst übernehmen muss.

    Arbeite größtenteils in c#. Dachte einfach wenn ich alle Pointer auf shared_ptr umstelle habe ich selbes verhalten. Aber wie ich nun gelernt habe nicht stuhr auf shared_ptr umstellen sondern die Besitzfrage klären.

    Ob das mein Speicherverhalten löst bleibt noch abzuwarten.



  • DANKE!



  • Es ist natürlich schön und gut, sich Gedanken zu machen, nur wenn sich der Datensatz noch einmal vergrößern sollte, stehst du wieder vor Problemen. Techniken wie lazy loading etc. könnten hier helfen (wir wissen ja nicht, wieviele Daten überhaupt durchschnittlich benötigt werden).
    Daher frage ich mal kurz und direkt: Es ist 2018, entsprechende Hardware weit verbreitet, warum war eine 64-Bit-Kompilierung noch nicht das Thema?



  • Es ist 2018, entsprechende Hardware weit verbreitet, warum war eine 64-Bit-Kompilierung noch nicht das Thema?

    Ohja.... das mag schon sein. In der Industrie ist das nicht immer ganz so einfach.

    Wenn wir hier 100 Maschinen in der Produktion haben die noch mit alter Hardware und Windows XP 32 bit laufen und ohne Probleme produzieren. Und ich "nur" meine Software etwas optimieren oder auch erweiteren will stellt sich immer die Frage:
    "Wer zahlt den Umbau auf aktuelle Hardware"



  • booster schrieb:

    Windows XP 32 bit

    Es gibt auch Large Address Aware, wenn dieses LAA-Flag im PE-Header gesetzt ist, kann das Programm bis zu 3GB Speicher verwalten.

    Der Organisationsafwand ist natürlich am geringsten, wenn komplett alles sofort in den Speicher geladen wird - ob das aber wirklich nötig ist, kannst nur du selber beurteilen...



  • Schlangenmensch schrieb:

    Wenn du die FlatCollection nur aus Perfomance Gründen verwendest und die TreeCollection sowieso immer hast, würde ich Unique und Raw Pointer verwenden.

    Wenn das das Szenario ist (die Tree-Collection ist Besitzer von allem und nur sie kann zerstören), dann braucht man auch die Unique-Pointer nicht, die helfen dann kein Stück weiter. Dann kann man alles mit Raw-Pointer machen und mit dem magischen Druidenwissen, dass alle, die nicht die Tree-Collection sind, nichts von dem zerstören dürfen, was sie anfordern.

    Das ist doch gerade der Gewinn der Smartpointer (unique, shared und weak), dass so ein Geheimwissen eben nicht mehr nötig ist.



  • @Printe

    Verstehe deine Antwort noch nicht.

    Du sagst im ersten Satz: "die helfen kein Stück weiter"

    und im zweiten Satz:

    "Das ist doch gerade der Gewinn der Smartpointer (unique, shared und weak), dass so ein Geheimwissen eben nicht mehr nötig ist."



  • Printe schrieb:

    Schlangenmensch schrieb:

    Wenn du die FlatCollection nur aus Perfomance Gründen verwendest und die TreeCollection sowieso immer hast, würde ich Unique und Raw Pointer verwenden.

    Wenn das das Szenario ist (die Tree-Collection ist Besitzer von allem und nur sie kann zerstören), dann braucht man auch die Unique-Pointer nicht, die helfen dann kein Stück weiter. Dann kann man alles mit Raw-Pointer machen und mit dem magischen Druidenwissen, dass alle, die nicht die Tree-Collection sind, nichts von dem zerstören dürfen, was sie anfordern.

    Das ist doch gerade der Gewinn der Smartpointer (unique, shared und weak), dass so ein Geheimwissen eben nicht mehr nötig ist.

    Ganz genau, es gibt kein Geheimwissen darüber, wer was zerstören kann.
    Besitzende Pointer sind immer Smart Pointer (in der Regel Unique oder Shared). Die müssen nicht von Hand freigegeben werden, sondern das erledigt der Destruktor für einen.
    Der Speicherbereich, auf den ein Raw Pointer zeigt wird nie freigegeben! Weil der nichts besitzt. Alles ganz einfach, kein Geheimwissen, keine Magie und nicht anfällig für Memory Leaks.



  • booster schrieb:

    @Printe

    Verstehe deine Antwort noch nicht.
    Du sagst im ersten Satz: "die helfen kein Stück weiter"

    und im zweiten Satz:

    "Das ist doch gerade der Gewinn der Smartpointer (unique, shared und weak), dass so ein Geheimwissen eben nicht mehr nötig ist."

    OK, dann versuche ich es anders auszudrücken.

    In einem Szenario, in dem man unique-Pointer in einem Container sammelt und auf Anforderung jedem Willi einen Raw-Pointer in die Hand drückt (so dass die unique-Pointer gerade nicht mehr unique sind), sind die unique-Pointer Unsinn. Sie täuschen eine Unique-ness vor, die gar nicht existiert. unique_ptr::get ist eine Funktion, die man nur mit größter Vorsicht einsetzen sollte, weil sie das Lebenszeit-Management komplett aushebeln kann.

    Was hier existiert, ist Object-sharing, und darum wären shared-ptr (und ggfs. weak_ptr) angebracht.

    Richtig wären unique-Pointer dann, wenn jedes Objekt, das angefordert wird, aus dem Container entfernt und dem neuen Besitzer exklusiv übergeben würde. Wenn man Objekte mit unique-Pointern verwaltet, dann kann es zu einer Zeit nur einen Besitzer geben. Wenn das nicht zum restlichen Design passt, dann sind unique_ptr das falsche Mittel, so einfach ist das.



  • @Printee

    Macht Sinn. Würde dann aber für mich bedeuten doch wieder shared_ptr?


  • Mod

    booster schrieb:

    Macht Sinn. Würde dann aber für mich bedeuten doch wieder shared_ptr?

    Du musst aber aufpassen, wer ein geteilter Besitzer ist und wer nicht, anstatt blind alle Verweise auf shared_ptr zu ändern. Wenn beispielsweise ein Kindknoten eine Referenz auf seinen Vorgänger hat ist das (bei üblichen Baumstrukturen) eben nur das, eine Referenz ohne Besitz. Siehe den Kommentar über weak_ptr weiter oben im Thread.



  • Es geht hier um den Besitz von Ressourcen. Das Objekte wirklich eine Ressource gleichwertig teilen, kommt doch eher selten vor. Um auf einer Ressource zu arbeiten muss ich sie aber nicht besitzen! Das sicher wirklich der Besitzer eine Ressource ändert, kommt meiner Erfahrung nach, eher selten vor.

    Generell shared pointer zu verwenden, schön Referenzen mitzählen und sich halt keine Gedanken um Ownership zu machen ist ein einfacher Weg aber kein schöner.
    In aller Regel will man aus einem Objekt auch nichts raus geben, was den Besitz an einer internen Ressource übernimmt oder daran teilhat. Das das vom Design benötigt wird, kommt doch eher selten vor.

    Der sauberste Weg hier, wäre wahrscheinlich ein Iterator auf das Map Element rauszugeben, ansatt eines raw oder shared ptr. Als pointer auf einen Parent bleibe ich dabei, dass hier ein Raw Pointer das mittel der Wahl ist und wenn ich ein Child besitze ist das in aller Regel ein unique_ptr!



  • booster schrieb:

    Doch etwas komplizierter. Es ist keine Flache map. Sondern die gespeicherten Objekte haben wiederum eine map mit Objekten gleichen typs. Hier wiederum benötige ich den Zeiger auf das Elternelement.

    D.h. du baust shared_ptr Zyklen. shared_ptr kann aber keine Zyklen freigeben. => Leak.

    EDIT: OK, das wurde ja schon geschrieben.



  • ok aber anstelle meiner shared_ptr für parent usw. könnte ich ja dann wie erwähnt weak_ptr benutzen. Und auch als Rückgabe meiner find Methode oder?



  • Vorschlag: Ownership per vector<unique_ptr<T>> . Dazu die ganzen Maps mit einfach T* als Wert.
    Done.

    Also quasi

    class MyBagOfThings {
        // ...
    private:
        std::vector<std::unique_ptr<Thing>> m_things;
        std::map<Key, Thing*> m_index;
    };
    

    Oder übersehe ich was?



  • Schlangenmensch schrieb:

    Generell shared pointer zu verwenden, schön Referenzen mitzählen und sich halt keine Gedanken um Ownership zu machen ist ein einfacher Weg aber kein schöner.

    ... sagt wer? Ich finde einfache Wege per se schön. So schön, dass ich nach komplizierteren erst bei eindeutig festgestelltem Bedarf suche.

    Der sauberste Weg hier, wäre wahrscheinlich ein Iterator auf das Map Element rauszugeben ...

    Nein. Es ist sogar äußerst unsauber, jemandem, der lediglich ein Objekt anfragt, einen Iterator zu schenken, denn:
    1. will derjenige gar nicht den Iterator, sondern nur das, was in iterator->second steckt.
    2. geht es ihn nichts an, dass es sich um eine Map handelt. Gibt man dieses Wissen öffentlich bekannt, kann man den Container nicht mehr ohne weiteres ändern.
    3. bekommt derjenige damit die Macht, nicht nur das gewünschte Objekt abzuholen, sondern Stück für Stück den ganzen Containerinhalt.



  • booster schrieb:

    ok aber anstelle meiner shared_ptr für parent usw. könnte ich ja dann wie erwähnt weak_ptr benutzen. Und auch als Rückgabe meiner find Methode oder?

    Wenn du mit weak_ptr arbeitest muss du halt immer mit lock() ein shared ptr erzeugen, wenn du mit dem Pointer arbeitest.



  • Um es noch einmal zusammenzufassen:
    Es gibt mehrere gangbare Wege. Welcher davon der richtige und für deinen Zweck beste ist, musst du selbst entscheiden, da wir deinen Anwendungsfall nicht genug kennen. Im übrigen stellen wir deswegen einfach mal die gesamte Datenstruktur in Frage 😉


Anmelden zum Antworten