Wie heißt der Nachfolger ...
-
Hallo,
ich hätte überhaupt nichts gegen einen GC in C++ solange selbiger optional ist.
-
Original erstellt von HumeSikkins:
solange selbiger optional ist.Warum das? An welchen Stellen hat man denn ohne GC einen Vorteil? Es gibt doch optionale GCs für C++, oder? Ich habe gehört, dass die alle Müll sind. ...zumindest vom Performance-Standpunkt aus. Ist halt etwas Aufgesetztes, also kein Wunder. Die bringen nur etwas gegen Speicherlecks, was natürlich auch sehr gut ist. Viele Programme haben schließlich Speicherlecks. Warum braucht mein Opera z.B. gerade über 82 MB Speicher? Der Grund ist, dass ich längere Zeit gesurft habe, aber nicht, dass ich gerade viele innere Fenster oder so auf habe.
-
Original erstellt von Gregor:
OK! ...was kostet Garbage-Collection? Performance? Ich denke, dass zumindest das Erzeugen von Objekten mit Garbage-Collection schneller sein sollte, als ohne.Wenn man Performance jagt, tut Freispeicher weh. Wenigstens das allokieren unzähliger kleiner Objekte. Oft wollen die aber aus der Anwendung heraus schon, daß man sie in Datenstrukturen wie Stack oder Queue stoft, und da ist es wieder leicht (und in den Standardklassen sogar schon drin), Seitenweise Speicher zu holen, den Speicher defragmentiert zu halten und so. Und wirklich billig.
Das einzige mal, daß ich ernsthaft überlegt habe, nen eigenen Allokator zu bauen, war folgender Fall:
Um in nem Simulator, wo ich Hunderte von Objekten simulieren wollte, ein wenig Parallelität zu bekommen, wollte ich die Methodenaufrufe Asynchon machen. Da ein Methodenaufruf ja dem Senden einer Nachricht entspricht, hab ich das wörtlich genommen, und echt Nachrichtenobjekte verschickt. Und die Nachrichtenobjekte haben ne Weckzeit drin, wann sie aktiviert werden sollen.
Weil die Nachrichten in einer gemeinsamen PriorityQueue verwaltet werden sollen, aber unterschiedliche Größen haben, kann ich nur Zeiger auf die Nachrichten verwalten. Also lege ich alle Nachrichten im Freispeicher an. Ich hatte zwar nen Durchsatz von 100000 Nachrichten/Sekunde, aber naja, ich hatte auch das Gefühl, mit nem eigenen Nachrichten-Allokator könnte man da einiges mehr rausholen.Mal abwarten, wann sowas als nächstes vorkommt.
Ansonsten meide ich einfach new für die kleinen Objekte und für die großen isse erstmal nicht so schlimm. Wenns irgend geht, auf den Heap mit dem Kram.Auf den neuen Rechnern ist es immer schlimmer mit schlechten Cache-Treffern. Ich überlege auch schon ne ganze weile dran, häufig benutze Objekte im Speicher nebeneinanderzulegen, aber da sträubt sich C++ ein wenig dagegen, was mich nicht perfekt glücklich macht.
zur veranschaulichung das mit den vielen kleinen messages:
class MessageBase { private: Time timeToWakeUp; public: MessageBase(Time ttwu) :timeToWakeUp(ttwu) { }; MessageBase(const MessageBase &m);//forbidden MessageBase &operator=(const MessageBase &m);//forbidden virtual ~MessageBase(void) { // cout<<"test\n"; }; Time getTime(void) { return timeToWakeUp; }; virtual void shoot(void)=0; virtual void deactivate(void)=0; virtual SimObject *getTarget(void)=0; bool operator<(const MessageBase &m) { return timeToWakeUp<m.timeToWakeUp; }; }; template<class TARGET1,class TARGET2> class Message0:public MessageBase { private: TARGET1 *target; void(TARGET2::*function)(); public: Message0(TARGET1 *t,Time ttwu,void(TARGET2::*f)()) :target(t), MessageBase(ttwu), function(f) { target->_incCountOfMessages(); }; ~Message0(void) { deactivate(); }; void shoot(void) { if(target!=NULL) (target->*function)(); }; void deactivate(void) { if(target!=NULL) { target->_decCountOfMessages(); target=NULL; }; }; SimObject *getTarget(void) { return target; }; }; template<class TARGET1,class TARGET2,class O1> class Message1:public MessageBase { private: TARGET1 *target; void(TARGET2::*function)(O1); O1 object1; public: Message1(TARGET1 *t,Time ttwu,void(TARGET2::*f)(O1),O1 o1) :target(t), MessageBase(ttwu), function(f), object1(o1) { target->_incCountOfMessages(); }; ~Message1(void) { deactivate(); }; void shoot(void) { if(target!=NULL) (target->*function)(object1); }; void deactivate(void) { if(target!=NULL) { target->_decCountOfMessages(); target=NULL; }; }; SimObject *getTarget(void) { return target; }; }; class Simulator { private: Time time; PtrHeap<MessageBase> messages; int count; public: Simulator(void) { count=0; }; ~Simulator(void) { messages.flush(); }; Time getTime() { return time; }; void addMessage(MessageBase *m) { messages.push(m); // cout<<"+"<<++count<<'\n'; }; void deactivate(SimObject *o); void step(TimeDiff i); }; void Simulator::deactivate(SimObject *o) { for(int i=0;i<messages.getSize();i++) { if(messages[i]->getTarget()==o) messages[i]->deactivate(); }; }; void Simulator::step(TimeDiff i) { Time end=time+i; for(;;) { if(messages.isEmpty()) break; MessageBase *m=messages.peek(); if(end<m->getTime()) break; messages.pop(); // cout<<"-"<<--count<<'\n'; time=m->getTime(); // cout<<time<<'\r'<<flush; m->shoot(); delete m; }; time=end; cout<<time<<'\r'<<flush; }; class SimObject { private: Simulator *simulator; SimObject &operator=(const SimObject &o); int countOfMessages; protected: bool trace; public: SimObject(Simulator *sim) :simulator(sim) ,trace(false) { countOfMessages=0; }; ~SimObject(void) { simulator->deactivate(this); }; void _incCountOfMessages(void) { countOfMessages++; if(countOfMessages>100) { cout<<"countOfMessages=="<<countOfMessages<<"\n"; }; }; void _decCountOfMessages(void) { countOfMessages--; }; void setTrace(bool t) { trace=t; }; template<class TARGET1,class TARGET2> void doLater(TARGET1 *t,TimeDiff dauer,void(TARGET2::*f)()) { Message0<TARGET1,TARGET2> *m= new Message0<TARGET1,TARGET2>(t,simulator->getTime()+dauer,f); simulator->addMessage(m); }; template<class TARGET1,class TARGET2,class O1> void doLater(TARGET1 *t,TimeDiff dauer,void(TARGET2::*f)(O1),O1 o1) { Message1<TARGET1,TARGET2,O1> *m= new Message1<TARGET1,TARGET2,O1>(t,simulator->getTime()+dauer,f,o1); simulator->addMessage(m); }; template<class TARGET1,class TARGET2,class O1,class O2> void doLater(TARGET1 *t,TimeDiff dauer,void(TARGET2::*f)(O1,O2),O1 o1,O2 o2) { Message2<TARGET1,TARGET2,O1,O2> *m= new Message2<TARGET1,TARGET2,O1,O2>(t,simulator->getTime()+dauer,f,o1,o2); simulator->addMessage(m); }; Time getTime(void) { return simulator->getTime(); }; };
-
Original erstellt von Gregor:
Viele Programme haben schließlich Speicherlecks. Warum braucht mein Opera z.B. gerade über 82 MB Speicher? Der Grund ist, dass ich längere Zeit gesurft habe, aber nicht, dass ich gerade viele innere Fenster oder so auf habe.[/QB]Ist kein Argument für nen obligatorischen GC. Es gibt in C++ keinen Grund mehr, Speicherlöcher zu basteln.
-
Original erstellt von Gregor:
Warum das? An welchen Stellen hat man denn ohne GC einen Vorteil?[/QB]Wenn ich nur kurz Speicher anlege, was damit mache, und dann sofort wegwerfe, dann schreib vorne ein new und hinten ein delete und keine Architektur mit Zwangs-GC kann genausso billig sein.
Macht zwar nur ein paar Takte aus, aber in C++ gehörts zu den Regeln, daß man nix zahlen muß für was, was man nicht braucht.
-
Hallo,
ganz interessant zum Thema ist auch das Topic "Will C++0X have a gc?" das im Oktober in der Newsgroup comp.std.c++ diskutiert wurde.
-
Original erstellt von kartoffelsack:
**Templates (habt ja gesagt, die solls jetzt doch geben),
**gibt es auch, aber sie müssen explizit instanziiert werden.
Klassen und Interfaces[ Dieser Beitrag wurde am 26.11.2002 um 12:54 Uhr von Noesis editiert. ]
-
Noch mal zum Thema GC. Ist ein GC wirklich besser, als Smartpointer.
Der GC muss bei jedem Objekt überprüfen, ob er es löschen darf. dazu muss er doch semtliche Referenzen überprüfen, ob diese nicht auf dieses Objekt verweisen.
Smartpointer wissen, wann es keine Referenz mehr auf das Objekt gibt und müssen nicht danach suchen.
Vielleicht sollte man aber auch eine mischung verwenden (oder wird es in Java schon so gelößt?): Die Referenzen sind eine Art Smartpointer, die das Objekt aber nicht selbstständig löschen, sondern nur einer Art GC mitteilen, wenn sie zerstört werden. So muss der GC nichts suchen, sondern wiß immer, wann er ein Objekt zerstören darf.
-
Original erstellt von volkard:
**
Wenn ich nur kurz Speicher anlege, was damit mache, und dann sofort wegwerfe, dann schreib vorne ein new und hinten ein delete und keine Architektur mit Zwangs-GC kann genausso billig sein.
Macht zwar nur ein paar Takte aus, aber in C++ gehörts zu den Regeln, daß man nix zahlen muß für was, was man nicht braucht.**Verstehe ich nicht. Es ist IMHO schneller, sich mit einem vernünftigen GC Speicher für ein Objekt zu holen, als ohne. Das Löschen dauert mit GC natürlich länger, es wird aber nicht gleich gelöscht, wenn das Objekt nicht mehr benötigt wird. In Java gibt es 2 Stellen, an denen der GC aktiv wird und Speicher wieder freigibt :
1. Wenn der Speicher voll ist.
2. Wenn man den GC per Hand aufruft. Natürlich macht man das in der Regel an Punkten, an denen man ansonsten keine Performance benötigt.Natürlich ist ein GC langsamer, wenn man in einer Schleife, die oft durchlaufen wird, immer ein kleines Objekt erzeugt und kurz danach wieder zerstört. ...aber ich denke, solche Objekte sollten eh auf den Stack, oder?! ...da hat der GC doch eher nichts verloren.
-
Was sagt ihr denn zu den "neuen Features"?
Ich denke, folgendes gibt es in C++ so nicht, ich habe es zumindest noch nicht gesehen :
1. Vorbedingungen und Nachbedingungen für Methoden
2. Der Bit-Datentyp
3. Threads als Teil der Standardbibliothek
...
-
1. Vorbedingungen und Nachbedingungen für Methoden
Gibt's meines Wissens nach nur in Eiffel. In C++ kann man dies aber im Gegensatz zu Java noch relativ einfach einbauen. Einfach Template-Method anwenden:
class Base { public: void Func() { // Vorbedingungen prüfen do_Func(); // Nachbedingungen prüfen } private: virtual void do_Func() { // Algorithmus hier } }; class Derived : public Base { public: // öffentliches Interface in Ruhe lassen private: void do_Func() { // spezialisierter Algo hier } };
Ist sicher nicht perfekt, aber auch nicht ganz schlecht.
2. Der Bit-Datentyp
Es gibt bitsets. Bit als Datentyp wird ist meiner Meinung nach aber wohl niemals geben. Die kleinste adressierbare Einheit in C und C++ war und ist Byte.
3. Threads als Teil der Standardbibliothek
Irgendeine Form von Threads wird's im C++0X wohl geben. Und wenn es nur eine Spezifikation (so wie bei den STL-Containern und Algorithmen )ist.
-
versioning?
also z.b. debug/release spez. code ohne makros realisieren zu können.
(zb ein compiler assert)
-
Original erstellt von Gregor:
Verstehe ich nicht. Es ist IMHO schneller, sich mit einem vernünftigen GC Speicher für ein Objekt zu holen, als ohne.Und das versteh ich nicht. Wodurch soll Speicher schneller verfügbar sein, nur weil ein GC da ist? Nimmst Du an, in C++ werde immer sofort jede freie Speicherseite dem BS zurückgegeben? Das ist wohl kaum der Fall, wäre ja auch viel zu lahm.
-
So! Ich habe jetzt einfach mal 2 Programme geschrieben, die größtenteils das Gleiche machen. Eins in C++, das andere in Java. Es geht darum, wie schnell Objekte auf dem Heap erzeugt/gelöscht werden können. Das Ergebnis überrascht mich selbst etwas. Wahrscheinlich habe ich beim C++-Programm goßen Unsinn gemacht. Ich fange ja schließlich gerade erst mit C++ an!
C++-Programm :
main.cpp :
#include <iostream> #include <stdlib.h> #include <ctime> #include "TestClass.h" int main(int argc, char *argv[]) { int size = 3000000; long long sum = 0; clock_t time; TestClass ** array = new TestClass* [size]; time = clock(); for (int i = 0 ; i < size ; ++i) { array[i] = new TestClass(i); } std::cout << clock() - time << std::endl; time = clock(); for (int i = 0 ; i < size ; ++i) { sum += array[i]->getNumber (); delete array[i]; } delete [] array; std::cout << clock() - time; system("PAUSE"); return 0; }
TestClass.h :
class TestClass { private : int number; public : TestClass () { } TestClass (int i) : number (i) { } ~TestClass () { } int getNumber () { return number; } void setNumber (int i) { number = i; } };
Java-Programm :
TestMemory.java :
public class TestMemory { /** Creates a new instance of TestMemory */ public TestMemory () { } public static void main (String [] args) { int size = 3000000; long sum = 0; long time; TestClass [] array = new TestClass [size]; time = System.currentTimeMillis (); for (int i = 0 ; i < size ; ++i) { array[i] = new TestClass(i); } System.out.println (System.currentTimeMillis () - time); time = System.currentTimeMillis (); for (int i = 0 ; i < size ; ++i) { sum += array[i].getNumber (); array[i] = null; } array = null; System.out.println (System.currentTimeMillis () - time); time = System.currentTimeMillis (); System.gc(); System.out.println (System.currentTimeMillis () - time); System.out.println (sum); } private static class TestClass { private int number; public TestClass (int i) { number = i; } public void setNumber (int i) { number = i; } public int getNumber () { return number; } } }
Das Ergebnis ist folgendes :
Java braucht zum Erzeugen etwa 2 Sekunden, C++ mehr als 2,5 Sekunden. Java braucht zum Löschen keine 200ms, C++ braucht wieder mehr als 2,5 Sekunden. Ich denke, das Java-Programm bricht ab, bevor es mit dem Löschen fertig ist. Anders kann ich mir das nicht erklären.
-
@Gregor: wie schnell ist dein prozessor?
-
Original erstellt von volkard:
@Gregor: wie schnell ist dein prozessor?1,2 GHz P4M (bzw. 1,6 GHz, aber runtergetaktet, es lief aber auch noch was im Hintergrund. Bei beiden Programmen das gleiche, die Ergebnisse waren trotzdem reproduzierbar)
-
lole:
import c.stdio; import windows; class BigClass { int number; } int main () { const int size = 3000000; int start = GetTickCount (); BigClass [] BC = new BigClass [size]; int end = GetTickCount (); printf ("Benoetigte Zeit: %i", end-start); return 0; }
Witzigerweise ist dieser Code in D in der Debug Version um rund 40 Millisekunden schneller... (40ms)
-
Achja, an alle nette Leute, die der Garbage Collector in D stört:
Hier klicken[ Dieser Beitrag wurde am 26.11.2002 um 23:53 Uhr von Noesis editiert. ]
-
@ Noesis : Bau doch mal ein Programm, das in D tatsächlich das Gleiche macht, wie das C++-Programm. Du erzeugst ja bisher nur ein großes Array. In den anderen Programmen werden auch noch ganz viele kleine Objekte erzeugt. ...und dann wieder gelöscht!
EDIT : ...oder werden die Objekte da schon miterzeugt?
[ Dieser Beitrag wurde am 27.11.2002 um 00:03 Uhr von Gregor editiert. ]
-
Ähm, hollo, ich bin *noch* kein D Spezialist, aber mir bringt er die Fehlermeldung, wenn ich statische Arrays (arrays mit fixer Größe erzeuge, Typ [const größe] x)
"Stack overflow"
Dynamische Arrays (Typ [] x) legt er anscheinend auf dem Heap ab, aber nur wenn man sie mit new erzeugt (Typ [] x = new Typ[wieviele]), ansonsten ebenfalls am Stack
C-Arrays (Typ * x) nur mit new -> nur am Heap.
Ich werd mir mal etwas Zeit nehmen, um mir das gründlich anzuschauen...