Speicherverbrauch shared_ptr und normaler *



  • 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 😉



  • booster schrieb:

    ok aber anstelle meiner shared_ptr für parent usw. könnte ich ja dann wie erwähnt weak_ptr benutzen.

    Ja, weil du sonst geschlossene Referenz-Kreise baust. Das ist der Grund, weshalb es weak_ptr gibt.

    Und auch als Rückgabe meiner find Methode oder?

    Wozu soll das gut sein? Die Map hält einen shared_ptr, find erzeugt einen weak und reicht den raus, und der Empfänger muss erst mal wieder einen shared draus machen, um damit was anfangen zu können?



  • @printe
    du teilst also jedes mal wenn du einen pointer raus gibst gleich die kompletten Besitzverhältnisse?
    Ein shared_ptr ist nicht einfach nur mal eben einen counter incrementieren. Da steckt ein gewisser sync overhead drin, der nicht zu verarchten ist.



  • OT

    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.

    Diese Kritik hört man oft.

    Lustigerweise habe ich in 20 Jahren Arbeiten mit C++ mehr Probleme erlebt die dadurch verursacht wurden dass auf shared_ptr verzichtet wurde als Probleme die von übertriebenem shared_ptr Einsatz kamen.

    Und jedes mal wenn ich "keine Gedanken um Ownership zu machen" als quasi Argument gegen shared_ptr lese denk' ich mir meinen Teil.

    /OT



  • ps:

    booster schrieb:

    4. Ist eine std::map wirklich der richtige Container für deinen Usecase?
    Ich benötige die map mit einem string als Key um nach bestimmten Objekten zu suchen. Denke schon, das hier map das richtige ist.

    Alternativen könnten sein:
    std::unordered_map
    boost::flat_map


Anmelden zum Antworten