Für Java ist es nicht so hilfreich, Objekte * zu bündeln, da der erste GC-Zyklus für noch vorhandene Objekte sie im Speicher neu mischt, sie aus dem "Eden" -Raum verschiebt und dabei möglicherweise die räumliche Lokalität verliert.
- In jeder Sprache ist es immer nützlich, komplexe Ressourcen zu bündeln, deren Zerstörung und Erstellung ähnlicher Threads sehr teuer ist. Diese können es wert sein, zusammengefasst zu werden, da die Kosten für das Erstellen und Zerstören fast nichts mit dem Speicher zu tun haben, der dem Objekthandle für die Ressource zugeordnet ist. Partikel passen jedoch nicht in diese Kategorie.
Java bietet eine schnelle Burst-Zuweisung mithilfe eines sequentiellen Allokators, wenn Sie Objekte schnell dem Eden-Raum zuordnen. Diese Strategie der sequentiellen Zuweisung ist superschnell und schneller als malloc
in C, da nur der bereits direkt zugewiesene Speicher zusammengefasst wird. Sie hat jedoch den Nachteil, dass Sie keine einzelnen Speicherblöcke freigeben können. Es ist auch ein nützlicher Trick in C, wenn Sie Dinge nur superschnell zuweisen möchten, beispielsweise für eine Datenstruktur, in der Sie nichts entfernen müssen, einfach alles hinzufügen und dann verwenden und das Ganze später wegwerfen müssen.
Aufgrund dieses Nachteils, dass einzelne Objekte nicht freigegeben werden können, kopiert der Java GC nach einem ersten Zyklus den gesamten vom Eden-Speicherplatz zugewiesenen Speicher mithilfe eines langsameren, allgemeineren Speicherzuordners, der Speicher zulässt, in neue Speicherbereiche in einzelnen Stücken in einem anderen Thread befreit werden. Dann kann es den im Eden-Raum als Ganzes zugewiesenen Speicher wegwerfen, ohne sich um einzelne Objekte zu kümmern, die jetzt kopiert wurden und an anderer Stelle im Speicher leben. Nach diesem ersten GC-Zyklus können Ihre Objekte im Speicher fragmentiert werden.
Da die Objekte nach diesem ersten GC-Zyklus fragmentiert werden können, gehen die Vorteile des Objektpoolings, wenn es hauptsächlich um die Verbesserung der Speicherzugriffsmuster (Referenzlokalität) und die Reduzierung des Zuordnungs- / Freigabe-Overheads geht, weitgehend verloren dass Sie in der Regel eine bessere Referenzlokalität erhalten, indem Sie ständig neue Partikel zuweisen und diese verwenden, während sie noch frisch im Eden-Raum sind und bevor sie "alt" und möglicherweise im Speicher verstreut werden. Es kann jedoch äußerst hilfreich sein (z. B. eine Leistung zu erzielen, die mit C in Java konkurriert), zu vermeiden, Objekte für Ihre Partikel zu verwenden und einfache alte primitive Daten zu bündeln. Für ein einfaches Beispiel anstelle von:
class Particle
{
public float x;
public float y;
public boolean alive;
}
Mach so etwas wie:
class Particles
{
// X positions of all particles. Resize on demand using
// 'java.util.Arrays.copyOf'. We do not use an ArrayList
// since we want to work directly with contiguously arranged
// primitive types for optimal memory access patterns instead
// of objects managed by GC.
public float x[];
// Y positions of all particles.
public float y[];
// Alive/dead status of all particles.
public bool alive[];
}
Um den Speicher für vorhandene Partikel wiederzuverwenden, können Sie Folgendes tun:
class Particles
{
// X positions of all particles.
public float x[];
// Y positions of all particles.
public float y[];
// Alive/dead status of all particles.
public bool alive[];
// Next free position of all particles.
public int next_free[];
// Index to first free particle available to reclaim
// for insertion. A value of -1 means the list is empty.
public int first_free;
}
Wenn das nth
Partikel nun stirbt, um es wiederverwenden zu können, schieben Sie es wie folgt auf die freie Liste:
alive[n] = false;
next_free[n] = first_free;
first_free = n;
Überprüfen Sie beim Hinzufügen eines neuen Partikels, ob Sie einen Index aus der freien Liste hinzufügen können:
if (first_free != -1)
{
int index = first_free;
// Pop the particle from the free list.
first_free = next_free[first_free];
// Overwrite the particle data:
x[index] = px;
y[index] = py;
alive[index] = true;
next_free[index] = -1;
}
else
{
// If there are no particles in the free list
// to overwrite, add new particle data to the arrays,
// resizing them if needed.
}
Es ist nicht der angenehmste Code, mit dem Sie arbeiten können, aber damit sollten Sie in der Lage sein, einige sehr schnelle Partikelsimulationen zu erhalten, wobei die sequentielle Partikelverarbeitung immer sehr cachefreundlich ist, da alle Partikeldaten immer zusammenhängend gespeichert werden. Diese Art von SoA-Repräsentant reduziert auch die Speichernutzung, da wir uns nicht um das Auffüllen, die Objektmetadaten für Reflexion / dynamischen Versand, kümmern müssen und heiße Felder von kalten Feldern trennen (zum Beispiel beschäftigen wir uns nicht unbedingt mit Daten Felder wie die Farbe eines Teilchens während des Physikdurchlaufs, daher wäre es verschwenderisch, sie in eine Cache-Zeile zu laden, nur um sie nicht zu verwenden und zu entfernen.
Um die Arbeit mit dem Code zu vereinfachen, lohnt es sich möglicherweise, eigene grundlegende Container mit veränderbarer Größe zu schreiben, in denen Arrays von Floats, Arrays von Ganzzahlen und Arrays von Booleschen Werten gespeichert sind. Auch hier können Sie keine Generika verwenden und ArrayList
hier (zumindest seit meiner letzten Überprüfung), da dies GC-verwaltete Objekte erfordert, keine zusammenhängenden primitiven Daten. Wir möchten zusammenhängende Arrays verwenden int
, z. B. nicht GC-verwaltete Arrays, Integer
die nach dem Verlassen des Eden-Raums nicht unbedingt zusammenhängend sind.
Bei Arrays von primitiven Typen, sind sie immer zusammenhängend sein garantiert, und so können Sie die äußerst wünschenswert Referenzlokalität erhalten (für die sequentiellen Partikel Verarbeitung macht es einen großen Unterschied) und all die Vorteile , die Objektverwaltung schaffen soll. Bei einem Array von Objekten ist es stattdessen etwas analog zu einem Array von Zeigern, die zunächst auf zusammenhängende Weise auf die Objekte zeigen, vorausgesetzt, Sie haben sie alle gleichzeitig im Eden-Raum zugewiesen, können aber nach einem GC-Zyklus überall auf das Objekt zeigen in Erinnerung behalten.