# Performance Objektallokation



## DrMotha (10. Mai 2011)

Ich muss ungefähr 20 Mal in der Sekunde rund 6x250000 also ca. 1,5 Mio Objekte anlegen (einer Vector3-Klasse, die drei floats enthält). Derzeit ist der Vector3 immutable, dementsprechend auch die vielen Objekte.

Ist es besser mutable Objekte zu verwenden oder ist die Objektallokation nicht das Bottleneck sondern die Berechnungen? Wie kann ich am besten mit Hilfe des NetBeans-Profilers herausfinden ob die Objektallokation das Bottleneck meiner Applikation ist?

Die CPU-Zeit sit noch nicht auf 100% obwohl ich genügend Threads liefere damit es alle Cores ausnutzen könnte.

Es geht um einen Rasterizer, und hier besonders um die Performance des Pixel-Shaders. Also da wird pro Pixel (500x500 davon) eine gewisse Anzahl an Vector-Multiplikationen durchgeführt.

Hoffe ihr könnt mir da etwas unter die Arme greifen bzw. habt gute (und vor allem aktuelle) Artikel für mich.

Vielen Dank im voraus!

LG
Bernhard


----------



## DerEisteeTrinker (10. Mai 2011)

Grundsätzlich ist eine Objektallokation zeitintensiver als eine Wertzuweisung. Man müsste aber überprüfen, ob es sinnvoller ist, wenn du die Objekte vorhälst in einer Datenstruktur. Denn bei so vielen Objekten könnte ich mir vorstellen, dass das "Objekt suchen" auch einiges an Zeit beansprucht.

Miss einfach mal mit System.nanoTime() die Zeit. Das mehrfach hintereinander und dann veränder die Implementierung. Schau dir aber auch an, wie sich der Speicher verhält und ob die Verteilung noch ok ist für den Anwendungsfall. Veränder auch die HotSpot-Variante von Client auf Server oder andersherum. Bei Server brauch er etwas länger mit dem Start der Anwendung, weil er aggressiver von sich aus optimiert. Das kann dir unter Umständen noch einiges an Zeitersparnis bringen.


----------



## maki (10. Mai 2011)

Verlässliche Daten über Bottlenecks bekommt man nur mit einem Profiler.
Getting Started with VisualVM &mdash; Java.net

Möglicher Vorteil von Immutables: Man kann sie wiederverwenden, wenn sie inhaltlich gleich sind.


----------



## Marco13 (10. Mai 2011)

Bis vor kurzem hätte ich auch gesagt, dass die Objektallokation teuer sein kann. Sowas wie "Object pooling" wird grundsätzlich nicht empfohlen, die neuen JVMs optimieren da schon ganz gut. Aber neulich auf der JAX hat Brian Goetz in einem Vortrag angedeutet, dass Performance, die _vermeintlich_ durch Allokationen verloren geht, auch durch Caching-Effekte verloren gehen kann. Ich vermute, dass das auch vom konkreten Anwendungsfall abhängt, aber ... man könnte pauschal sagen: "Er wird's wohl wissen"...

Das Problem bei solchen Dingen ist, dass da auch ein Profiler nur bedingt weiterhelfen kann. Solche Effekte sind schwer zu messen. Aber natürlich wäre ein Profilerlauf der erste Ausgangspunkt.

Die Empfehlung, die OO-Puristen jetzt vielleicht weh tut: Bei wirklichen Low-Level-High-Performance-Aufgaben ist und bleibt ein echter, roher float[3*n]-Array immer noch das schnellste. (Dass man den später leichter an die GPU schicken kann ist nur ein Teilaspekt, der jetzt vielleicht noch nicht relevant ist). Das heißt aber nicht, dass man den nicht "so schnell wie möglich" (d.h. möglichst direkt nach der "Schicht", die die low-level-Berechnungen macht) hinter irgendeinem Interface verstecken sollte.


----------



## Ergometh (13. Mai 2011)

Wie bereits genannt ist es ratsam, erst einmal festzustellen, ob die Objekterzeugung und Einsammlung durch den Garbage Collector das Problem ist.

