CodingCat hat geschrieben:Ubseht hat geschrieben:CodingCat hat geschrieben:Aramis hat geschrieben:Um die Auswirkungen zu entspannen haben wir hier eben begonnen, unsere Datenstrukturen zu ent-OOisieren, sprich dafuer geeignete Objekte auf rohe Datenarrays aufzusplitten (damit M&S schneller laeuft).
Jep, genau das macht mich immer noch sprachlos. Auch in Java hieß für uns Optimierung damals zurück auf eine Sprachebene, die noch klar unter C anzusiedeln ist.
Bitte gibt uns doch eine Erläuterung dieser Sprachebene und welche Optimierungen ihr dadurch denn angeblich erzielt haben mögt.
Das Fehlen von Value Types zwang uns dazu, alle Daten in Form von Arrays primitiver Datentypen abzulegen. Aus Arrays von Objekten wurden somit viele Arrays von Objektattributen. Selbst in C kann ich Objekte in Form von
structs in Arrays packen, ohne damit das Vielfache an Speicher zu verschwenden.
Ja Value Types gibt es nicht. Insofern stimmt deine Aussage bezüglich der primitiven Sprachebene um Speicher zu sparen. Dachte ihr hättet irgendwas wilderes gemacht. Anstelle von Arrays solltet ihr übrigens lieber nio-Buffer verwenden. Diese haben folgende Vorteile:
- - Ihr Speicher kann mittels "allocateDirect" angelegt werden. Die JVM versucht dann den Zugriff zu optimieren. Z.B. kann die JVM unter umständen den Speicher außerhalb des GC-Heaps anlegen.
- Die ByteBuffer-Implementierung kommt auch mit anderen primitiven Typen außer byte zurecht (also floats, doubles, chars, ints, longs, shorts). Die Typen lassen sich mischen -> Man kann mittels einer Fassade structs nachbilden.
- Auf nio-Buffer lässt sich leicht mittels jni zugreifen. Siehe: http://docs.oracle.com/javase/7/docs/te ... er_address
- Frameworks wie http://www.lwjgl.org/ (Unter anderem OpenGL Zugriff für Java) verwenden nio-Buffers. Die Buffer Daten lassen sich also relativ einfach visualisieren.
Da obendrein Generics für primitive Datentypen nicht unterstützt werden, mussten wir uns zusätzlich noch eigene (Hash- und Queue-)Container-Klassen schreiben, die wir dann zu allem Überfluss für verschiedene primitive Typen per Copy'n'Paste vervielfältigen durften.
Recht bekannt in der Java Community ist das Trove-Projekt. Dieses enhält Container-Implementierungen für Primitivtypen.
Projekt:
http://trove.starlight-systems.com/home
JavaDoc:
http://trove4j.sourceforge.net/javadocs/
Falls ihr wieder ähnliche Container braucht, könnt ihr ja evaluieren, ob die Trove-Versionen euren Ansprüchen genügen. Neben Trove gibt es übrigens auch weitere ähnliche Implementierungen. Falls eure Container besser sein sollten, könnt ihr ja überlegen, Java dadurch zu verbessern, dass ihr eure veröffentlicht (z.B. GitHub).
BTW habt ihr Java also doch auch in einer Sprachebene oberhalb Cs verwendet. ;)
Davon abgesehen ist auch bei uns ein Vorberechnungsalgorithmus auf einem 48-Core-Rechner über 16 Stunden hinweg alle paar Sekunden auf 0% Auslastung gefallen. Woran das lag, konnten wir allerdings Aufgrund des stark eingeschränkten Zugangs zu diesem Rechner nicht weiter analysieren. Mein Tipp wäre auch hier der GC, da die Berechnungen davon abgesehen kaum Synchronisationspunkte enthielten.
In der Sun-Implementierung der JVM (HotSpot) gibt es einen Parallel-GC. Bei diesem sollte die Auslastung dann nicht bei 0 liegen. Falls ihr das einmal testen möchtet, kannst du mich gerne noch nach Details dazu fragen.
Aramis hat geschrieben:
GC in Java und .net ist zwar nicht das, was du gerne hättest, aber tolerabel?
Ja, so koennte man es formulieren :-)
Gut, das ist eine legitime Meinung und ich muss keine Missionierungsversuche unternehmen.
Sofern es im .net Compact Framework etwas dem StringBuilder oder StringBuffer aus Java ähneldem gibt, wäre dies eine Möglichkeit diese Problem sauber und sprachgerecht zu lösen.
Gibt es natuerlich - aber ist voellig nutzlos weil StringBuilder.ToString() eben doch ein neues Objekt erstellt :-) Genau das ist der Punkt: sobald der GC das Bottleneck wird, findet man sich ploetzlich im tiefsten Mittelalter wieder die meisten, an sich gut durchdachten, Sprachmittel und grosse Teile der Standardbibliotheken ploetzlich auf der Blacklist stehen.
Naja völlig nutzlos ist es eben deshalb nicht, weil nicht pro Stringoperation ein neuer String erstellt wird, sondern erst am Ende. Je nach dem was ihr macht, können das also massive Einsparungen sein.
Jedoch sind zumindest in normalen Java temporäre Objekte einer der Vorteile von GC. Der GC muss schließlich solche Objekte nicht kopieren und hat daher mit Solchen nur wenig arbeit.
Ich hab erhebliche Zweifel dass ein Mark&Sweep GC die Dekonstruktion vieler temporaerer, hochlokaler Objekte schneller hinkriegt als eine deterministische Speicherverwaltung, die einfach am Ende des Scopes den Speicher freigibt. In beiden Faellen muessen wenige, kleine und vermutlich hintereinanderliegende Speicherbloecke schnell zugewiesen und am Ende wieder freigeben werden - nur dass der GC noch zusaetzlichen Verwaltungsaufwand hat. Ein GC hat hoechstens einen etwas besseren globalen Ueberblick – wobei den auch eine deterministische Heap-Implementierung haben kann (LFH o.ae.).
Es ging mir nicht darum, dass der GC schneller als eine manuell angepasste Speicherverwaltung ist, sondern, dass die Beseitigung von temporären Objekten, das ist was der GC besonders gut kann. Objekte die lange, aber nicht bis zum Ende des Programmes leben, sind das Gegenstück (machen dem GC Probleme).
Die modernen Java-GCs arbeitet übrigens anders. Sie geben nichts frei, sondern vewenden den selben Speicher neu. Arbeit gibt es daher nur mit noch lebenden Objekten, da diese kopiert werden müssen. Dieses Verhalten ließe sich natürlich auch manuell in C/C++ nachbilden.