Speicherverbrauch shared_ptr und normaler *



  • Der shared_ptr selbst braucht typischerweise doppelt so viel wie ein roher Zeiger.

    Und pro Pointee der über shared_ptr verwaltet wird kommt nochmal was dazu. Wie viel genau hängt von etlichen Faktoren ab, ich würde aber mal mit der Grössenordnung 4x Roher-Zeiger rechnen.


  • Mod

    hustbaer schrieb:

    Der shared_ptr selbst braucht typischerweise doppelt so viel wie ein roher Zeiger.

    Und pro Pointee der über shared_ptr verwaltet wird kommt nochmal was dazu. Wie viel genau hängt von etlichen Faktoren ab, ich würde aber mal mit der Grössenordnung 4x Roher-Zeiger rechnen.

    Wobei das natürlich gute Gründe hat, nämlich damit der shared_ptr das machen kann, was er macht. Im Prinzip gibt es hier drei Möglichkeiten:
    1. shared_ptr ist unpassend.
    2. shared_ptr ist passend und man kann nichts besseres tun, weil alles so notwendig ist.
    3. shared_ptr ist zwar passend, aber man kann eine kreative Lösung finden, dass man mit Trickserei doch noch Speicher spart, weil man irgendeine weitere Eigenschaft der Anforderungen ausnutzt.

    Das der TE noch nicht auf die Gegenfragen geantwortet hat, kann man nichts weiter dazu sagen.



  • Ja klar ist der Speicherverbrauch OK. Und klar hast du Recht dass es gut wäre zu wissen was der OP überhaupt macht und wieso er überhaupt shared_ptr verwenden will.

    Dachte mir nur ich könnte die Frage zur Abwechslung auch einfach mal beantworten 🙂


  • Mod

    Das richtete sich weniger an dich als an den Threadersteller, damit er versteht, warum man seine Frage nicht so gut beantworten kann, solange er nicht mit mehr Details raus rückt.



  • Hallo

    Versuche mal die Fragen zu beantworten.
    1. Wer ist der Besitzer der Objekte?
    Objekte werden von einer Factory gespeichert. Und in der map abgelegt. Map ist Besitzer.

    2. Warum std::shared_ptr und nicht std::unique_ptr?
    Benötigt unique_ptr weniger speicher als shared_ptr?

    3.Brauchst du wirklich alle Objekte dauerhaft?
    Eigentlich schon, aber zumindest einmal beim Laden. Aber ich bekomme ja nicht alle geladen. Stürzt ja vorher ab.

    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.

    5. Wieviele Objekte sind das denn? Tausende? Millionen?
    3 bis 4 hunderttausend -> schwankend

    6. Wieso er überhaupt shared_ptr verwenden will?
    `Na für den Komfort. Man kann ja eigentlich das ganze immer mit einem Rohzeiger machen. Man muss sich dann halt selber um das allokieren und aufräumen kümmern.

    Oder wann sollte man auf den shared_ptr verzichten?`


  • Mod

    booster schrieb:

    Versuche mal die Fragen zu beantworten.
    1. Wer ist der Besitzer der Objekte?
    Objekte werden von einer Factory gespeichert. Und in der map abgelegt. Map ist Besitzer.

    Eindeutiger Besitzer -> unique_ptr.

    booster schrieb:

    2. Warum std::shared_ptr und nicht std::unique_ptr?
    Benötigt unique_ptr weniger speicher als shared_ptr?

    Auch. Aber in erster Linie macht er das, was du willst (map ist Besitzer der Objekte), shared_ptr hingegen nicht.



  • unique_ptr geht nicht.

    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.

    Und eine Kopie von unique_ptr geht nicht.

    Es wird hier die Variablenstruktur einer SPS dargestellt.



  • booster schrieb:

    5. Wieviele Objekte sind das denn? Tausende? Millionen?
    3 bis 4 hunderttausend -> schwankend

    Wenn du 400.000 Objekte erzeugst, die zusammen 2 GB Speicher fressen, dann ist jedes Objekt ungefähr 10.700 Bytes groß. Auf die paar Bytes für den Smartpointer kommt es dabei nicht an.

    10 KB um eine SPS-Variable darzustellen kommt mir enorm viel vor. Schleppen die Objekte die gesamte Benutzerdoku mit sich herum oder wie kommt das?



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

    shared_ptr im Kreis? Blöde Idee. Wann wird der Speicher wohl freigegeben?

    Benutze std::uniqe_ptr in der Map und Referenzen oder Pointer für "das Elternelement".



  • booster schrieb:

    unique_ptr geht nicht.

    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.

    Und eine Kopie von unique_ptr geht nicht.

    Es wird hier die Variablenstruktur einer SPS dargestellt.

    Als Zeiger auf das Elternelement kannst du ruhig einen Raw Pointer verwenden. In der Regel will man ja keinen Besitz am Parent ausdrücken oder haben.
    Wo du den Unique Ptr kopieren willst, sehe ich auch nocht nicht.

    Wenn die map die Objekte besitzt, könnte man auch überlegen, ob man auf die Pointer da nicht ganz verzichtet und die Objekte halt direkt von der Map verwalten lässt.

    Aber all das hilft dir nicht, den benötigten Speicher dramatisch zu reduzieren. Ebenso kann ich mir nicht vorstellen, dass du durch die Umstellung von Raw pointer auf Shared Ptr einen Mehrbedarf an Speicher von 80% hast. Möglicherweise hältst du jetzt Objekte am Leben, weil noch ein Shared Pointer darauf zeigt, die du früher schon zerstört hast (und möglicherweise Pointer vorgehalten hast, die ins Nirwana zeigen).

    Und, bad_alloc bei 2GB? Auf was für einem System arbeitest du?



  • @Printe:
    Sorry. Falsch. Durch deinen Hinweis habe ich nochmals kontrolliert sind über ne Million.

    @manni66:
    Der Speicher wird freigegeben wenn die map geleert wird!?

    Du meinst unique_ptr in der map und in den einzelnen Objekten dann shared_ptr auf parent, next usw.

    Und in den Klassen die von der map den zeiger anfordern? Dann muss ich ja von dem unique ptr eine Kopie zurück geben?



  • Nein, Manni meint, gar kein Shared Ptr. Shared Ptr heißt, geteilter Besitz, also das dass Objekt so lange existiert, wie einer der Pointer darauf zeigt. Und, dass willst du bei einer Kind -> Eltern Beziehung üblicherweise nicht.



  • booster schrieb:

    @Printe:
    Sorry. Falsch. Durch deinen Hinweis habe ich nochmals kontrolliert sind über ne Million.

    Und wenn schon - das sind immer noch 4 kB pro Objekt.

    1. Frage: Kann das sein? Sind deine Objekte derart groß? Falls nicht, liegt das Problem woanders. Evtl. hältst du irgendwo große Speichermengen fest und ahnst nichts davon - das ist das, was Schlangenmensch meinte.

    2. Frage: Falls das sein kann: Muss das auch so sein? Gibt es an SPS-Variablen so viel zu speichern? Oder kannst du Teile davon auf der Festplatte lassen und nur bei Bedarf nachladen? Irgendwelche beschreibenden Infos zum Beispiel?

    Du meinst unique_ptr in der map und in den einzelnen Objekten dann shared_ptr auf parent, next usw.

    Nein. Um Kreis-Referenzen mit shared_ptr zu vermeiden, gibt es als drittes Verwaltungsobjekt den weak_ptr. Nachlesen und schlau werden.

    Und in den Klassen die von der map den zeiger anfordern? Dann muss ich ja von dem unique ptr eine Kopie zurück geben?

    Du kannst auch mit unique_ptr::get einen Raw-Pointer aufs verwaltete Objekt holen. Der Anforderer darf nur nicht auf die Idee kommen, dieses Objekt freizugeben, er muss es nach Gebrauch einfach loslassen.

    Eigentlich ist das aber dreckig und unterläuft das mühsam eingeführte Objektmanagement gleich wieder.



  • booster schrieb:

    @manni66:
    Der Speicher wird freigegeben wenn die map geleert wird!?

    Vermutlich nein.

    booster schrieb:

    Du meinst unique_ptr in der map und in den einzelnen Objekten dann shared_ptr auf parent, next usw.

    Nein!
    Und wo kommt denn jetzt auf einmal next her?

    SeppJ schrieb:

    Das richtete sich weniger an dich als an den Threadersteller, damit er versteht, warum man seine Frage nicht so gut beantworten kann, solange er nicht mit mehr Details raus rückt.

    Dein Salamitaktik wird dir nicht helfen.



  • @Schlangenmensch
    Ok da gebe ich dir recht das ich intern mit Rohzeigern arbeiten kann.

    Ist allerdings etwas aufwendiger, als wenn ich immer mit shared_ptr arbeite.

    Aber gut. Die Frage ist aber dann immer noch die Rückgabe meiner find Methode()
    Die Aktuell so aussieht

    shared_ptr<TcAdsSymbolInfo> FindSymbol(const string& name) const
    {
        const auto pos = _infoMap.find(name);
    
        if (pos != _infoMap.end())
        {
            return pos->second;
        }
    
        for (const auto& info : _infoMap)
        {
            const auto symbol = info.second->SubSymbols->FindSymbol(name);
            if (symbol != nullptr)
            {
                return symbol;
            }
        }
    
        return nullptr;
    }
    

    wenn meine map nun einen unique ptr beinhaltet kann ich diesen ja so nicht zürck geben


  • 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; 
    }
    

Anmelden zum Antworten