Speicherverbrauch shared_ptr und normaler *



  • booster schrieb:

    @printee

    und um eine Klasse die zum einen eine Referenz auf die struct hat. Desweiteren eine map die wiederum die Kinder darstellen. Objekte der selben Klasse.

    hatte ich geschrieben.

    Ja, stimmt. Ein Bröckchen Information, das im Getöse untergegangen ist. Ein hingeworfener halber Satz wird nun mal leicht übersehen. Was lernen wir draus? Geduldig bei der Sache bleiben, Kritik ernst nehmen, Streit aus dem Weg gehen und die Informationen besser aufbereiten. Du bist es, der hier Hilfe will, vergiss das nicht.

    Ist es also so?

    struct ITcAdsSymbol
    {
    	//	struct-Members wie oben
    };
    
    class MyClass
    {
    	ITcAdsSymbol & symbol;
    
    	std::map<std::string, MyClass> children;
    };
    
    //	irgendwo ein systemweites Map-Objekt
    std::map<std::string, MyClass> parents;
    

    Was soll ich machen mein ganzes Programm posten und einfach sagen optimiert mal?

    Nein, denn das ist dein Job, nicht unserer.



  • booster schrieb:

    Die keys sind die Variablenamen der SPS. Und da gibt es so viele Keys wie variablen und das sind nun mal über eine Million.

    Sind die Variablen der SPS nummeriert oder bestehen sie nur aus dem Namen? Kannst du eine Lookup-Tabelle bauen, die den Namen zu einer ID liefert? Dann kannst du von std::map<std::string,ITcAdsSymbol> zu std::map<unsigned int,ITcAdsSymbol> wechseln, um doppelte strings zu vermeiden. Und wenn du den Namen einer Variablen brauchst suchst du ihn in der LUT.



  • Und verrat bitte endlich, wieviel Bytes Daten ursprünglich aus der SPS geholt werden. Damit man einschätzen kann, wieviel von diesen 1,2 GB echte Information ist und wieviel Redundanz.



  • DocShoe schrieb:

    Die Pragma Anweisung verhindert Padding, was zu kleineren Strukturgröße führt, aber den Zugriff auf die Member etwas verlangsamt.

    Auf den meisten Plattformen ist es von der Performance her quasi Wurst. Auf SPARC ist #pragma pack(1) die Hölle (tm).



  • Nein, denn das ist dein Job, nicht unserer.

    Also dann kann ich ja nur das Posten was ich im ersten Moment für richtig halte bzw. das worauf sich meine Konkrete Frage bezieht.

    Man wirft mir vor das ich zuwenig Informationen heraus gebe. Was sind zu wenig was sind zu viel?

    Und ich versuche sachlich zu bleiben, aber der seppl wirft mir ja immer wieder hin dass mein Datenmodel nicht passt nur weil ich nicht alles komplet offen gelegt habe.

    Und ich nehme Krtik ernst. Nur Beleidiguungen und pauschale Aussagen nehme ich nicht ernst.

    Und verrat bitte endlich, wieviel Bytes Daten ursprünglich aus der SPS geholt werden. Damit man einschätzen kann, wieviel von diesen 1,2 GB echte Information ist und wieviel Redundanz.

    Also die Daten der SPS sind wirklich sehr wenig. 3MB um genau zu sein.
    Das Problem ist der Objektbaum.

    Und der sieht im Prinzip so aus wie printe gezeigt hat.

    nur dass ich um die map nochmals ne wrapper Klasse gemacht habe in der ich die Findmethode implementiert habe,

    struct ITcAdsSymbol
    {
        //  struct-Members wie oben
    };
    
    class AdsSymbol
    {
        ITcAdsSymbol & symbol;
    
        shared_ptr<InfoCollection> _subsymbols;
    };
    
    class InfoCollection
    {
       map<string, shared_ptr<AdsSymbol>> _infoMap;
    
       shared_ptr<AdsSymbol> 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;
        }
    }
    
    //  das  erwähnte "systemweite Map-Objekt" ist in der Klasse Loader hinterlegt.
    // bzw es sind ja wie gesagt 2 collections. Flach und Baum.
    
    [code="cpp"]/// SymbolInfo Collection als Baum Struktur
    shared_ptr<InfoCollection> _treeCollection;
    
    /// SymbolInfo Collection als Flache Struktur
    shared_ptr<InfoCollection> _flatCollection;
    

    [/code]

    Der ganze Speicher wird natürlich verbraucht bei der Aufbereitung der Daten der SPS in die genannte Struktur.



  • Also wenn 3MB auf 1.2GB aufgebläht werden gibt es da sicher Optimierungspotenzial.

    Wie viele Elemente hat denn so eine map? Sind das eher 10 oder eher 100? Du hast ja schon bemerkt, dass die map overhead hat. Wenn ich dich richtig verstanden habe brauchst du die Daten nur zum Aufbauen anderer Daten, sie sind also read-only. Statt einer map könntest du auch einen sortierten vector nehmen und mit binärer Suche suchen, dann hast du einen vector mit wenig Overhead und trotzdem schnellen Zugriff. Auf smart pointer könntest du dann ebenfalls komplett verzichten und die Kindbäume mit rohen Zeigern befüllen. Das sollte einiges bringen.



  • Na also, endlich kommen die interessanten Informationen. Jetzt hab ich noch ein paar Verständnisfragen:

    1. Was verwendest du als Key? Das, was in ITcAdsSymbol::name steht?
    2. Der Unterschied zwischen der flatCollection und der treeCollection ist was genau? Das hast du bestimmt schon mal irgendwo erwähnt, aber vielleicht kannst du es, mit den Deklarationen vor der Nase, nochmal kurz auf den Punkt bringen?
    3. Ist jedes Symbol und jedes Subsymbol genau einmal in den Collections? Oder sind die mehrfach vorhanden? Wenn ja, nach welchen Regeln?
    4. AdsSymbol enthält eine Referenz auf ein ITcAdsSymbol-struct. Warum ist das eine Referenz, und wo befinden sich die tatsächlichen Struct-Daten? Die fehlen uns nämlich noch.



  • Hallo Printe

    1. Ja

    2. Die TreeCollection ist genau so aufgebaut wie die Variablenstruktur in der SPS. Also es gibt etwa 1000 Variablen auf oberster ebene. Alle anderen sind dann wiederum Kinder und Enkelkinder usw. also SubSymbols genannt.
    In der FlatCollection liegen alle Variablen bis in tiefster Ebene auf selber Ebene.

    3. Also jedes Symbol ist genau einmal vorhanden.

    Der Name (im Gegensatz zum short Name) ist aber zusammengesetzt.
    Also z.B.
    Oberste Varible heißt "MyFunctionBlock"
    Dann heißt die Variable darunter "MyFunctionBlock.MyStruct"
    und die wieder darunter
    "MyFunctionBlock.MyStruct.MyArray"

    4. Schreibfehler ist keine Referenz. Sorry das & ist zu viel.



  • booster schrieb:

    Der Name (im Gegensatz zum short Name) ist aber zusammengesetzt.
    Also z.B.
    Oberste Varible heißt "MyFunctionBlock"
    Dann heißt die Variable darunter "MyFunctionBlock.MyStruct"
    und die wieder darunter
    "MyFunctionBlock.MyStruct.MyArray"

    Kommen denn diese zusammengesetzten Namen original aus der SPS? Oder bildest du die in der Software, indem du auf jedem Weg durch den Baum die Namen aufsammelst und hintereinanderstellst?

    Verstehst du den Unterschied?

    Im zweiten Fall bildest du selber Namen wie "MyFunctionBlock.MyStruct.MyDings", "MyFunctionBlock.MyStruct.MyBums", "MyFunctionBlock.MyStruct.MyBlah" usw., die es in den Originaldaten so nicht gibt. Der Teil "MyFunctionBlock.MyStruct" wird dabei immer wieder dupliziert. Das bläht den Speicherbedarf schon merklich auf ...

    Außerdem hast du wegen flat und tree schon mal alles doppelt.


  • Mod

    booster schrieb:

    1. Ja

    Dann ist set die Datenstruktur, die du suchst, nicht map. => doppelte Strings vermieden

    booster schrieb:

    2. Die TreeCollection ist genau so aufgebaut wie die Variablenstruktur in der SPS. Also es gibt etwa 1000 Variablen auf oberster ebene. Alle anderen sind dann wiederum Kinder und Enkelkinder usw. also SubSymbols genannt.
    In der FlatCollection liegen alle Variablen bis in tiefster Ebene auf selber Ebene.

    Was heisst selbe Ebene? Es ist relativ einfach, einen Iterator zu schreiben, der den gesamten Baum durchlaufen kann. => Speicherverbrauch halbiert.

    booster schrieb:

    3. Also jedes Symbol ist genau einmal vorhanden.

    Der Name (im Gegensatz zum short Name) ist aber zusammengesetzt.
    Also z.B.
    Oberste Varible heißt "MyFunctionBlock"
    Dann heißt die Variable darunter "MyFunctionBlock.MyStruct"
    und die wieder darunter
    "MyFunctionBlock.MyStruct.MyArray"

    name ist einfach zusammengesetzt aus shortName der parents plus short Name des Elements, getrennt duch Punkte? Dann ist das völlig überflüssige Speicherverschendung. Unter diesen Bedingungen ist dein FindSymbol auch doppelt ineffizient (einerseits O(N) statt O(log N) beim Durchlaufen des Baumes, und andererseits sind die zu vergleichenden Strings unnötig lang).

    Ich sehe auch keinerlei Notwendigkeit hier überhaupt irgendeine Form von Zeigern einzusetzen. Das ganze könnte also ungefär so aussehen

    class AdsSymbol
    {
        ITcAdsSymbol symbol;
    
        AdsSymbol(const AdsSymbol&) = delete;
        AdsSymbol& operator=(const AdsSymbol&) = delete;
        AdsSymbol(AdsSymbol&&); // ggf. Zeiger in _subSymbols auf parent anpassen
        AdsSymbol& operator=(AdsSymbol&&);
    
        struct SymbolCompare {
            template <typename T>
            bool operator()(T&& x, const AdsSymbol& y) const { return std::forward<T>(x) < y.symbol.shortName; }
            template <typename T>
            bool operator()(const AdsSymbol& x, T&& y) const { return x.symbol.shortName < std::forward<T>(y); }
            bool operator()(const AdsSymbol& x, const AdsSymbol& y) const { return x.symbol.shortName < y.symbol.shortName; }
            using is_transparent = int;
        };
        mutable set<AdsSymbol, SymbolCompare> _subSymbols;
    ...
        const AdsSymbol* FindSymbol(const string& name) const { return FindSymbol(string_view(name)); }
        const AdsSymbol* FindSymbol(string_view name) const
        {
            if (name == symbol.shortName)
                return this;
    
            if (symbol.shortName.size() <= name.size() || symbol.shortName == name.substr(0, symbol.shortName.size()) || name[symbol.shortName.size()] != '.')
                return nullptr;
    
             return FindSymbolImpl(substr(symbol.shortName.size()+1));
         }
         const AdsSymbol* FindSymbolImpl(stringview name) const
         {
             auto end = find(name.begin(), name.end(), '.');
             auto subSym = _subSymbols.find(name.substr(0, end));
             if (subSym == _subSymbols.end())
                 return nullptr;
             if (end == name.end())
                 return &*subSym;
             return subSym->FindSymbolImpl(name.substr(end+1));
         }
    };
    
    AdsSymbol tree_root;
    


  • @printee

    Ja ich versteh den Unterschied. Aber ich setzt die Namen nicht zusammen.
    In Name steht immer der gesamte Name drin. Also zusammengesetzt mit ".", Da mache ich nichts, das kommt direkt so von der SPS

    In shortName steht dann nur der eigene Name ohne die Namen der Eltern.

    Ich habe auch schon den shortName als key für die map verwendet und mal die FlattCollection für einen Test komplett weggelassen. Und der Speicher lief auch bis auf 2GB voll.



  • @camper

    1. Was dann? Klar ist der String dann doppelt vorhanden. Aber ich benötige den String ja als key

    2. Natürlich kann ich da einen Iterator schreiben der den Baum durchläuft. Das hatte ich ja schon geschreiben. Es war nur eine Frage der Effizienz ob ein Iterator schneller ist oder die Flatt halt einfach ab Programmstart aufzubauen.

    Aber wie gesagt. Die Flatt habe ich für einen Test weggelassen. Selber Speicherverbrauch.

    3. Siehe Antwort vorher. Wenn ich nur den shortName als Key nehme beliebt auch hier der Speicherverbrauch gleich hoch. In Name ist so schon der komplette Name drin.

    4. Danke für den Programmcode. Muss mir den erst mal genau anschauen. Aber wie gesagt mein Problem scheint nicht an den Pointern und an den zu langen strings gelagert zu sein.



  • Mir fällt gerade was anderes auf: Weiter vorn hast du geschrieben, es gibt insgesamt ungefähr eine Million Objekte (Symbols und Subsymbols). Wie passen die denn in die 3 MB, die von der SPS geholt werden? Das sind ja gerade mal 3 Bytes pro Objekt. Also, irgendwas stimmt hier nicht ...



  • @Printee du hast rech da passt was nicht.
    Aber ich weiß noch nicht was.

    Wenn ich mir den Speicher im Visual Studio (Process Memory) und im Taskmanager (Arbeitsspeicher privater Arbeitsatz) vor und nach dem Laden der Informationen aus der SPS anschaue. Steigt dieser in letzten Test sogar nur um 1 MB.



  • Vergiss das, was dir irgendwelche Taskmanager erzählen. Vor allem, wenn du den Verdacht hast, dass mit deinem Speicher irgendwas durcheinandergeht.

    Wenn dein Programm die SPS-Daten ausliest, dann muss es doch aufs Byte genau feststellen können, wie viele das sind. Ich verstehe ehrlich gesagt nicht, was daran so schwierig ist.



  • Vergiss das, was dir irgendwelche Taskmanager erzählen. Vor allem, wenn du den Verdacht hast, dass mit deinem Speicher irgendwas durcheinandergeht.

    Ja natürlich aber für einen grobe Aussage tuts der Taskmanager.
    Und 1 MB ist wie du schon sagtest ziemlich wenig.

    Ich verstehe ehrlich gesagt nicht, was daran so schwierig ist.

    Wer hat behauptet das dies schwierig ist? Habe da nun nur nicht gleich so genau hingeschaut. Ist ja nicht das eigentliche Problem.

    Aber für dich ganz genau. Der Speicher steigt um genau: 682,38 KB



  • booster schrieb:

    Vergiss das, was dir irgendwelche Taskmanager erzählen. Vor allem, wenn du den Verdacht hast, dass mit deinem Speicher irgendwas durcheinandergeht.

    Ja natürlich aber für einen grobe Aussage tuts der Taskmanager.

    Wenn du verstanden hast wie das mit den Heaps funktioniert, und auf die richtige Spalte im Task-Manager guckst (die unter Windows 10 z.B. per Default gar nicht angezeigt wird), dann schon. Sonst nicht.



  • Irgendwie wird es solangsam echt unübersichtlich:

    Deine Software benötigt, abhängig von CString oder std::String 1,2 oder 2 GB Arbeitsspeicher.
    Aber das Delta von vor einlesen und danach mit den aufgebauten Datenstrukturen hast du nur ein Unterschied von unter einem MB.
    Ist das so richtig? Dann suchen wir offensichtlich an der falschen Stelle nach deinem Speicherverbrauch...

    Was anderes, du hast irgendwo geschrieben, das du eine Exception bekommst. Bekommst du die, nachdem du die zyklischen shared pointer aufgelöst hast immer noch?



  • @hustbear
    Bin unter Windows 7 und hier ist die Spalte Arbeitsspeicher (privater Arbeitssatz) eingeblendet!

    @SchlangenMensch

    Aber das Delta von vor einlesen und danach mit den aufgebauten Datenstrukturen hast du nur ein Unterschied von unter einem MB.
    Ist das so richtig? Dann suchen wir offensichtlich an der falschen Stelle nach deinem Speicherverbrauch...

    Nein der unterschied von unter einem MB ist vor und nach dem lesen der Daten aus der SPS. Das hat ja Pintee gefragt. Das ist noch bevor die Datenstruktur aufgebaut ist.

    Was anderes, du hast irgendwo geschrieben, das du eine Exception bekommst. Bekommst du die, nachdem du die zyklischen shared pointer aufgelöst hast immer noch?

    Ja die exception kommt immer kanpp an der unteren Grenze zu 2GB speicher. Dann komm bad allocation. Ich habe die nun mal mit rohzeigern und mit weak pointern gemacht. Habe nun mal für einen Test auch die ganzen Beziehungen untereinader mal weg gelassen. Bring nichts.



  • booster schrieb:

    Ich verstehe ehrlich gesagt nicht, was daran so schwierig ist.

    Wer hat behauptet das dies schwierig ist? Habe da nun nur nicht gleich so genau hingeschaut. Ist ja nicht das eigentliche Problem.

    Doch, genau das ist das Problem. Dass du nicht so genau hinschaust. Dein Programm verballert gigabyteweise Speicher für quasi nichts (immer schon) und du wunderst dich nicht darüber. Wieso auch - läuft ja alles prächtig.

    Aber für dich ganz genau. Der Speicher steigt um genau: 682,38 KB

    Offenbar ist das doch zu schwierig. Ich hatte nicht gefragt, um wieviel irgendwelche Anzeigen steigen, sondern wie groß der Rohdatenblock ist, den deine Software aus der SPS liest.

    Und wenn die 682KB tatsächlich die gelesene Blockgröße wären, dann wären das rund 0,7 Byte pro Symbol - es ist wirklich erstaunlich, dass du solche Zahlen stumpf abschreibst, ohne dich im Mindesten darüber zu wundern. Gehörst du etwas auch zu denen, die bei all dem Kilo-Mega-Giga schon lange den Überblick verloren haben?


Anmelden zum Antworten