Speicherverbrauch shared_ptr und normaler *


  • Mod

    Kann's sein, dass deine Objekte miteinander in einer selbstgeschriebenen baum- oder listenartigen Struktur organisiert sind, welche den Besitz verwaltet, während die map nur ein unbesitzendes mapping von Strings auf die Elemente ist? Ich weiß, du sagst etwas anderes, aber jedes Details, das du uns zeigst, deutet darauf hin, dass die Map nicht der Besitzer der Daten ist. Der Besitzer von Objekten ist in aller Regel die Struktur, in der sie organisiert sind¹, nicht irgendetwas, das auf sie verweist.

    ¹: Das muss nicht notwendigerweise die Gesamtstruktur sein. Beispielsweise kann bei einer Baumstruktur entweder der Gesamtbaum der Besitzer sein, aber man kann es auch sehr elegant so formulieren, dass jeweils ein Knoten der Besitzer seiner Unterknoten ist.



  • @SeppJ

    Kann's sein, dass deine Objekte miteinander in einer selbstgeschriebenen baum- oder listenartigen Struktur organisiert sind?

    Ja, ist so. Mit map als Besitzer meine ich die maps der einzelnen Objekte.

    Aber ich verstehe noch nicht ganz was was die Erkenntnis nun helfen sollte Speicher zu sparen. Ob die Objekte nun flach oder als Baumstruktur dargestellt sind.


  • Mod

    Primäres Ziel sollte erst einmal sein, die Korrektheit deiner Datenstrukturen sicher zu stellen. Sowohl vom grundlegenden Design, als auch von der technischen Umsetzung her. Beides scheint derzeit ein Problem bei dir zu sein.

    Sehr wahrscheinlich lösen sich damit dann auch deine Speicherprobleme, denn wie schon angemerkt wurde, sind deine Beschreibungen inkonsistent. Wenn shared_ptr bei dir 80% Overhead verursacht, dann können deine Objekte nicht sehr groß sein. Wenn deine Objekte nicht sehr groß sind, können ein paar Millionen davon nicht die 2GB knacken. Das heißt, da passieren in deinem Programm andere Dinge, als du denkst. Sprich: Es ist jetzt höchstwahrscheinlich fehlerhaft. Und alles was du über deine Datenorganisation erzählst deutet darauf hin, dass der Fehler (oder wenigstens einer davon) beim Design deiner Datenstrukturen liegt.



  • Beides scheint derzeit ein Problem bei dir zu sein?

    Das siehts du so, ich nicht.

    Die Struktur wurde korrekt aufgebaut. Ich arbeite mich nur gerade mit den shared_ptr ein. Und bin auf der Suche nach dem Problem. Wenn vorher 1,2GB raus kam und ich nur auf shared_ptr umgestellt habe liegt das Problem nahe dass es an den shared_ptr liegt. Darum habe ich nach dem Speicherverbrauch eines shared_ptrs gefragt.

    Ich kann hier nicht die komplette Architektur offen legen und habe halt versucht in Ansätzen zu beschreiben. Nur weil ich nicht präzise genug auf deine Fragen antworten kann heißt das doch lange noch nicht dass ich Probleme mit dem grundlegenden Design habe. 😡

    Sind deine Beschreibungen inkonsistent!

    Wo nur weil ich mich in der Anzahl der Variablen geirrt habe. Tut mir leid war nur aus der Erinnerung heraus. Kann gerade nicht nachschauen.



  • Ich fürchte, dass Sepps Einschätzung nicht ganz falsch ist.

    Was wird in den Objekten alles gespeichert? Also, wie groß kann ein Objekt sein? Als Abschätzung, wie viel Speicher du benötigst.

    Weiterhin: Smart Pointer nur da einsetzen, wo Besitz gefragt ist. Wenn kein Besitz benötigt wird, reicht ein Raw Pointer.

    Zu deiner Frage,

    TcAdsSymbolInfo* FindSymbol(const string& name) const 
    { 
         const auto pos = _infoMap.find(name);
    
         if (pos != _infoMap.end()) 
         { 
             return pos->second.get(); //wenn's denn ein unique_ptr ist 
         } 
    
         for (const auto& info : _infoMap) 
         { 
                 auto symbol = info.second->SubSymbols->FindSymbol(name); //sollte dann auch ein raw pointer zurück geben
             if (symbol != nullptr) 
             { 
                 return symbol; 
             } 
         } 
    
         return nullptr; 
    }
    

  • Mod

    booster schrieb:

    Die Struktur wurde korrekt aufgebaut.

    Selbst, wenn man dir das mal glaubt (trotz der zahllosen Hinweise dagegen), bleibt doch zusammengefasst: Du möchtest Tipps zur Optimierung einer Architektur, die du aber nicht genau beschreiben kannst/willst. Die Teilbeschreibungen, die du uns gegeben hast, sind allesamt inkonsistent/falsch/ungenau gewesen, so dass jeder Tipp, den wir dir zu einer dieser Teilbeschreibungen gegeben haben, jeweils unpassend war. Wie soll man dir da helfen? Optimierung setzt voraus, dass man das System kennt, nicht ein System, das der Autor für irgendwie ähnlich hält.

    Du brauchst auch nicht sauer zu werden, wenn dir jemand sagt, dass deine Beschreibungen inkonsistent sind. Das ist einfach ein Fakt, dass sie das sind und das weißt du auch selber. Es ist unser gutes Recht, uns darüber zu beschweren, denn dadurch waren alle bisherigen Hilfsbemühungen vergebene Arbeit, und das ist deine Schuld, dass deine bisherigen Beschreibungen nichts taugten. Du willst hier die Hilfe haben, du machst es uns unnötig schwer, dir zu helfen. Alles was wir von deinem Projekt kennen, ist das, was du hier beschreibst. Du magst überzeugt sein, dass deine Architektur korrekt ist, aber wenn du sie hier inkorrekt beschreibst, dann sieht das für uns natürlich so aus, als wäre es falsch und dann bleibt uns nichts anderes übrig, als dich darauf aufmerksam zu machen. Wir können keine offensichtlich falsche Architektur optimieren.



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


Anmelden zum Antworten