Speicher freigeben extrem langsam
-
Hallo Zusammen,
ich habe folgendes Problem: Ich habe eine Baumstruktur an Klassen aufgebaut, so dass Klasse1 einen std-vector aus pointern auf Klasse2 und die wiederum einen std-vector aus pointern auf Klasse3 und in dieser Klasse dann ein std-vector mit long long int Werten liegt.
c++-code sieht dann wie folgt aus:
class Klasse1 { public: Klasse1(); ~Klasse1(); std::vector<Klasse2*> Klasse1_Data; //Länge dynamisch (z.B.10) std::vector<long long int> Klasse1_Sum; //Länge fix (z.B. 100'000) }; class Klasse2 { public: Klasse2(); ~Klasse2(); std::vector<Klasse3*> Klasse2_Data; //Länge fix (z.B. 100'000) std::vector<long long int> Klasse2_Sum; //Länge fix (z.B. 100'000) }; class Klasse3 { public: Klasse3(); ~Klasse3(); std::vector<long long int> Klasse3_Data; //Länge dynamisch (z.B.10-20) };Dann wird folgendes gemacht:
- Ich lege zum Beispiel mit new() dynamisch 10 Objekte von Klasse2 an und dort wiederrum mit new() 100'000 Elemente von Klasse2 und dort speichere ich dann im Konstruktor eine fest vorgegebene Anzahl, aber für jede der 100'000 Instanzen zufällige und unterschiedliche Anzahl (z.B. mal 14 oder mal 16 oder mal 18 ...), an long long int Zufallszahlen im Vektor Klasse3_Data. In den Vektoren Klasse2_Sum und Klasse1_Sum wir dann jeweils die Summe des Vektors Klasse3_Data der dynamischen Unterklassen gebildet.
Nun mein Problem: Die Berechnugen und das Besorgen von Speicher (mach ich mit reserve(10), dann reserve(100000) und reserve(14 oder 16 oder 18) geht sehr schnell innerhalb weniger Sekunden (ca 10 Sekunden), wenn ich aber mit delete die pointer wieder löschen will dauert es ungefähr 10-Mal so lange (ca 120 Sekunden) wie die gesamten Berechnungen und den Speicher zu allokieren.
Die Delete-Prozeduren werden im Destruktor für alle Objekte durchgeführt, also in Klasse1
for (size_t i=0;i<Klasse1.size();i++) { delete Klasse2_Data[i]; }und in Klasse2:
for (size_t i=0;i<Klasse2_Data.size();i++) { delete Klasse2_Data[i]; }Hoffe, dass das einigermasssen verständlich ist, ansosnten bin ich natürlich gerne bereit mehr Infos zu geben. Wär super, wenn jemand eine Idee hätte, wieso dass so extrem langsam ist mit dem Speicher freigeben bzw wie ich das am Besten umbauen könnte, da ich das leider machen muss mit dem Speicher freigeben.
Besten Dank im voraus
Michael
-
Ich bin mir nicht sicher, was der compiler da optimieren kann, bei deiner variante - aber ich würde ma folgendes probieren:
for (size_t i=0, e(Klasse1.size());i<e;i++) { delete Klasse1_Data[i]; }oder gleich über iteratoren - allerdings sollte einem guten compiler das egal sein...
btw: auf release-mode gestellt?
edit: falls du msvc nutzt: #define _SECURE_SCL 0?
bb
-
Salü,
Vielen Dank schonmal für die schnelle Antwort, Code bringt leider aber nix bei der Performance. Ansonsten ist auf Release gestellt und ich benutz die Express Edition von Visual C++ 2008. Hab noch das #define _SECURE_SCL 0 eingebaut, bringt aber leider auch nix.
Hab nun nochmal analysiert, wo die Zeit verbraten wird, und es ist tatsächlich im Destruktor von Klasse2, da dort 100'000 Mal der Destruktor von Klasse 3 aufgerufen wird, der dann jeweils den Vektor löscht. Überfordern 100'000 Instanzen der Klasse 3 die Standard-Template-Library?
Beste Grüsse
Michael
-
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::dequeeine 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/64vll (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 0beistd::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
publicist ü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 newundoperator deletemit 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