Speicher freigeben extrem langsam



  • bau ma nen (compilierbares) minimal-bsp pls...
    sonst wird dir da keiner helfen können...

    bb



  • Tja, k.A. was du machst, aber 100'000 Objekte sind erstmal kein Problem (warum auch?). Code wuerde helfen, dein Problem zu finden, da es an allem liegen kann. Woher nimmst du die Information, dass die meiste Zeit im Destruktor von Klasse 2 verbraten wird? Vielleicht ist auch der Destruktor von Klasse 3 verantwortlich.

    Desweiter drueckst du dich recht undeutlich aus:

    new() dynamisch 10 Objekte von Klasse2 an und dort wiederrum mit new() 100'000 Elemente von Klasse2 ...

    Haeh? Wenn du deinen Code sowieso nur in Worten wiedergibst, dann kannst du auch gleich den Code zeigen.



  • Was bringt dir std::vector , wenn du den Speicher trotzdem manuell verwaltest?

    Massen-Allokationen und -Deallokationen sind schneller als einzelne, also lass dich hier durch die STL unterstützen. Vielleicht ist auch std::deque eine Alternative.



  • Hab das mal (beispielhaft) programmiert. Hoffe es läuft bei euch. Bei mir dauert das Anlegen des Speichers nur 2 Sek, insgesamt läuft das Programm aber 44 Sek.

    Muss doch bei nem vector von pointern das manuell verwalten, sonst gibt er den Speicher doch nicht frei, oder? Bin bei meinen Berechnungen bei ca 1,5GB Speciherbedarf, den ich wieder frei geben muss um weitere Berechnungen zu machen, sonst würd ich halt nur Speicher holen.

    Hatte es zuerst ohne Pointer, das war aber noch langsamer...

    // Memory_Test.cpp : Defines the entry point for the console application.
    //
    
    #include "stdafx.h"
    #include "stdio.h"
    #include "Memory_Test.h"
    #include <ctime>
    
    int NumberSimulations;
    double startTime;
    
    Klasse1::Klasse1() {
    	for (int i=0 ; i < 3 ; i++) {
    		Klasse1_Data.push_back(new Klasse2());
    	}
    }											
    Klasse1::~Klasse1(){
    	for (size_t i=0 ; i < Klasse1_Data.size() ; i++) {
    		delete Klasse1_Data[i];
    	}
    }
    Klasse2::Klasse2() {
    	int lambda;
    	Klasse2_Data.reserve(NumberSimulations);
    	for (int i=0; i<NumberSimulations;i++) {							// für jede simulation
    		lambda = rand()%20;												// Zufallszahl für die Länge des Vektors in Klasse3
    		Klasse3* Klasse2_Temp = new Klasse3(lambda);					// Temporäres Objekt zum Speichern der Daten
    		for (int j=0; j<lambda; j++) {
    			Klasse2_Temp->Klasse3_Data.push_back(rand());				// füllen des Vektors mit Zufallszahlen
    		}
    		Klasse2_Data.push_back(Klasse2_Temp);
    	}
    }
    Klasse2::~Klasse2() {
    	for (size_t i=0 ; i < Klasse2_Data.size() ; i++) {
    		delete Klasse2_Data[i];
    	}
    }
    Klasse3::Klasse3() {
    }
    Klasse3::~Klasse3() {
    }
    Klasse3::Klasse3(int lambda) {
    	Klasse3_Data.reserve(lambda);										// Reservieren des Speicherplatzes
    }
    void generateRandomNumbers() {
    
    	std::cout << "Start Berechnungen " << std::endl;
    	Klasse1 Klasse1Data;
    	double endTime = (clock()/CLOCKS_PER_SEC) - startTime;
    	std::cout << "Ende Berechnungen, Zeit: " << endTime << " Sek" << std::endl;
    }
    int main() {
    
    	startTime = clock()/CLOCKS_PER_SEC;
    	NumberSimulations = 100000;
    	generateRandomNumbers();
    	double endTime = (clock()/CLOCKS_PER_SEC) - startTime;
    	std::cout << "Ende Programm inkl. Destruktor, Zeit: " << endTime << " Sek" << std::endl;
    	std::cin >> NumberSimulations;
    	return 0;
    }
    

    die .h Datei

    #include <vector>
    #include <iostream>
    
    class Klasse3 {
    public:	
    	Klasse3();										
    	~Klasse3();
    	Klasse3(int);	
    	std::vector<long long int> Klasse3_Data;			// Daten per Simulation
    };
    class Klasse2 {
    public:	
    	Klasse2();													
    	~Klasse2();
    	std::vector<Klasse3*> Klasse2_Data;					// Länge entspricht Anzahl der Simulationen
    	std::vector<long long int> Klasse2_Sum;				// Summe der pro Simulation
    };
    class Klasse1 {
    public:	
    	Klasse1();											
    	~Klasse1();
    	std::vector<Klasse2*> Klasse1_Data;					// Länge entspricht Anzahl der gewünschten Objekte
    	std::vector<long long int> Klasse1_Sum;				// Summe der pro Simulation
    };
    

    Wär super, wenn also irgendwer weiss, wie ich das Speicher freigeben schneller als 42 Sek. machen kann. Irgendwie kann es doch nicht sein, dass das 20mal länger dauert als den Speicher zu allokieren???

    Besten Dank

    Michael



  • Naja - unter Minimalbsp verstehe ich aber was anderes - und schön getrennt ist das auch net (seit wann implementiert man fkt einer klasse in der main, schreibt aber die klasse in ne eigene header-datei?)

    Mal davon abgesehen, waren einige Dinge ja echt bäh -.- (bsp. implementiert man keinen DTor, um dort dann gar nix zu machen etc.)

    ich hab folgende Zeiten: (create/destruct)
    (@E8400 @3GHz CPU, 32bit programm, 64bit vista)

    std::vector<T> 39/30 (wobei ich gedacht hätte, dass er da klüger ist ^^)
    std::vector<T*> 0/33
    boost::ptr_vector <T> 0/33

    überraschenderweise hat er bei dem x64 compilat deutlich länger gebraucht (und ich dachte bis jz immer, dass der e8400 wirklich ne 64bit cpu ist - ist offenbar aber nur 64bit kompatibel):

    81/61
    0/64
    0/64

    vll (mit Sicherheit) bringt es etwas, nen memory-pool anzulegen, über placement new zu gehen und dann kannst du das delete auch endlich auf ein großes stück speicher und nicht auf 30000kleine jagen ^^
    fragt sich nur, ob bei einer simulation 30 sekunden so groß ins gewicht fallen...

    über iteratoren im dtor gings auch nicht schneller - die zeit wird aber wirklich komplett im dtor von klasse2 "verbraucht"...
    (aber es muss ja auch 300000x delete aufrufen)

    ich hab auch mal einiges geändert:
    hab auch noch bissl was unsinniges eingebaut, weil ich angst hatte, dass der compiler womöglich zu viel optimiert...
    (vll findet sich jmd anderes, der es jz machen möchte)

    #if 0
    #	define _SECURE_SCL 0
    #	include <vector>
    #	define VEC(X) std::vector<X*>
    #	define DTOR_NEEDED
    #elif 0
    #	include <boost/ptr_container/ptr_vector.hpp>
    #	define VEC(X) boost::ptr_vector<X>
    #	define STACK_BACK
    #elif 1
    #	define _SECURE_SCL 0
    #	include <vector>
    #	define VEC(X) std::vector<X>
    #	define STACK
    #endif
    
    #include <ctime> 
    #include <iostream>
    
    clock_t t1(0);
    long long int ctorcount(0);
    long long int dtorcount(0);
    
    class Klasse3
    {
    public:    
        Klasse3(size_t lambda = 0)
    	{
    		++ctorcount;
    	    Klasse3_Data.reserve(lambda);
    	}
    
    	std::vector<long long int> Klasse3_Data;
    };
    
    class Klasse2
    { 
    public:    
        Klasse2(size_t NumberSimulations)
    	{
    		Klasse2_Data.reserve (NumberSimulations);
    		for (size_t i(0), e(NumberSimulations); i != e; ++i)
    		{
    			const int lambda = rand()%20;
    
    			Klasse2_Data.push_back (
    #ifndef STACK
    				new
    #endif
    				Klasse3(lambda)
    			);
    
    			for (size_t j(0), ej(lambda); j != ej; ++j)
    			{
    #if defined (STACK) || defined (STACK_BACK)
    				Klasse2_Data.back().Klasse3_Data.push_back ( rand() );
    #else
    				Klasse2_Data.back()->Klasse3_Data.push_back ( rand() );
    #endif
    			}
    		}
    	}
    #ifdef DTOR_NEEDED
        ~Klasse2()
    	{
    		clock_t a = clock();
    		for (size_t i (0), e(Klasse2_Data.size()) ; i != e ; ++i)
    		{
    			++dtorcount;
    			delete Klasse2_Data[i];
    		}
    		t1 += clock() -a;
    	}
    #endif
        VEC(Klasse3) Klasse2_Data;
    };
    
    class Klasse1
    { 
    public:    
        Klasse1(size_t NumberSimulations)
    	{
    		for (size_t i=0; i != 3 ;++i)
    		{
    			Klasse1_Data.push_back (
    #ifndef STACK
    				new
    #endif
    				Klasse2(NumberSimulations)
    			);
    		}
    	}
    #ifdef DTOR_NEEDED
        ~Klasse1()
    	{
    		for (size_t i(0), e(Klasse1_Data.size()) ; i != e ; ++i)
    		{
    	        delete Klasse1_Data[i];
    		}
    	}
    #endif
    
    	VEC(Klasse2) Klasse1_Data;
    };
    
    std::ostream& operator << (std::ostream& f, const Klasse1 &these)
    {
    	return f << "muell: " << these.Klasse1_Data.size() << " / " << ctorcount;
    }
    
    int main()
    {
        const double startTime = clock()/CLOCKS_PER_SEC;
        size_t NumberSimulations = 100000;
    
    	std::cout << "Start Berechnungen" << std::endl;
    	{
    		Klasse1 Klasse1Data(NumberSimulations);
    	    double endTime = (clock()/CLOCKS_PER_SEC) - startTime;
    		std::cout << "Ende Berechnungen, Zeit: " << endTime << " Sek" << std::endl;
    		std::cout << Klasse1Data << std::endl;
    	}
    
    	double endTime = (clock()/CLOCKS_PER_SEC) - startTime;
        std::cout << "Ende Programm inkl. Destruktor, Zeit: " << endTime << " Sek" << std::endl;
    
    	std::cout << "1: " << t1 << " (" << dtorcount << "x)" << std::endl;
    
    	system("pause");
    }
    

    bb

    edit:
    #define _SECURE_SCL 0 bei std::vector<T> vergessen ^^
    -> zeiten geändert ^^



  • Dein Code ist ein ziemliches Gewirr. Wieso leitest du die Konstruktionen weiter und erzeugst jeweils einzelne Instanzen mit new ? Die Gefahr für Memory Leaks steigt damit stark.

    Aber grundsätzlich finde ich den Code sehr unübersichtlich. Die Aufgaben sind völlig willkürlich verteilt. Nur schon der Konstruktor der Klasse3, der Speicher vorreservieren soll, welcher wieder in Klasse2 gefüllt wird. Durch das public ist überhaupt keine Kapselung vorhanden.

    Michi78 schrieb:

    Muss doch bei nem vector von pointern das manuell verwalten, sonst gibt er den Speicher doch nicht frei, oder? Bin bei meinen Berechnungen bei ca 1,5GB Speciherbedarf, den ich wieder frei geben muss um weitere Berechnungen zu machen, sonst würd ich halt nur Speicher holen.

    Hatte es zuerst ohne Pointer, das war aber noch langsamer...

    Das glaube ich nicht. Wie gesagt ist es meistens schneller, wenn viel Speicher auf einmal allokiert/deallokiert werden kann.

    Kannst du dein Problem nicht auf einen übersichtlichen kurzen Code reduzieren, der dem beschriebenen Verhalten immer noch gerecht wird?



  • Nexus schrieb:

    Michi78 schrieb:

    Muss doch bei nem vector von pointern das manuell verwalten, sonst gibt er den Speicher doch nicht frei, oder? Bin bei meinen Berechnungen bei ca 1,5GB Speciherbedarf, den ich wieder frei geben muss um weitere Berechnungen zu machen, sonst würd ich halt nur Speicher holen.

    Hatte es zuerst ohne Pointer, das war aber noch langsamer...

    Das glaube ich nicht. Wie gesagt ist es meistens schneller, wenn viel Speicher auf einmal allokiert/deallokiert werden kann.

    hum? er hat aber keine einfache möglichkeit, das auf einmal zu allokieren/deallokieren - er müsste eben wie gesagt den weg über den memory pool gehen - das sollte zwar nicht unendlich viel arbeit sein, aber konzeptionell ist es bestimmt nich gerad einfach (am stück? eher nich bei 1,5GB -> blockgröße?)
    er muss alle news durch placement news ersetzen oder direkt nen eigenen allokator schreiben und den 3 klassen diesen mitgeben...

    würde aber einiges für die lsg sprechen, denk ich ^^

    bb



  • Sorry an alle für den Code-Wirrwar, bin halt schon froh wenn der Rechner das macht, was er machen sollte... Vielen Dank aber für eure Ideen.

    unskilled schrieb:

    fragt sich nur, ob bei einer simulation 30 sekunden so groß ins gewicht fallen...

    naja, bei dem Beispiel werden ca 50MB Speicher genutzt. Bei 1,5 GB, den ich ca 2-3 mal freigeben müsste, warte ich dann schon eine ganze Weile auf die Speicher-Putzkolonne...

    unskilled schrieb:

    er hat aber keine einfache möglichkeit, das auf einmal zu allokieren/deallokieren - er müsste eben wie gesagt den weg über den memory pool gehen

    Das seh ich ähnlich, denn ich weiss nunmal nicht wieviel ich brauche da die Länge der Vektoren zufällig ist und sich in jeder Simulation ändert. Wie würde denn das mit dem Memory-Pool prinzipiell gehen, könntest mir das kurz erklären???

    unskilled schrieb:

    aber es muss ja auch 300000x delete aufrufen

    Was ich halt nicht versteh ist, wieso 300000 mal den Destruktors aufzurufen so extrem viel länger dauert als 300000 mal den Konstruktor.

    Hoffe, irgendwer hat noch Ideen oder Anregungen...

    Beste Grüsse

    Michael



  • Ich hab den Code gerade mal ausprobiert, dauert bei mir alles exakt 0 Sekunden, mit VS05.



  • Michi78 schrieb:

    Wie würde denn das mit dem Memory-Pool prinzipiell gehen, könntest mir das kurz erklären???

    Selber machen lohnt sich nicht. Schau mal hier:
    http://www.boost.org/doc/libs/1_38_0/libs/pool/doc/index.html



  • Badestrand schrieb:

    Ich hab den Code gerade mal ausprobiert, dauert bei mir alles exakt 0 Sekunden, mit VS05.

    Oo
    welchen? wie/wo/... ?

    ich habs doch extra alles ausprobiert (und hab VS08)

    bb



  • Badestrand schrieb:

    Ich hab den Code gerade mal ausprobiert, dauert bei mir alles exakt 0 Sekunden, mit VS05.

    ja, der kumpel kann testprogramme der art

    int* arr=new int[...];
    //schreib und lies in arr wie du magst
    delete[] arr;
    

    erkennen und wegoptimieren.
    man muß generell bei messprogrammen immer dafür sorgen, daß alle operationen in einer ergebniszahl kumukliert werden und dann zum beispiel mit cout.



  • so hab ichs ja nu au extra geschrieben... ~~

    bb



  • Gerade habe ich etwas Ähnliches versucht (unter MSVC++ 2008 Express). Wenn ich auf "Starten" klicke, dauert die Deallokation sehr lange. Benutze ich "Starten ohne Debuggen", geht es sehr schnell, Allokation und Deallokation dauern ungefähr gleich lange (beide sehr kurz). Das gilt auch für direktes Starten der .exe-Datei, also sobald keine Debugging-Umgebung mehr vorhanden ist.

    Ich habe auch ein wenig mit dem AMD CodeAnalyst rumprobiert, da brauchten Aufrufe von operator new und operator delete mit Abstand am wenigsten Zeit. Allerdings weiss ich nicht, wie viel da geinlinet wird, und mit Assembler kenne ich mich zu wenig aus...



  • volkard schrieb:

    man muß generell bei messprogrammen immer dafür sorgen, daß alle operationen in einer ergebniszahl kumukliert werden und dann zum beispiel mit cout.

    In einer von unskilleds Varianten wird ja die Anzahl der Dtor-Aufrufe mitgezählt und am Schluss ausgegeben, der Assembler-Code sieht auch nicht so aus, als ob alles wegoptimiert werden würde 😕

    Andererseits hab ich grad mal geschaut, bei mir verbraucht's im Taskmanager auch nur 36 MB, ist ja schon was anderes als die 1,5 GB von denen der TO sprach.



  • Badestrand schrieb:

    volkard schrieb:

    man muß generell bei messprogrammen immer dafür sorgen, daß alle operationen in einer ergebniszahl kumukliert werden und dann zum beispiel mit cout.

    In einer von unskilleds Varianten wird ja die Anzahl der Dtor-Aufrufe mitgezählt und am Schluss ausgegeben, der Assembler-Code sieht auch nicht so aus, als ob alles wegoptimiert werden würde 😕

    Andererseits hab ich grad mal geschaut, bei mir verbraucht's im Taskmanager auch nur 36 MB, ist ja schon was anderes als die 1,5 GB von denen der TO sprach.

    Ich seh gerade, dass es bei mir auch max. 50MB sind - aber es sah so aus, als ob er zwischendrin immermal was wieder freigibt - er optimiert halt einfach zu sehr -.- Zu lange dauern tuts trotzdem ^^
    der to kann sich ja ma melden und sagen, wie es mit nem memory-pool aussieht...

    bb



  • unskilled schrieb:

    Zu lange dauern tuts trotzdem ^^

    Seid ihr jetzt sicher, dass ihr ohne Debug-Laufzeitumgebung startet (auch im Release-Modus)? Bei mir hat das sehr viel ausgemacht.



  • Nexus schrieb:

    unskilled schrieb:

    Zu lange dauern tuts trotzdem ^^

    Seid ihr jetzt sicher, dass ihr ohne Debug-Laufzeitumgebung startet (auch im Release-Modus)? Bei mir hat das sehr viel ausgemacht.

    tzz 😣
    natürlich hab ichs im release-mode compiliert und getestet...
    schnell (<1sec) gings bei mir erst, nachdem ichs nach dem (release-)durchlauf (zusätzlich) nochmal mit dem profiler optimiert hatte - aber der wird ja mitbekommen haben, dass er sämlichen new/delete-kack weglassen kann - und deshalb kann man die werte wohl auch nicht zum vergleich nehmen...

    bb



  • unskilled schrieb:

    tzz 😣
    natürlich hab ichs im release-mode compiliert und getestet...

    Natürlich. Es ging aber nicht um Release- vs. Debug-Mode, sondern ums Starten der Laufzeitumgebung. Und bei MSVC++ ist das in beiden Konfigurationen möglich. Wie gesagt hat das bei mir den Grossteil der Zeit ausgemacht.

    Da ich das nur gut meinte und auf eine mögliche Fehlerquelle hinweisen wollte, besteht auch kein Grund, so zu antworten.



  • Fragt doch erst mal was das Programm machen soll. Es ist sehr wahrscheinlich, dass es da ne bessere Lösung gibt als diesen wirren Code zu optimieren.


Anmelden zum Antworten