Das Erzeugen derartig vieler Objekte pro Sekunde halte ich jedoch für sehr bedenklich. Immutables werden meist als eine Art Krücke verwendet weil Java leider keinerlei strukturierte Wertetypen anbietet.

Datenobjekte mit denen viel "gerechnet" wird würde ich niemals als immutable implementieren.

Java müllt stets den Speicher voll um dann bei der garbage collection herauszusuchen, welche Objekte nicht mehr referenziert werden. Je mehr noch referenzierte Objekte es allerdings gibt, umso langsamer dürfte auch das werden - bedingt durch den verwendeten Algorithmus.

Sehr große Felder von Datenobjekten lohnen ggf. auch der "Einbettung". Warum nicht einfach alle xyz-Werte von tausenden von Vektoren in einem Array ablegen. Dies ist natürlich eine reine Performanceoptimierung und erfordert mehr Programmieraufwand, beispielsweise um die xxz-Werte dann in die Vektoren zu kopieren. 

Anhänger der "reinen" objektorientierten Lehre werden hoffentlich alle Haare grau. Aber ich denke es geht um problemlösende Anwendungsentwicklung.


----------



## Empire Phoenix (13. Mai 2011)

Immutables können allerdings effizienter garbagecoolected werden unter umständen.

Zb. wenn die nur local existieren als zwischenergebniss sind neue garbagecollectoren durchaus in der lage die direkt zu entsorgen.


----------



## Empire Phoenix (13. Mai 2011)

