Wie heißt der Nachfolger ...
-
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...
-
Das hört sich ja so an, als ob man auch Dinge gezielt auf dem Stack erzeugen kann. Ist das bei Objekten vielleicht auch möglich? Das würde ja heißen, dass die Diskussion oben umsonst war! ...ein Einwand weniger gegen D!
-
Sieht seltsam aus, weil features to drpp: "Creating object instances on the stack. ..."
bin ich
auf jedenfall kann man sich den Garbage Collector abstellen, und auch selbst bestimmen, wann er wie reinigen soll (gehört zur Standard Library)
-
hab auch mal gemessen.
um sicherzustellen, daß er sum auch berechnet, hab ich int(sum) noch ausgegeben.
compiliert auf msvc60, gemessen auf celeron400.
2543
3645hmm. mein rechner scheint ungefähr so schnell zu sein, wie deiner. du hast vielleicht auch nen celeron400.
mal schauen, was java da angestellt hat.
testhalber bau ich mal in die klasse das hier ein:void operator delete(void* p)
{
}2563
371aha. java hat also ein wenig mehr gemacht also nix.
mal in der main noch einfügen array[i]=0;
2683
441jo, das könnte es sein.
als wenn java da echt noch nix gelöscht hätte.
vielleicht bewirkt System.gc() hier noch kein echtes freigeben.
mal nen op new bauen, der zum free paßt.
void* operator new(size_t s)
{
if(free)
{
void* result=free;
free=*reinterpret_cast<void**>(free);
return result;
}
return ::operator new(s);
}
und das für c++ untypische array[i]=0; weg:2763
691vielleicht ist es auch das, was java gemacht hat.
public void gc()Runs the garbage collector. Calling this method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse.
und da die letzten 3000000 allokierungen ne TestClass angelegt haben, wärs ja eine ganz logische annahme, daß die nächsten 3000000 allokierungen wieder TestClass anlegen. Und Blöcke zusammenfassen kann man immernoch, wenn andere größen angefordert werden.
hier hab ich in c++ natürlich das problem, daß der speicher echt allokiert bleibt. und nen hintergrundprozess absetzen, der in idle-zeiten echt löscht, führt mich sofort in mehrkosten wegen multithreading und so. ihr habt das gleich dabei, was java hier recht lecker macht.
mal nen test machen, wo die freien blöcke auch wiederverwendet werden.
int main() { int size = 3000; __int64 sum = 0; clock_t time; time = clock(); for(int j=0;j<3000;++j) { TestClass ** array = new TestClass* [size]; for (int i = 0 ; i < size ; ++i) { array[i] = new TestClass(i); } for (int i = 0 ; i < size ; ++i) { sum += array[i]->getNumber (); delete array[i]; } delete [] array; } std::cout << clock() - time<<std::endl; std::cout << int(sum) <<std::endl; system("PAUSE"); return 0; }
17505
uih.
und mit selbsgebautem new und delete mit verketteter liste freier blöcke:
741was macht java hier? ich nehme an, auch schnell, aber ohne, daß der user so lästige sachen wie eigenes new/delete bauen muß?
-
Original erstellt von Gregor:
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)das läßt mich befürchten, daß was an der messung nicht ok ist, denn mein celeron400 hatte fast die gleichen zeiten fürs anlegen. evtl nicht alle optimierungen beim compiler an? ka. oder die zeit geht für die RAM-Zugriffe drauf und der Prozessor dreht größtenteils Däumchen.
-
Original erstellt von volkard:
**
hmm. mein rechner scheint ungefähr so schnell zu sein, wie deiner. du hast vielleicht auch nen celeron400.
**...oder vielleicht ist der Flaschenhals hier ganz wo anders! ...ich tippe auf die Speicherperformance!
EDIT : Vielleicht sollten wir mal ein kleines Array nehmen, was in den Cache paßt und welches dann sehr oft gefüllt und wieder geleert wird.
[ Dieser Beitrag wurde am 27.11.2002 um 00:36 Uhr von Gregor editiert. ]
-
btw eigentlich sollte das hier nicht zum Kampf zwischen Java und C++ ausarten...
naja, in D ist auf jedenfall
Blabla x;
und
Blabla x = new Blabla;
sowie
Blabla *x = new Blabla;
erlaubt...
confusing, ich find aber einfach nicht genug infos.
kann sein, dass ich schon recht müde bin...
die spezif. sagt auch, dass D keine Objekte mehr am Stack anlegt.
Aber wie kommt es dann zu o.g. Stack Overflow? Hab ich was falsch gelesen?
-
Original erstellt von Noesis:
**btw eigentlich sollte das hier nicht zum Kampf zwischen Java und C++ ausarten...
**Es ist ein Kampf GC gegen nicht GC! ...den ich mit den neuen Programmen bestimmt gewonnen habe! ...kommt gleich!
-
OK
-
neue main.cpp :
#include <iostream> #include <stdlib.h> #include <ctime> #include "TestClass.h" int main(int argc, char *argv[]) { int size = 500; long long sum = 0; clock_t time; TestClass ** array = new TestClass* [size]; int i,j; time = clock(); for (j = 0 ; j < 200000 ; ++j) { for (i = 0 ; i < size ; ++i) { array[i] = new TestClass(i); } for (int i = 0 ; i < size ; ++i) { sum += array[i]->getNumber (); delete array[i]; } } delete [] array; std::cout << clock() - time; system("PAUSE"); return 0; }
neue TestMemory.java :
public class TestMemory { /** Creates a new instance of TestMemory */ public TestMemory () { } public static void main (String [] args) { int size = 500; long sum = 0; long time; int i,j; TestClass [] array = new TestClass [size]; time = System.currentTimeMillis (); for (j = 0 ; j < 200000 ; ++j) { for (i = 0 ; i < size ; ++i) { array[i] = new TestClass(i); } for (i = 0 ; i < size ; ++i) { sum += array[i].getNumber (); array[i] = null; } } array = null; 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; } } }
Ergebnis : Java braucht etwa 7 Sekunden, C++ braucht etwa 2 Minuten.
EDIT : Hier wird der GC mit Sicherheit aktiv, da der Speicher, den Java zur Verfügung hat, sonst nicht ausreicht.
[ Dieser Beitrag wurde am 27.11.2002 um 00:54 Uhr von Gregor editiert. ]
-
der Java GC bringt sicherlich etwas Speed beim Anlegen von Objekten, da er den Heap nicht so fragmentiert, aber es ist uns doch klar dass das defragmentieren selbst Speed kostet und ein GC insgesamt mehr kostet, dafür hat der GC den Vorteil das es beim anlegen schneller ist, also uns da Speed bringt wo es wichtig ist.
Und der Test wird verfälscht, das kann man schon an den Ergebnis ablesen, was ist wahrscheinlicher das Java 17 mal schneller ist oder das der gc nicht das macht was man erwartet.und ein Small Objekt Allocator sollte auch C++ ein guten Sprung nach vorne verpassen.
[ Dieser Beitrag wurde am 27.11.2002 um 01:11 Uhr von Dimah editiert. ]
-
Naja! ...wenn ich j bis 400000 statt bis 200000 laufen lasse, dann steigt die Zeit bei beiden Programmen linear an. Bis wo soll ich j denn laufen lassen, damit du davon überzeugt bist, dass der GC sehr aktiv gewesen sein muss?
...im Übrigen stimme ich nicht damit überein, dass der GC insgesamt mehr Zeit brauchen muss. Er kann alle Objekte auf einmal behandeln. Sowas kann von vorteil sein. Außerdem defragmentiert der GC nicht. Der Speicher wird einfach nicht fragmentiert. Der GC muss also garnicht defragmentieren.
-
136055, also 2:16 min. Der Flaschenhals liegt irgendwo anders.
mit eigenem op new/delete:
5748uih, was hätte ich viel rausholen können, wenn ich damals den messages noch nen richtigen allocator verpaßt hätte. so hab ich nur in die doku geschrieben, daß meine nachfolger das mal machen sollten, die habens aber sicherlich nicht getan.
also sowas darf man mit ungetuntem c++ ja gar nicht machen. und ich hatte immer angenommen, der msvc würde bereits für kleine objekte sonderbehandlungen machen.
also ich gebe zu, daß der user bei java mit dem gc keinen bedarf hat, sich irgendwelche gedanken zu machen, obs schneller ginge, man nimmt die standard-sachen und hat bereits was sehr sehr gutes.
in c++ ohne gc kann man schneller sein, aber zu vielfachem programmieraufwand. den irgendjemand mal macht, und man inkludiert dann das zeugs. ich vermute jetzt, daß es nicht wirklich am gc liegt, sondern daß java nen small object allocator hat, der immer gleich nen batzen auf einmal vom bs holt (4096 bytes wäre wohl klug, weils eine speicherseite ist), und dann da die neu zu erzeugenden objekte einfach reinsetzt wie in ein array. das sollte eigentlich völlig ausreichen, um die hier sichtbare performance zu bringen.
@hume: dein loki-port ist so vollständig, daß der small object allocator klappt?
-
Original erstellt von volkard:
ich vermute jetzt, daß es nicht wirklich am gc liegt, sondern daß java nen small object allocator hat, der immer gleich nen batzen auf einmal vom bs holt (4096 bytes wäre wohl klug, weils eine speicherseite ist), und dann da die neu zu erzeugenden objekte einfach reinsetzt wie in ein array.Ich weiß nicht genau, wie Java das macht. Aber zumindest gibt Java Speicher, den es einmal hat, nicht mehr so schnell an das Betriebssystem zurück. Der Speicher wird dann wohl nur Java-intern frei gemacht. ...und unter Umständen irgendwann wieder belegt. Vielleicht reicht das schon aus.
-
@hume: dein loki-port ist so vollständig, daß der small object allocator klappt?
Jo. Der sollte problemlos funktionieren. Nur haben mir mittlerweile einige Experten mitgeteilt, dass der Allokator nicht so toll sein soll, wie man sich das wünscht. Ich habe das irgendwann auch mal getestet, kann mich aber nicht mehr wirklich an das Ergebnis erinnern. Vom Hocker gehauen hat's mich aber auf jeden Fall nicht.
Man müsste mal schauen, wie schnell die Boost pool-Allokatoren sind.
[ Dieser Beitrag wurde am 27.11.2002 um 01:48 Uhr von HumeSikkins editiert. ]
-
Original erstellt von Gregor:
Bis wo soll ich j denn laufen lassen, damit du davon überzeugt bist, dass der GC sehr aktiv gewesen sein muss?Er wurde aktiv, keine Frage! Wäre er nicht aktiv geworden, wärste voll in den Speicherflaschenhals gerannt. Mag sein, daß er weiß, wie groß der Cache ist, und bei new aufgerufen wird, wenn der Cache voll ist, damit top performance bei solchen Anwendungen garantiert ist. Ich traue da Deinem GC inzwischen viele tolle Tricks zu.
...im Übrigen stimme ich nicht damit überein, dass der GC insgesamt mehr Zeit brauchen muss. Er kann alle Objekte auf einmal behandeln. Sowas kann von vorteil sein.
Kann sein, weil die Schleifen schneller brummen, wenn sie nicht auch noch mit Anwendungscode durchsetzt sind. Und kann auf jeden Fall sein, wenns ein anderer Thread auf nem anderen Prozessor macht. Kann aber auch sein, daß bei sofortigem Löschen mehr Cache-Treffer passieren.
Außerdem defragmentiert der GC nicht. Der Speicher wird einfach nicht fragmentiert. Der GC muss also garnicht defragmentieren.
Für Top-Speed in dieser Anwendung muß ich erst nen small object allocator besorgen. Kann aber sein, daß ich erst einen selber bauen will, was dann bestimmt bis ins neue Jahr dauert. Ich melde mich auf jeden Fall wieder.
Und dann wärs spannend, ne Anwendung zu bauen, die drauf ausgelegt ist, den Speicher gnadenlos zu fragmentieren, und mal zu gucken. Das könte dann den Beweis erbringen, daß es Anwendungen gibt, die nen echten GC wie in Java benötigen.
-
Original erstellt von volkard:
**
also ich gebe zu, daß der user bei java mit dem gc keinen bedarf hat, sich irgendwelche gedanken zu machen, obs schneller ginge, man nimmt die standard-sachen und hat bereits was sehr sehr gutes.
**Bei anderen Dingen, die man programmiert ist es genau andersherum. Da ist man dann mit Java viel langsamer als mit C++, kann Java aber mit entsprechenden Optimierungen prozentual besser beschleunigen, als C++.
Es wundert mich, dass folgender Hauptnachteil eines GC noch nicht genannt wurde:
Wenn man einen GC hat, dann ist das Programm in dre Regel nicht echtzeitfähig, da der GC letztendlich jederzeit aktiv werden kann und so eine große Pause produzieren kann. Man kann sich zwar bemühen, den GC in Programmteilen aufzurufen, in denen man keine Performance braucht, IMHO kann man aber nicht 100%ig festlegen, dass der GC nicht irgendwann an einer Stelle aktiv wird, an der er nicht aktiv werden soll.
-
Original erstellt von volkard:
**
Für Top-Speed in dieser Anwendung muß ich erst nen small object allocator besorgen. Kann aber sein, daß ich erst einen selber bauen will, was dann bestimmt bis ins neue Jahr dauert. Ich melde mich auf jeden Fall wieder.Und dann wärs spannend, ne Anwendung zu bauen, die drauf ausgelegt ist, den Speicher gnadenlos zu fragmentieren, und mal zu gucken. Das könte dann den Beweis erbringen, daß es Anwendungen gibt, die nen echten GC wie in Java benötigen.**
Da bin ich mal gespannt!