Speicherverbrauch shared_ptr und normaler *



  • @camper
    ja das mit dem delete[] habe ich auch bemerkt. Dies habe ich bei mir im Code aber richtig gestellt. An dem liegt es nicht.

    Grauenhaft? Was meinst du genau ausser den falschen delete.



  • Gut wenn man sich das Beispiel runter lädt und die Klasse CAdsParseSymbols anschaut dann kann man grauenhaft sagen.



  • Das ist aber doch nicht der Code den du verwendest, oder? Zumindest finde ich keine Stelle, wo irgendwelche Maps befüllt werden oder ähnliches.



  • @Schlangenmensch

    Das ist der Code den ich verwende um die Informationen aus der SPS auszuelesen.
    Mit Hilfe dieser Informationen baue ich dann meine Flat- und TreeCollection auf.



  • Das ist der Code dazu

    void TcAdsSymbolInfoLoader::CreateCollection(const shared_ptr<AdsParseSymbols>& parseSymbols,
                                                 shared_ptr<TcAdsSymbolInfoCollection>& treeCollection,
                                                 shared_ptr<TcAdsSymbolInfoCollection>& flatCollection) const
    {
        if (!parseSymbols)
        {   
            const auto message = FTRANSLATE("Invalid argument '{1}'.", "parseSymbols");
            THROW_TRACEABLE_EXCEPTION(message);
        }
    
        treeCollection = make_shared<TcAdsSymbolInfoCollection>();
        flatCollection = make_shared<TcAdsSymbolInfoCollection>();
    
        AdsSymbolInfo	main;
        weak_ptr<TcAdsSymbolInfo> tclast;
    
        const auto symbolCount = parseSymbols->SymbolCount();
    
        for(unsigned int i=0; i < symbolCount; i++)
        {
            auto tcmain = make_shared<TcAdsSymbolInfo>();
    
            // next symbol für das vorherige setzen
            if( i != 0 )
            {
                tclast.lock()->_nextSymbol = tcmain;
            }
    
            parseSymbols->Symbol(i,main);
    
            const auto subsymbolCount = CreateSubSymbols(parseSymbols,main,tcmain);
    
            tcmain->AssignAdsSymbolInfo(main,subsymbolCount);
    
            flatCollection->Add(tcmain);
            treeCollection->Add(tcmain);
    
            // Symbol abspeichern für Zeiger auf letztes Element
            tclast = tcmain;
        }
    }
    
    int TcAdsSymbolInfoLoader::CreateSubSymbols(const shared_ptr<AdsParseSymbols>& parseSymbols,
                                                AdsSymbolInfo& parent,
                                                const shared_ptr<TcAdsSymbolInfo>& tcparent) const
    {   
        AdsSymbolInfo subinfo;
        weak_ptr<TcAdsSymbolInfo> tclast;
    
        const auto pDataType = parseSymbols->GetTypeByName(parent.type);
        const auto subSymbolCount = parseSymbols->SubSymbolCount(pDataType);
    
        for (unsigned int i = 0; i < subSymbolCount; i++)
        {
            auto tcsubinfo = make_shared<TcAdsSymbolInfo>();
            //auto tcsubinfo = new TcAdsSymbolInfo();
    
            // next symbol für das vorherige setzen
            if (i != 0)
                tclast.lock()->_nextSymbol = tcsubinfo;
            // erstes Subsymbol für parent setzen
            else
                tcparent->_firstSubSymbol = tcsubinfo;
    
            parseSymbols->SubSymbolInfo(parent, i, subinfo);
    
            // Symbol abspeichern für Zeiger auf letztes Element
            tclast = tcsubinfo;
    
            // Parent setzen
            tcsubinfo->_parent = tcparent;
    
            // SubSymbols rekursiv aufrufen
            const auto count = CreateSubSymbols(parseSymbols, subinfo, tcsubinfo);
    
            tcsubinfo->AssignAdsSymbolInfo(subinfo, count);
    
            _flatCollection->Add(tcsubinfo);
            tcparent->_subSymbols->Add(tcsubinfo);        
        }
    
        return subSymbolCount;
    }
    


  • Welchen Typ hat TcAdsSymbolInfo::_parent?



  • TcAdsSymbolInfo::_parent ist aktuell vom typ weak_ptr<TcAdsSymbolInfo>.

    TcAdsSymbolInfo ist eigentlich nur eine wrapper klasse um AdsSymbolInfo
    die ich dann in meinen Collections abspeichere

    class TcAdsSymbolInfo
    {
    
        /// Zeiger auf erstes Symbol
        weak_ptr<TcAdsSymbolInfo> _firstSubSymbol;
    
        /// Zeiger auf nächstes Symbol
        weak_ptr<TcAdsSymbolInfo> _nextSymbol;
    
        /// Zeiger auf Elternsymbol
        weak_ptr<TcAdsSymbolInfo> _parent;
    
        /// Zeiger auf Untersymbole
        shared_ptr<TcAdsSymbolInfoCollection> _subSymbols;
    
        /// Struktur mit Basisinformationen
        ITcAdsSymbol _baseInfos;
    
        void AssignAdsSymbolInfo(AdsSymbolInfo &info, const int subSymbolCount )
        {
            _baseInfos.comment          = info.comment;
            _baseInfos.datatype         = AdsDatatypeId::_from_integral(info.dataType);
            _baseInfos.indexGroup       = info.iGrp;
            _baseInfos.indexOffset	= info.iOffs;
            _baseInfos.name		= info.fullname;
            _baseInfos.shortName        = info.name;
            _baseInfos.size		= info.size;
            _baseInfos.type		= info.type;
            _baseInfos.subSymbolCount   = subSymbolCount;
        }
    }
    


  • Schau dir nochmal das Beispiel von weak_ptr hier an http://en.cppreference.com/w/cpp/memory/weak_ptr

    std::weak_ptr<int> gw;
    
    void observe()
    {
        std::cout << "use_count == " << gw.use_count() << ": ";
        if (auto spt = gw.lock()) { // Has to be copied into a shared_ptr before usage
    	std::cout << *spt << "\n";
        }
        else {
            std::cout << "gw is expired\n";
        }
    }
    
    int main()
    {
        {
            auto sp = std::make_shared<int>(42);
    	gw = sp;
    
    	observe();
        }
    
        observe();
    }
    

    Du läufst genau in die Falle, du erstellst mit make_shared in Zeile 59 einen shared ptr und weißt den in Zeile 64 oder 67 einem weak_ptr zu. Wenn der shared_ptr jetzt out of scope geht, ist der weak_ptr auch ungültig.



  • Ja ich erzeuge einen shared_ptr in Zeile 59 den ich dann in Zeile 83 und 84 meiner Collection zuweise. Das heißt die Collection ist dann der Besitzer des Objekts.

    der weak_ptr muss dann doch auch ungültig werden wenn das eigentliche objekt gelöscht wird. Das passiert ja erst wenn das Objekt aus der Collection raus fliegt.

    Oder nicht?



  • Oh, die Add() Funktion da hatte ich tatsächlich übersehen.
    Hängt natürlich von der Implementierung ab. Bei den Funktionen die du hier zeigst übergibst du immer eine Referenze auf einen shared_ptr. Das ist natürlich gut für die Performance, auf der anderen Seite verhinderst du damit, dass der reference count inkrementiert wird.
    Wie auch immer, dass kannst du recht einfach im Debugger oder mit einer Debug Ausgabe überprüfen und erklärt auch nicht, den von dir beschriebenen Speicherverbrauch deiner Anwendung.



  • Ja die Add Methode ist aber nicht const&

    void Add(shared_ptr<TcAdsSymbolInfo> symbolInfo)
    

  • Mod

    Wie verhalten sich das Programm+Log, wenn du

    static symbol_count = 0;
        PROCESS_MEMORY_COUNTERS mem_info;
        GetProcessMemoryInfo(GetCurrentProcess(), &mem_info, sizeof(mem_info));
        TRACE((to_string(++symbol_count) + " " + to_string(mem_info.WorkingSetSize)).c_str());
    

    vor dem return in CreateSubSymbols einfügst?
    Wieviel Speicher wird per Symbol im Schnitt benötigt?
    Ist der Speicherverbrauch pro Symbol (einigermaßen) konstant oder wächst er mit der Zeit?



  • @camper
    Habe das mit dem Visual Studio memoery profiler schon getestet.
    da hat jedes Symbol zwischen 0,6 - 3 K Byte
    das mal 1,4 Millionen Objekte kommt schon etwa hin mit den 2 GB.

    Aber ich teste das mit deinem Code schnipsel auch noch bevor wieder...



  • Ich meinte weniger den Aufruf von der Add Methode sonder eher das hinzufügen zur Map. Aber das ist ja im Moment auch egal. Wenn da was schief läuft, fliegt dir die Software zur Laufzeit irgendwann um die Ohren und reserviert nicht 2 GB Speicher.

    Hast du mal Valgrind, oder eine entsprechende alternative laufen lassen. Bei dem externen Code, den du gezeigt hast, bin ich mir nicht sicher, dass da alles sauber läuft.



  • Ja bei dem externen Code bin ich mir auch nicht sicher. Der sieht wirklich graunhaft aus.

    Aber ich weiß noch nicht ob der mein Speicher verbraucht.

    Und wieso der Code nur 600KB braucht um die die 1,4 Millionen Datensätze auszulesen ist auch noch nicht ganz klar.



  • booster schrieb:

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

    Ja. Das ist nicht die Spalte die du suchst. Die kann sich quasi beliebig ändern.

    Was du suchst ist "commit size". Ich weiss aber nicht wie die im Windows 7 Task Manager heisst. Und auch dann gibt's Pitfalls, nämlich dass der Heap freigegebenen Speicher nicht unbedingt ans System zurück gibt (bzw. das oft auch gar nicht kann), und dass AFAIK Programmcode sowie geladene DLLs mitgezählt werden.



  • @hustbear

    Würde mal sagen die commit size ist die "Zugesicherte Größe".



  • @camper

    Wie verhalten sich das Programm+Log, wenn du ... vor dem return in CreateSubSymbols einfügst?

    Also ich versuch mal zu interpretieren und hoffe ich bekomme nicht gleich wieder eine auf die Mütze, wie "ist ja alles inkonsistent" und du denkst ja gar nicht nach".

    Folgende Erkenntnise.
    WorkingSetSize nach 1. Aufruf von CreateSubSymbol:
    34631680 Bytes

    WorkingSetSize nach 500. Aufruf von CreateSubSymbol:
    35221504 Bytes

    Unterschied -> 589824 Bytes = 589KB

    Sprich nach 1,4 Millionen Symbolen
    kommt dann schon um die 2GB raus.



  • Also um noch die beiden Fragen zu beantworten

    Wieviel Speicher wird per Symbol im Schnitt benötigt?

    Also wenn ich noch weiterlaufen lasse und mehr als 500 Symbole betrachte um die
    1,5 KB

    Ist der Speicherverbrauch pro Symbol (einigermaßen) konstant oder wächst er mit der Zeit?

    Er ist einigermaßen konstant. Wächst auf jeden Fall nicht mit der Zeit.


  • Mod

    Wieviele von den 1,4 Millionen Symbolen sind Array-Elemente (der Datenblock enthält nur Informationen über Arraydimensionen, das Programm erzeugt aber für jedes einzelne Array-Element einen Symboleintrag) ?
    Ich habe ja keine Ahnung, was mit den Daten später gemacht werden soll, allerdings erscheint es mir relativ sinnlos, für jedes einzelne Element eines Arrays Informationen zu speichern, die sowieso identisch mit dem übergeordneten Symbol sind. Das würde auch erklären, wie ein 682KB großer Datenblock sich überhaupt auf so viele Elemente beziehen kann.


Anmelden zum Antworten