Ansonsten gibt es die JSTackAlloc bibliothek, die es ermöglicht object pools sehr kompfortabel einzubauen (im sinne von das die per bytecode manipulation sich um vieles kümmert und man nur andeuten muss das die objecte daraus sein sollen. Wird zb von der java implementierung der bullet physic engine benutzt, da diese enorme mengen an zwischenergebniss objecten hat.


----------



## Marco13 (14. Mai 2011)

@Ergometh: Da hat sich einiges gatan, die Leute die die GC's schreiben sind nicht blöd  Ich glaube unter dem Stichwort "Escape Analysis" wird ein Verfahren verstanden, bei dem der GC feststellen kann, ob ein Objekt gelöscht werden muss oder wieder verwendet werden kann, al GANZ einfacher Fall sowas wie

```
for (int i=0; i<100; i++)
{
    Vector3f v = new Vector3f(i,i,i);
    result.add(v);
}
```
aber eben auch in deutlich komplizierteren Zusammenhängen. Trotzdem habe ich auch die Tendenz eher zu sagen: Wenn man ihm Arbeit sparen kann, kann man das ja machen, sofern es keine "premature Optimization" ist.


----------



## Ergometh (14. Mai 2011)

Bzgl. Marco13: 

Escape Analysis ist ein interessanter Hinweis und sicher eine Verbesserungsmöglichkeit des Compilers, Interpreters oder Just-In-Time-Compilers für die Performance der Garbage Collection. Letztlich verbessert man damit aber lediglich ein Problem das man sich selbst eingebrockt hat. Und das Erzeugen eines neuen Objektes kostet leider auch wieder einiges.

Ich halte automatisches Speichermanagement und Garbage Collection für absolut wünschenswert für die Anwendungsentwicklung. Die Verwendung von Immutables zum "herumrechnen" ist jedoch grundsätzlich ein Fehler. Wie generell das massenhafte Erzeugen temporärer kleiner "Datenobjekte".

Das Fehlen von (strukturierten) Wertetypen bzw. einer vergleichbaren Semantik von Call-By-Value und Wertezuweisung schafft in Java leider oftmals das Problem zusätzlichen Kodieraufwands mit entsprechenden zusätzlichen Fehlerquellen. C# bietet hier "struct" als Lösung. Dann kann man letztlich bezüglich Wertetypsemantik arbeiten wie in C/C++. Ohne Slicing-Risiko da ohne Vererbung. Aber das hilft dem Fragesteller alles nichts ...


----------



## slawaweis (14. Mai 2011)

DrMotha hat gesagt.:


> Ist es besser mutable Objekte zu verwenden oder ist die Objektallokation nicht das Bottleneck sondern die Berechnungen? Wie kann ich am besten mit Hilfe des NetBeans-Profilers herausfinden ob die Objektallokation das Bottleneck meiner Applikation ist?


bei solchen Problemen ist es immer gut ein Framework zu haben, welches flexibel ist. Neben der immutable Klassen, sollte es auch äquivalente mutable Klassen, sowie eine reine Array-Lösung geben. So kann man verschiedene Ansätze miteinander vergleichen und abwiegen. Nur die immutable Variante zu messen gibt keine Aussage, ob eine andere Variante effektiver oder einfacher zu programmieren wäre.

Slawa


----------



## Kr0e (14. Mai 2011)

Ich bin da gleicher Meinung wie Marco13. Außerdem könnte es doch sein, dass die JVM sowieso immutable Objects cached. Sprich in der JVM sind die vlt. garnicht immutable und können bei einer neuen Erstellung aus dem Cache genommen werden. Nur auf Javaebene sind die dann immutable.. Ich denke, dass da unheimlich viel optimiert wird, sonst würde man das auch merken...  Erstell mal 10 Millionen String mit einem sich immer veränderndem Inhalt... Ich glaub nicht, dass 10 Millionen String erstellt und gelöscht werden.. Dafür geht das meiner Ansicht nach viel zu fix bei Java. 

ABER: Das ist grad reine Spekulation... 

Gruß,

Chris


----------



## tfa (14. Mai 2011)

Besonders kurzlebige Objekte sind aus GC-Sicht praktisch kostenlos, Stichwort Generational GC. Ich sehe keinen Grund, eine Sprache mit überflüssigen Features wie Wert-Objekten zu verunreinigen, nur um eventuell "premature optimization" betreiben zu können. Das schafft mehr Probleme als dass es löst.


----------



## Spacerat (14. Mai 2011)

Genau dieses Problem hatte ich auch schon mal und zwar im 3D Bereich mit Morphing, allerdings mit dem Unterschied, dass meine Vector3-Klasse (bzw. Point3-Klasse) nicht immutable war. Ob nun die Objektallozierung von mutables teurer als die von immutables ist, kann ich nicht sagen, aber zumindest hat es mir sehr geholfen, ein temporäres 3D-Objekt mit gleicher Anzahl Vertices (jene mutable Point3-Klasse) zu erstellen um die einzelnen Morph-Werte darin ständig zu aktualisieren. Das hatte den Vorteil, dass nicht ewig neue Objekte erstellt wurden und der GC damit weitgehend entlastet wurde. Die Performance steigerte sich je nach verwendeter Hardware (ATI 9600, ATI X1950 und NVidia 260GT) vom 10-Fachen bis ins geschätzt (FPS-Anzeige wurde bei der NVidia-Karte gesprengt ) 1000-Fache.

@Kr0e: Gerade im 3D-Bereich, vorzugsweise bei Matritzen, Vektoren, Points und ähnlichem (vorallem bei Strings ) scheint Objekt-Caching sehr wenig Sinn zu machen. Es gibt leider viel zu viele Objektmöglichkeiten, so dass dann entweder sehr viel Cache-Speicher benötigt oder der Cache viel zu oft bereinigt werden müsste um Platz für andere Objekte zu schaffen. Ist vorstellbar, das beide Aspekte performancemässig unheimlich teuer werden würden.


----------



## Kr0e (15. Mai 2011)

Ich habe nicht gesagt, dass man selber cached, sondern, dass ich vermute, dass die JVM intern cached bis zu einem gewissen Limit, nicht mehr und nicht weniger  Mach doch einfach einen Test und dann sag nochmal, dass die Objekterstellung das Problem ist... 
Ich habe jedenfalls noch nie Probleme gehabt in dieser Richtung und bezweifele es daher stark!

Kleines Beispiel zu ByteBuffers: Ich dachte immer, dass es vlt Sinn macht, benutzte ByteBuffer-Objekte wiederzuverwenden... Im Endeffekt war es effektiver, immer ein neues objekt zu erstellen... DAs kann nur heißen, dass die Buffer intern wiederverwendet werden... Ich glaube einfach, der Javaprogrammierer muss sich umn sowas kaum Gedanken machen, da die Genies von Sun/Oracle alles tun, damit selbst der schlimmste Code gut läuft 

Beziehen sich deine Erfahrungen vlt auf ältere JVMs ?


----------



## maki (15. Mai 2011)

> Ich glaube einfach, der Javaprogrammierer muss sich umn sowas kaum Gedanken machen, da die Genies von Sun/Oracle alles tun, damit selbst der schlimmste Code gut läuft


Normalerweise läuft das genau so, der Java Programmierer schreibt simplen Code, optimiert wird von der VM.

Falls das nicht reicht, kann man immer noch profilen.

Hypotetische "was wäre wenn" Diskussionen können mitunter sehr unterhaltsam sein, aber wirklich optimieren kann man nur mit einem Profiler, nicht mit theoretischen Diskussionen.


----------



## Spacerat (15. Mai 2011)

Kr0e hat gesagt.:


> Beziehen sich deine Erfahrungen vlt auf ältere JVMs ?


Auf die Versionen 1.5 und 1.6. Nehmen wir mal an, es sollen 1,5 Mio mal pro Sekunde Vector3 mir 3 Flieskommawerten erstellt werden, das wären 4,5 Mio *floats* bzw. 18 Mio Bytes pro Sekunde also eine Datenrate von rund 18MB/s. Glaube kaum, dass eine VM aus Optimierungsgründen Objekte Cached geschweige denn überhaupt Cachen kann, wenn in der nächsten Sekunde Die selbe Anzahl jedoch komplett unterschiedliche Werte an Bytes aufkommt. Der GC wird mit Sicherheit auch nicht einmal pro Sekunde gestartet. Die Datenrate ist dabei natürlich von im- bzw. mutable völlig unabhängig. Für mich klingt es deswegen nicht zuletzt nur wegen meiner damaligen Ergebnisse vollig einleuchtend, dass die VM eine derartige Datenflut selbst bei genialster Implementierung gar nicht schafft. Im übrigen ist's dabei auch egal, wo gecached wird.
@maki: So hypothetisch finde ich diese Diskussion gar nicht, zumal mir bereits meine Ergebnisse vorliegen.


----------



## Ergometh (15. Mai 2011)

Bzgl tfa:

Kurzlebige Objekte im Freispeicher zu erzeugen kann in bestimmten Fällen wirklich erstaunlich performant sein. Inwiefern praktisch kostenlos, das würde ich gern wissen.

Wenn es grundsätzlich so unproblematisch mit kurzlebigen Objekten ist stellt sich mir auch die Frage warum es die Methode von ...awt.Component

```
Dimension getSize( Dimension result );
```
als Alternative zu

```
Dimension getSize();
```
gibt.

Inwiefern das Vorhandensein einer Eigenschaft, wie Wertetypen eine "Verunreinigung" darstellt muss jeder für sich entscheiden.

Meine Zielsetzungen sind Entwicklungs- und Laufzeiteffizienz. Wobei die Entwicklungseffizienz meist wichtiger ist. Leider ist auch der Programmieraufwand durch Fehlen von Wertetypen i.d.R. höher. 

Java arbeitet leider ausschließlich mit impliziten Zeigern (im Java-Kontext Referenz genannt) auf Objekte. Das Hantieren mit Zeigern und Call-By-Reference sind fehleranfällig. Um diese Fehler zu vermeiden hat man dann die Immutables auf breiter Front in Java eingesetzt. Ich denke C# hat hier aus den Fehlern von Java gelernt. 

Bzgl. "Object Caching":

Object Caching im Sinne des Vorhaltens von Objekten im Nutzzustand, die dann anhand von Schlüsseleigenschaften gefunden werden bringt hier sicher nichts.

Ganz anders sieht es mit Object Reuse aus. Dies kann bei massenhaften kleinen Objekten, die immer wieder neu erzeugt werden große Performancegewinne bringen. Hier werden Objekte eines Typs (einer Klasse) unabhängig von ihrem Zustand zur Wiedernutzung in einem Pool gespeichert.


----------



## Kr0e (15. Mai 2011)

Ergometh hat gesagt.:


> Ganz anders sieht es mit Object Reuse aus. Dies kann bei massenhaften kleinen Objekten, die immer wieder neu erzeugt werden große Performancegewinne bringen. Hier werden Objekte eines Typs (einer Klasse) unabhängig von ihrem Zustand zur Wiedernutzung in einem Pool gespeichert.



Das meinte ich eigentlich auch so... Hab mich mit Caching ein bisschen doof ausgedrückt .. =(


----------



## slawaweis (15. Mai 2011)

ich habe einen kleinen Benchmark geschrieben, speziell für Vektoren, vecmath.jar aus Java3D wird dafür benötigt. Ich habe es auf einem Q6600, JDK 1.6.0_24 und -Xms256m -Xmx512m mehrmals ausgeführt. Die Ergebnisse jeweils für Client und Server:


```
Client Immutable   6750 ms
Client Mutable     5688 ms

Server Immutable   5609 ms
Server Mutable     5515 ms
```

es scheint, dass die mutable Variante meines Tests etwas schneller ist, weil sie nur zwei Vektoren neu anlegt und nicht drei.

Der Quelltext, als Argument kann "0" für den immutablen und "1" für mutablen Test übergeben werden.


```
package performance;

import java.util.*;

import javax.vecmath.*;

public class OATest
{
 public static void main(String [] args)
  {
  if(args.length == 0 || args[0].equals("0"))
    {
    immutableTest();
    }
  else
    {
    mutableTest();
    }
  }

 public static void immutableTest()
  {
  long start = System.currentTimeMillis();
  immutableTest(10000000);
  long stop = System.currentTimeMillis();

  System.out.println("Immutable: " + (stop-start) + " ms");
  }

 public static void mutableTest()
  {
  long start = System.currentTimeMillis();
  mutableTest(10000000);
  long stop = System.currentTimeMillis();

  System.out.println("Mutable: " + (stop-start) + " ms");
  }

 public static void immutableTest(int count)
  {
  Random r = new Random();
  Vector3d v1, v2, v3;
  Vector3d [] array = new Vector3d[count];

  for(int i = 0; i < count; i++)
     {
     v1 = new Vector3d(r.nextDouble(), r.nextDouble(), r.nextDouble());
     v2 = new Vector3d(r.nextDouble(), r.nextDouble(), r.nextDouble());

     v3 = new Vector3d();
     v3.add(v1, v2);

     array[i] = v3;
     }
  }

 public static void mutableTest(int count)
  {
  Random r = new Random();
  Vector3d v1, v2, v3;
  Vector3d [] array = new Vector3d[count];

  for(int i = 0; i < count; i++)
     {
     v1 = new Vector3d(r.nextDouble(), r.nextDouble(), r.nextDouble());
     v2 = new Vector3d(r.nextDouble(), r.nextDouble(), r.nextDouble());

     v2.add(v1);

     array[i] = v2;
     }
  }
}
```

Slawa


----------



## ice-breaker (15. Mai 2011)

Also in "Java Concurrency in Practice" in "11.4.7 Just say no to object pooling" heisst es, dass 
	
	
	
	





```
new object
```
 in Java mitlerweile nur noch rund 10 Maschineninstruktionen benötigt und somit schneller als malloc in C ist.
Der Vorteil von immutable Objekten wird in "3.5.2 Immutable objects and initialization safety" beschrieben, durch die Garantie, dass der Zustand neu erzeugter immutable Objekte auch sofort anderen Objekten sichtbar ist - auch ohne Synchronisierung.
Dies bezieht sich auf "3.5 Safe Publication" mit Listing 3.14 [1] sowie auf "3.5.1 Improper publication: when good objects go bad" mit Listing 3.15 [1] welches zeigt, dass einfach ein Objekt zu erzeugen und dem Rest der Welt bekannt zu machen nicht ausreicht, dass dieses auf Grund des Java Memory Modells auch wirklich sichtbar ist.

Bei Immutable Objekten geht es also um Geschwindigkeit, aber nicht durch die Objekterzeugung gewonne, sondern durch fehlende Synchronisierung, die man sich sparen kann, wenn man das Objekt an ein anderes publiziert z.B. an einen Konstruktor einer Klasse übergibt.

[1] Java Concurrency in Practice


----------



## Ergometh (15. Mai 2011)

Bzgl. slawaweis:

Ohne den Performancetest nachvollzogen zu haben: Es ist mir grundsätzlich unverständlich warum der Garbage Collector zwischen Mutable und Immutable unterscheiden sollte. Zumal dies eine Eigenschaft ist, die der Compiler bzw. Interpreter erst einmal erkennen muss.

Es sollte immer gleich schnell sein.

Bzgl. ice-breaker:

Danke für die genauen Quellenhinweise.

Ohne in eine Analyse der Algorithmen einzusteigen ist das mit der Anzahl von Maschineninstruktionen natürlich schwer nachvollziehbar. Grundsätzlich sehe ich auch keinen Grund warum Java hier langsamer sein sollte als C, da die Allokation ja intern in der JVM abläuft.

Leider kommt mein eigener simpler Performancetest mit JDK 1.6.0_25 bei Massenwiedernutzung mit Pool statt Erzeugen mit new zu einem anderen Ergebnis:
ca. 0,52 Sekunden vs. ca 2,7 Sekunden mittels new. Natürlich kein synchronisierter Pool.
Hier wurden 20 mal 1.000.000 Vektoren aus zwei int-Werten erzeugt/freigegeben.

Einen Reuse-Pool würde aber auch ich nur in dem seltenen Fall einsetzen das ich gezwungen bin sehr große Mengen Objekte immer gleicher Typen zu erzeugen und immer wieder frei zu geben. In der Regel ist allein die Gefahr noch verwendete Objekte fälschlich freizugeben ein guter Grund auf einen Pool zu verzichten. Zuerst sollte man immer prüfen, ob das Erzeugen/Wegwerfen großer Zahlen von Objekten nicht vermeidbar ist.

Für Concurrency sind Immutables als Ersatz für Call-By-Value bzw. ein (atomares) Value-Assign zweifellos  nützlich.


----------



## Marco13 (15. Mai 2011)

Als kleinen Einschub mal den Hinweis von Brian Goetz: Write Dumb Code.
Das gilt aber sicher nicht für alle Fälle (für 90%, vielleicht auch 95% oder 99%, aber manche beschätigen sich eben (besonders) gerne mit dem Rest - wie klein er auch sein mag  ). 

@slawaweis: Hab's jetzt nicht getestet, postuliere aber mal, dass dieser Vergleich eine Aussagekraft von nahe 0 hat: Was macht "random.nextDouble"? (Ich meine: Wie aufwändig ist das?)
(Wenn du es nicht machst, mach ich es  aber: ) Vielleicht sollte man das bei Gelegenheit mal umbauen, so dass die Erzeugung der Eingabedaten nicht mitgemessen wird (und auch die Problemgröße geändert wird, und beide Verfahren mehrfach abwechslend ausgeführt werden)


----------



## slawaweis (15. Mai 2011)

am Anfang des Threads ging es um die Frage, was performanter im Bezug auf Vektoroperationen ist. Objektpooling spielt hier keine Rolle. Es geht eher um das:

*v3 = v1 + v2* vs. *v2 += v1*

also um die Frage, sollte man bei jeder Vektoroperation ein neues Vektorobjekt erzeugen oder sollte einem Vektorobjekt in der Formel der Wert direkt zugewiesen werden. Ich habe versucht diesen Umstand in einem Benchmark abzubilden. Selbstverständlich darf mein Code verändert und angepasst werden, um bessere Bedienungen/Ergebnisse zu bekommen. Ich würde mich auch über andere Benchmarks und deren Ergebnisse in Bezug auf Vektoroperationen freuen.

@Ergometh
den Garbage Collector habe ich bisher gar nicht erwähnt.

@Marco13
random.nextDouble() wird in gleicher Anzahl in beiden Varianten verwendet und stellt irgendeine Funktionalität in einem produktiven System dar. Ich habe es zuerst mit 0.0-len versucht, aber der Benchmark war zu schnell. In einem produktiven System werden ja auch unterschiedliche Werte verwendet, was ich mit random.nextDouble() abzubilden versuchte.

Am Anfang habe ich tatsächlich beide Varianten in einem Durchlauf abwechselnd ausgeführt. Doch in einem guten System würde man keinen Mischmasch von immutable und mutable für die selbe Sache verwenden, deshalb habe ich es getrennt. So sind auch bessere Aussagen über ein rein immutables und ein rein mutables System möglich.

Slawa


----------



## ice-breaker (15. Mai 2011)

Ergometh hat gesagt.:


> Leider kommt mein eigener simpler Performancetest mit JDK 1.6.0_25 bei Massenwiedernutzung mit Pool statt Erzeugen mit new zu einem anderen Ergebnis:
> ca. 0,52 Sekunden vs. ca 2,7 Sekunden mittels new. Natürlich kein synchronisierter Pool.
> Hier wurden 20 mal 1.000.000 Vektoren aus zwei int-Werten erzeugt/freigegeben.



neija es geht in meinen Quellen auch nur um die Erzeugung, du betrachtest damit ja die ganze Anwendung, wenn also dein JVM wenig Speicher zugewiesen bekommen hat, muss der Garbage Collector natürlich bei einem immutable-Ansatz viel öfter laufen als bei einem mutable-Ansatz, es sind dann zwar die GC-Durchläufe effizienter (Young-Generation durchsuchen) aber auch viel öfter.

Also Fazit wie immer: Aus Mikrobenchmarks kann man keinen Schluss ziehen.


----------



## Empire Phoenix (15. Mai 2011)

Im übrigen was evtl eine sauber Lösung für den OP ersteller ermöglicht:
erstelle für alle Vektorfunktionen eine version bei der man das Ergebnisobject mitgeben kann und dort dann die werte eingetragen werden. Damit ist es einerseits noch möglich gut nachzuvollziehen wie die objecte wie manipuliert werden, und andererseits kann man sehr oft eine menge Allokationen sparen, wenn man sub weiß das man gerade die Position eines objectes ändern will kann man dann direct den Vector manipulieren.


----------



## Spacerat (15. Mai 2011)

@Ergometh: ...und wieder was gelernt. *Object Reuse* ist wohl der Fachbegriff, der mir fehlte.  Ich nannte es damals Object-Recycling. Zumindest weis ich jetzt ein wenig mehr wovon ich rede.


Kr0e hat gesagt.:


> Das meinte ich eigentlich auch so... Hab mich mit Caching ein bisschen doof ausgedrückt .. =(


Warum sagst' des net gleich? "ein bisschen doof ausgedrückt" ist gut. "Verdrückt ausgekehrt" triffts besser. looool
Da wir jetzt genau wissen was du meinst bzw. was wir meinen, stellt sich mir die Frage, ob eine JVM neben Caching auch Object-Reuse von sich aus nutzt und ob sie selbstständig unterscheiden kann, wann sie was einsetzt. Wenn sie es kann, sind die Entwickler der JVM ausgesprochen genial. Aber ich gehe nach wie vor davon aus, dass sie es nicht kann, andernfalls müsste man sich ja keine Gedanken mehr darum machen ob man z.B. *StringBuffer* oder *StringBuilder* verwendet, bevor man irgendwelche Strings zusammen setzt.
Zumindest kann man schon mal davon ausgehen, dass bei massiver Objektflut Object-Reuse einem ständigem "new" vorzuziehen ist. Leider funktioniert Object-Reuse nur bei *mutables*. Deswegen mal 'ne klare Antwort für den Themenstarter. Ja, scheinbar ist es besser *mutables* zu verwenden.


----------



## Marco13 (15. Mai 2011)

slawaweis hat gesagt.:


> Ich habe es zuerst mit 0.0-len versucht, aber der Benchmark war zu schnell.



Darum ging es: Wenn man etwas mißt, was ca. 5 Sekunden dauert, geht ein Unterschied von 10 ms vs. 20 ms. vielleicht in der Messungenauigkeit unter - obwohl DAS so gesehen ja "doppelt so lange" brauchen würde...


----------



## Kr0e (15. Mai 2011)

Vlt. mal ganz lustig:

Beim Durchstöbern von JComponent fand ich das:


```
private static void recycleRectangle(Rectangle rect) {
        synchronized(tempRectangles) {
            tempRectangles.add(rect);
        }
    }
```

Ziemlich interessant wenn man unser thema damit vergleicht... tempRectangles ist eine List von 11 elementen... diese Datei stammt noch aus älteren JVM Zeiten... Bei neueren Klassen habe ich sowas bisher nicht gesehen... Ich vermute, dass die JVM heutzutage sehr viel optimiert was solche Codezeilen überflüssig macht.. gut so!


----------



## Marco13 (16. Mai 2011)

Aus gegebenem Anlass nochmal ein Versuch - ja, Microbenchmarks, ich weiß, und Biased ist der auch ... aber ganz konkret: Wenn man einen Array mit vielen Punkten hat, und die mit einer Matrix transformieren will, und vor der Wahl steht, die Eingabepunkte direkt zu transformieren oder transformierte Kopien zu erstellen, wird der Unterschied schon recht deutlich:

```
package _main;

import java.util.Random;

import javax.vecmath.Matrix4f;
import javax.vecmath.Point3f;
import javax.vecmath.Vector3d;

public class VecmathPerformance
{
    public static void main(String[] args)
    {
        Matrix4f matrix = new Matrix4f();
        matrix.rotX(45);
        
        for (int n=100000; n<=10000000; n+=100000)
        {
            Point3f v0[] = createInput(n);
            Point3f v1[] = createInput(n);
            
            long before = 0;
            long after = 0;
            
            before = System.nanoTime();
            immutableTest(matrix, v0, v1);
            after = System.nanoTime();
            System.out.println("immutable time for "+n+" elements was "+(after-before)/1e9+" result "+v0[0]);
            
            before = System.nanoTime();
            mutableTest(matrix, v0);
            after = System.nanoTime();
            System.out.println("  mutable time for "+n+" elements was "+(after-before)/1e9+" result "+v0[0]);
        }
    }
    
    private static Point3f[] createInput(int count)
    {
        Random r = new Random(0);
        Point3f array[] = new Point3f[count];
        for(int i = 0; i < count; i++)
        {
            array[i] = new Point3f(r.nextFloat(), r.nextFloat(), r.nextFloat());;
        }
        return array;
    }
    
    public static void immutableTest(Matrix4f matrix, Point3f v0[], Point3f v1[])
    {
        for(int i = 0; i < v0.length; i++)
        {
            Point3f v = new Point3f();
            matrix.transform(v0[i], v);
            v1[i] = v;
        }
    }
    public static void mutableTest(Matrix4f matrix, Point3f v0[])
    {
        for(int i = 0; i < v0.length; i++)
        {
            matrix.transform(v0[i]);
        }
    }
}
```

Um das nochmal zu betonen: In den ALLERmeisten Anwendungsfällen sind Objektallokationen das kleinste Übel (solche Sachen wie ein unbedarft aufgerufenes [c]logger.log(DEBUG, complexThing.toString());[/c] oder irgendwelches XML-rumgeserialisiere sind da viel kritischer), aber _speziell_ bei Grafik (und dafür wird die Vecmath eben meistens verwendet) macht es offenbar doch (noch) einen deutlichen Unterschied. Und der Anwendungsfall (einen Array von Punkten mit einer Matrix transformieren) ist IMHO schon omnipräsent...


----------

