Variablen Variablen voneinander abhängig

Hi,
ich meine eigentlich verstanden zu haben, wann man einer Methode Parameter übergeben sollte, wie man mit denen handhabt und was man in den Konstruktor schreiben sollte, usw.
Aber jetzt hänge ich schon eine ganze Zeit lang an einem Problem:

Es gibt die Klassen Feld, Gitter, sowie ein Interface, das die Methoden von Gitter beinhaltet.
Ein Gitter besteht aus n*n Feldern.
Code:
public class Feld {
    private int feldNummer;
    private int x;
    private int y;
   
    public Feld(int feldNummer) {
        this.feldNummer = feldNummer;
    
    public int xKoordinate(Gitter g) {
        x = feldNummer % g.getSeitenLaenge() + 1;
        return x;
    }
   
    public int yKoordinate(Gitter g) {
        y = feldNummer / g.getSeitenLaenge() + 1;
        return y;
    }
}
Code:
public class Gitter implements Traceable {
    private ArrayList<Feld> spielFeld = new ArrayList<>();
    private int seitenLaenge;
   
    public Gitter(int seitenLaenge) {
        this.seitenLaenge = seitenLaenge;
        for (int i = 0; i < (Math.pow(this.seitenLaenge, 2); i++) {
            spielFeld.add(new Feld(i));
        }
    } 
}
(get-Methoden existieren natürlich auch)
Ich möchte noch in weiteren Methoden von Gitter die x- und y-Koordinaten verwenden.
Das Problem ist: Die Methoden für die Koordinaten übergeben den Wert nicht an die globalen Variablen x bzw. y. Ich vermute, dass es funktionieren würde, wenn ich dem Feld-Konstruktor die Parameter x und y hinzufüge und initialisiere. Aber ich will nicht jedes mal wenn ich eine Instanz von Feld erstelle, alle 3 Variablen angeben müssen. Das ist ja Quatsch, weil x und y von der feldnummer abhängig sind.
Meine andere Idee sah so aus:
Code:
public Feld(int feldNummer) {
        this.feldNummer = feldNummer;
        this.x = feldNummer % Gitter.getSeitenLaenge + 1;
        this.y = feldNummer / Gitter.getSeitenLaenge + 1;
}
Ohne die Methoden, dafür muss die Variable seitenLaenge dann static sein, was später zu neuen Problemen führt.

Das ich ein Interface implementieren soll, hab ich mir übrigens nicht selbst ausgedacht und bisher seh ich darin auch noch keinen Nutzen. Könnte dieses Interface irgendwie mein Problem lösen? Oder habt ihr andere Vorschläge?
 

mrBrown

Super-Moderator
Mitarbeiter
Ich vermute, dass es funktionieren würde, wenn ich dem Feld-Konstruktor die Parameter x und y hinzufüge und initialisiere. Aber ich will nicht jedes mal wenn ich eine Instanz von Feld erstelle, alle 3 Variablen angeben müssen. Das ist ja Quatsch, weil x und y von der feldnummer abhängig sind.
Ja, würde es und wäre auch der richtige Weg ;)

Gitter erstellt die Felder und Gitter bestimmt die Feldnummer sowie die eigene Größe - es kann also x und y berechnen und übergeben.

Das ich ein Interface implementieren soll, hab ich mir übrigens nicht selbst ausgedacht und bisher seh ich darin auch noch keinen Nutzen. Könnte dieses Interface irgendwie mein Problem lösen? Oder habt ihr andere Vorschläge?
Ohne zu wissen, wofür das Interface da ist, und welche Methoden es enthält, kann man da nichts zu sagen ;)
 
Danke!:)
Ich hab es anders gelöst, indem ich die Methoden xKoordinate und yKoordinate in die Klasse Gitter verschoben habe, und dabei ein Feld als Parameter übergebe.
Jetzt sind zwar x, y keine Klassenvariablen mehr, aber das stört mich nicht.
 

mrBrown

Super-Moderator
Mitarbeiter
Danke!:)
Ich hab es anders gelöst, indem ich die Methoden xKoordinate und yKoordinate in die Klasse Gitter verschoben habe, und dabei ein Feld als Parameter übergebe.
Jetzt sind zwar x, y keine Klassenvariablen mehr, aber das stört mich nicht.
Das klingt nach dem absolut falsche Weg, jetzt hat ein Feld ja gar keine Koordinaten mehr...
 
Meinst du? Aber ich will doch hauptsächlich in der Klasse Gitter damit arbeiten.
Wenn ich die Methoden in der Klasse Feld anlege, müsste ich doch jedes mal schreiben (in der Klasse Gitter):
Java:
irgendeinFeld.xKoordinate(this);
Aber das sieht mir auch nicht nach der Ideallösung aus.
 

mrBrown

Super-Moderator
Mitarbeiter
Die Lösung mit Konstruktoren wäre die sinnvolle, hattest du ja selbst schon erwähnt.

Fels sollte die Variablen enthalten, aber nicht die Klasse Gitter kennen
 
mrBrown hat gesagt.:
Nein!
Du kannst dem Feld einfach die Position übergeben.
Das versteh ich jetzt nicht ganz, sorry.
Die Felder sind alle in einer ArrayList<Feld>, weshalb ich diese Formel anwenden muss, was dazu führt, dass ich auf die Variable seitenLaenge von Gitter zugreifen muss.
Das kann ich aber nicht, weil eine Instanz von Gitter erst in der Main erstellt wird.
Java:
private int x;
private int y;
public Feld(int feldNummer) {
        this.feldNummer = feldNummer;
        this.x = feldNummer % seitenLaenge + 1; // wie kann ich so auf seitenLaenge zugreifen?
        this.y = feldNummer / seitenLaenge + 1;
}
Oder meinst du das ganz anders?
 

Javinner

Top Contributor
Punkte sollten (genauso wie zB Zahlen und Zeichenketten) sinnvollerweise unveränderlich sein - es sind eben „Werte“ ;)
Meine persönliche Meinung: du diskutierst oft ums Recht ;) Ich kann nicht sagen, dass du keine Ahnung hast, eher im Gegenteil, jedoch entsteht nach einer Weile eben der Eindruck bei mir. Schon paar Mal beobachtet :cool:
 

mrBrown

Super-Moderator
Mitarbeiter
Meine persönliche Meinung: du diskutierst oft ums Recht ;) Ich kann nicht sagen, dass du keine Ahnung hast, eher im Gegenteil, jedoch entsteht nach einer Weile eben der Eindruck bei mir. Schon paar Mal beobachtet :cool:
Na diskutieren, wenn man denkt im Unrecht zu sein ist meinstens auch ziemlich witzlos, oder? ;)

Du kannst gerne deine Argumente darlegen, warum ein mutable Point besser ist - ich bin in Diskussionen durchaus ziemlich leicht zu überzeugen ;)


Das versteh ich jetzt nicht ganz, sorry.
Die Felder sind alle in einer ArrayList<Feld>, weshalb ich diese Formel anwenden muss, was dazu führt, dass ich auf die Variable seitenLaenge von Gitter zugreifen muss.
Das kann ich aber nicht, weil eine Instanz von Gitter erst in der Main erstellt wird.
Stell dir die Signatur des Konstruktors so vor:
public Feld(int feldNummer, int x, int y)

Die Feld-klasse sollte weder wissen, dass es ein Gitter, noch das es eine Seitenlänge gibt.
 

Javinner

Top Contributor
@mrBrown
Wo du doch gerade dran bist, deine Aussage zu untermauern, warum nun Punkte feste Werte haben sollen ;)

Ich für mein Teil bin der Meinung, dass es nicht verkehrt ist, wenn ein Punkt die Position im Raum wechseln kann.
Zum Beispiel habe ich wo gelesen, dass man in Spielen den Gegenständen eine Position im Raum geben muss, damit diese genau dort platziert werden können, natürlich muss in einem dreidimensionalen Raum eine weitere Variable dazu kommen.

Wie siehst du das?
 

truesoul

Top Contributor
Hallo.

Die von @mrBrown vorgeschlagen bzw von dir selbst vorgeschlagene Variante wäre in diesem Fall die sinnvollste.
Es macht sicher mehr Sinn auch selber sowas mal zu implementieren als auf fertige Klassen zu zugreifen (Point).
Allerdings ist der Hinweis auf Point auch sicher nicht falsch.

Grüße
 

mrBrown

Super-Moderator
Mitarbeiter
Wo du doch gerade dran bist, deine Aussage zu untermauern, warum nun Punkte feste Werte haben sollen ;)

Ich für mein Teil bin der Meinung, dass es nicht verkehrt ist, wenn ein Punkt die Position im Raum wechseln kann.
Zum Beispiel habe ich wo gelesen, dass man in Spielen den Gegenständen eine Position im Raum geben muss, damit diese genau dort platziert werden können, natürlich muss in einem dreidimensionalen Raum eine weitere Variable dazu kommen.
Ein "Punkt" im Allgemeinen Sinne ist eben ein fester Wert, der sich nicht ändern kann.
Der Punkt (1,1) ist eben immer dieser Punkt und ist immer der selbe wie (1,1).
Wenn man zu dem Punkt (1,1) jetzt den Vektor (1,1) dazu addiert, hat man ja nicht den Punkt (1,1) verändert, sondern das Ergebnis ist der neue Punkt (2,2).

Wenn man seine Position im Raum ändert, ist man einfach an einem neuem Punkt. Man hat aber nicht den Punkt, an dem man vorher war, verändert ;)

Das ganze kannst du analog zu Zahlen oder Strings sehen - da ist dieses Prinzip for jeden selbstverständlich ;)
 
Danke erst mal euch allen! So wie es jetzt ist, bin ich zufrieden.;)
Es gibt zwar schon wieder ein neues Problem was nichts damit zu tun hat (ich kanns mir selbst noch nicht erklären), aber das versuche ich erst mal selbst zu beheben.
 

Javinner

Top Contributor
@mrBrown
Letztendlich ist ein Spieler ein bewegtes Objekt, welches ebenso ein Punkt hat, der seine derzeitige Position beschreibt, und ich habe schon einige Programme gesehen, wo der Spieler, wenn er die Position wechselt, mittels
einer Methode den Punkt verändert, nicht neu setzt, sondern den existierenden verändert!

Wenn man einer primitiven Variable, nehmen wir int, eine Zahl zuweist, dann kann diese im späteren Verlauf überschrieben werden, sprich, die Variable bekommt keine neue Variable, sondern nur ein anderen Wert zugewiesen, oder irre ich mich hier? Dass String es anders handhabt, habe ich gelesen, warum das so ist, jedoch nur bedingt verstanden.

Ich sehe den Vorteil, warum man Klasse Punkt unveränderlich machen sollte, nicht.

@programmer147
Es tut mir echt leid, dass wir dein Thread zu Diskussionszwecken missbraucht haben!
 
Was ist denn jetzt deine Lösung?
Deine oder die von @mrBrown ?
Die von @mrBrown .
Also sieht das jetzt so aus:
Java:
public class Feld {
    private int feldNummer;
    private int x;
    private int y;
   
    public Feld(int feldNummer, int x, int y) {
        this.feldNummer = feldNummer;
        this.x = x;
        this.y = y;
    }
}
// + get-Methoden

public class Gitter {
    private ArrayList<Feld> spielFeld = new ArrayList<>();
    private int seitenLaenge;
   
    public Gitter(int seitenLaenge) {
        this.seitenLaenge = seitenLaenge;
        for (int i = 0; i < (Math.pow(seitenLaenge, 2)); i++) {
            spielFeld.add(new Feld(i, (i % seitenLaenge + 1), (i / seitenLaenge + 1)));
        }
    } 
}
 

truesoul

Top Contributor
Hallo.

Die Diskussion führt zu nichts weil etliche Entwickler unterschiedliche Meinung vertreten.
In Android ist die Implementierung auch nicht Unveränderlichkeit aber man findet auch in Beiträgen wo Point als
Unveränderlichkeit gesehen werden. Und etlichen Objektorientierten Sprache sind Point-Implementierungen auch Veränderlich.


Grüße
 

truesoul

Top Contributor
Die von @mrBrown .
Also sieht das jetzt so aus:
Java:
public class Feld {
    private int feldNummer;
    private int x;
    private int y;
  
    public Feld(int feldNummer, int x, int y) {
        this.feldNummer = feldNummer;
        this.x = x;
        this.y = y;
    }
}
// + get-Methoden

public class Gitter {
    private ArrayList<Feld> spielFeld = new ArrayList<>();
    private int seitenLaenge;
  
    public Gitter(int seitenLaenge) {
        this.seitenLaenge = seitenLaenge;
        for (int i = 0; i < (Math.pow(seitenLaenge, 2)); i++) {
            spielFeld.add(new Feld(i, (i % seitenLaenge + 1), (i / seitenLaenge + 1)));
        }
    }
}

Gute Wahl und gute Nacht
 

mrBrown

Super-Moderator
Mitarbeiter
Letztendlich ist ein Spieler ein bewegtes Objekt, welches ebenso ein Punkt hat, der seine derzeitige Position beschreibt, und ich habe schon einige Programme gesehen, wo der Spieler, wenn er die Position wechselt, mittels
einer Methode den Punkt verändert, nicht neu setzt, sondern den existierenden verändert!

Natürlich gibt es einige, die das so machen - man sieht halt auch viele schlechte Dinge :p

Wenn man einer primitiven Variable, nehmen wir int, eine Zahl zuweist, dann kann diese im späteren Verlauf überschrieben werden, sprich, die Variable bekommt keine neue Variable, sondern nur ein anderen Wert zugewiesen, oder irre ich mich hier? Dass String es anders handhabt, habe ich gelesen, warum das so ist, jedoch nur bedingt verstanden.

Richtig, die Variable bekommt einen neuen Wert. Genau das gleiche würde mit einem unveränderlichem Punkt passieren ;)
Bei einem mutable-Point bekommt die Variable keinen neuen Wert - aber das innere des Wertes verändert sich.
Der Identität (==) nach ist es noch derselbe, der Gleichheit (equals) nach ist es aber ein anderer.

Ich sehe den Vorteil, warum man Klasse Punkt unveränderlich machen sollte, nicht.

Anhand deines Beispiels oben: Der Spieler dürfte niemals einfach einen Punkt nach außen weiter geben oder von außen bekommen, sonst lässt sich der Spieler verändern, ohne das der es mitbekommt. Man müsste also die ganze Zeit mit Kopien des Objekts arbeiten.

Und zb: Man kann den Punkt nicht einfach als Key für eine Map nutzen - bei dieser müssen die Keys nämlich unveränderlich sein.

Ein weiterer Vorteil ist z.B. Thread-Sicherheit - da die Werte nicht verändert werden können, gibts auch keine Probleme, wenn mehrere Threads damit hantieren.

Zusätzlich können die durchaus einen Performance-Vorteil bieten (grad auch in Zukunft in Hinblick auf Project Valhalla)

Einfaches (sehr Pseudocodiges) Beispiel um beide Varianten einmal grob gegenüber zu stellen:

Code:
one = (1,1)
two = one.copy()
two + (1,1)

Code:
one = (1,1)
two = one + (1,1)

Welches sieht natürlicher aus? ;)
 

mrBrown

Super-Moderator
Mitarbeiter
Die Diskussion führt zu nichts weil etliche Entwickler unterschiedliche Meinung vertreten.
unterschiedliche Meinungen sind die Grundvoraussetzung für Diskussionen, mit deiner Begründung wäre jede Diskussion überflüssig :p

In Android ist die Implementierung auch nicht Unveränderlichkeit aber man findet auch in Beiträgen wo Point als
Unveränderlichkeit gesehen werden. Und etlichen Objektorientierten Sprache sind Point-Implementierungen auch Veränderlich.
Letzteres liegt einfach daran, dass in den meisten OO-Sprachen Veränderlichkeit der Default ist.
 

Blender3D

Top Contributor
Ein "Punkt" im Allgemeinen Sinne ist eben ein fester Wert, der sich nicht ändern kann.
Der Punkt (1,1) ist eben immer dieser Punkt und ist immer der selbe wie (1,1).
Wenn man zu dem Punkt (1,1) jetzt den Vektor (1,1) dazu addiert, hat man ja nicht den Punkt (1,1) verändert, sondern das Ergebnis ist der neue Punkt (2,2).

Wenn man seine Position im Raum ändert, ist man einfach an einem neuem Punkt. Man hat aber nicht den Punkt, an dem man vorher war, verändert ;)
Kommt auf den Anwendungsfall an:
Stimmt für Operationen wie: D = A+B+C
A , B, C dürfen sich hier aus Anwendersicht nicht verändern.

Für ein Spiel mit 1000 Asteroiden mit ihren Positionen Vektor(x,y). Hier ist es günstiger wenn der Vektor nicht konstant ist. Sonst müsste jedes mal bei einer Positionsänderung ein neues Vektorobjekt erzeugt werden. Besser ein veränderbarer Vektor der nur die Koordinaten ändert.
 

truesoul

Top Contributor
unterschiedliche Meinungen sind die Grundvoraussetzung für Diskussionen, mit deiner Begründung wäre jede Diskussion überflüssig

Wenn Diskussion von Veränderlichen/Unveränderlichen Objekten so mal rein garnichts mit dem Problem des Threads zu tun hat. Zumindest konnte ich kein Post von dir finden, dass Feld am besten Unveränderlich sein sollte. Dann ist eine Diskussion überflüssig weil es sonst führt es genau zu dass was es ist, ein Thema für die plauderecke.

Und wenn jeder permanent sich rechtfertigen muss, warum er ein vorgeschlag macht und du dann findest das die nicht geeignet ist, weil sie nur weil sie Veränderlich ist, nervt es nur noch. Wie gesagt, war nicht das Thema.

Letzteres liegt einfach daran, dass in den meisten OO-Sprachen Veränderlichkeit der Default ist.

Ach was? :rolleyes:

Thema ist für mich gegessen da der Benutzer ja das eigentliche Problem gelöst hat.

Grüße
 

mrBrown

Super-Moderator
Mitarbeiter
Für ein Spiel mit 1000 Asteroiden mit ihren Positionen Vektor(x,y). Hier ist es günstiger wenn der Vektor nicht konstant ist. Sonst müsste jedes mal bei einer Positionsänderung ein neues Vektorobjekt erzeugt werden. Besser ein veränderbarer Vektor der nur die Koordinaten ändert.
Und warum ist das so viel günstiger? ;)

Um‘s neue Objekte erzeugen kommt man btw nicht rum, man muss ständig für alles Defensive Kopien erzeugen
 

Blender3D

Top Contributor
Und warum ist das so viel günstiger? ;)
Code:
Vector add Vector( Vector v ){
    return new Vector( x+v.x, y+ v.y );
}
versus
Vector add Vector( Vector v ){
    x+=v.x;
    y+=v.y;
    return  this;
}
Wie man anhand von obigen Code leicht erkennen kann erzeugt die konstante Variante immer ein neues Objekt vom Typ Vektor.
Die nicht konstante Variante addiert immer nur die Koordinaten der beiden Vektoren und gibt das bereits vorhandene Objekt zurück.
Bei vielen Objekten --> ein höherer Aufwand. Speicher anfordern und freigeben. Bei einem Spiel das schnell sein will ist also die nicht konstante Variante besser.
;)
 

mrBrown

Super-Moderator
Mitarbeiter
Ja, ein gewisser Unterschied ist da, ich bezweifle aber, das der unter realen Bedingung auch nur Ansatzweise relevant ist.
Das ist ziemlich genau das, wovor Don Knuth so warnt :p

Bei vielen Objekten --> ein höherer Aufwand. Speicher anfordern und freigeben.
Speicher freigeben ist ein NOP, Speicher *nicht* freigeben kostet ;)
Das new liegt irgendwo im unteren Nanosekunden-Bereich, da kann schon der Zugriff auf eine Instanz-Variable länger dauern.
Kleine, kurzlebige Objekte sind das, wofür die GCs optimiert sind.

Man spart sich das Erstellen neuer Objekte ja auch nicht - jeder Getter und Setter zB müsste defensive Kopien des Objekts anfertigen, wenn man vernünftige Kapselung möchte.

und wie gesagt: grad in Hinblick auf kommende Java-Versionen bieten "Value Types" deutlich mehr Optimierungspotential.


Ein Benchmark dafür wäre mal interessant...
 

Blender3D

Top Contributor
Kleine, kurzlebige Objekte sind das, wofür die GCs optimiert sind
Ich habe mit Assembler, C und C++ programmieren gelernt. Früher ging es immer um Geschwindigkeit und Speicherplatz. Java ist eine Programmiersprache die ich wegen Ihrer Vorteile sehr schätze. Die Vorteile z.B. GC und keine direkten Pointer sind aber auch gleichzeitig ein Nachteil. Wer über Java den Zugang zum Programmieren bekommt, macht sich, dank des GC keine Gedanken darüber was im Hintergrund passiert. ( Ist meistens auch nicht nötig Java ist sehr schnell . )


Aber zurück zu dem Beispiel.
Ein Asteroid der durch die Game Engine ständig bewegt wird (80 Frames/s).
Was ist besser ?
1)
80 mal pro Sekunde einen Vektor erzeugen. Jetzt habe ich 1000 Asteroiden im Spiel --> 80.000 mal pro Sekunde einen Vektor erzeugen, der wieder freigegeben werden muss.
2) auf 80 Vektoren die Koordinaten ändern.
Warum sollte ich unnötigerweise den GC beschäftigen, wenn eine nicht konstante Lösung effizienter ist.
...:rolleyes:
Konstante Klassen haben in einem anderen Kontext genau so ihre Berechtigung.
Es gibt nie eine perfekte Lösung. Man erkauft sich immer durch einen Vorteil gleichzeitig einen Nachteil.
War eine der ersten Sprüche die ich bei meinem Studium gelernt habe.

Nebenbei Java hat auch nicht unbegrenzt Speicher.
The maximum theoretical heap limit for the 32-bit JVM is 4G. Due to various additional constraints such as available swap, kernel address space usage, memory fragmentation, and VM overhead, in practice the limit can be much lower. On most modern 32-bit Windows systems the maximum heap size will range from 1.4G to 1.6G. On 32-bit Solaris kernels the address space is limited to 2G. On 64-bit operating systems running the 32-bit VM, the max heap size can be higher, approaching 4G on many Solaris systems.
 

mrBrown

Super-Moderator
Mitarbeiter
Ich habe mit Assembler, C und C++ programmieren gelernt. Früher ging es immer um Geschwindigkeit und Speicherplatz. Java ist eine Programmiersprache die ich wegen Ihrer Vorteile sehr schätze. Die Vorteile z.B. GC und keine direkten Pointer sind aber auch gleichzeitig ein Nachteil. Wer über Java den Zugang zum Programmieren bekommt. Mach sich, dank des GC keine Gedanken darüber was im Hintergrund passiert. ( Ist meistens auch nicht nötig Java ist sehr schnell . )
Geschwindigkeit und Speicherplatz sind auch nicht egal - aber warum sollte ich per Hand optimieren (zZ unter Verzicht auf gutes Design), wenn der Compiler das um Längen besser kann als ich?


Ein Asteroid der durch die Game Engine ständig bewegt wird (80 Frames/s).
Was ist besser ?
1) 80 mal pro Sekunde einen Vektor erzeugen. Jetzt habe ich 1000 Asteroiden im Spiel --> 80.000 mal pro Sekunde einen Vektor erzeugen, der wieder freigegeben werden muss.
2) auf 80 Vektoren die Koordinaten ändern.

Um dein Beispiel noch zu erweitern: Da der Asteroiden-Entwickler defensiv programmiert, gibt er nach außen nur Kopien des Asteroiden zurück (sonst macht noch jemand Unsinn mit dem Vektor, zb beim Zeichnen das Umrechnen in Bildschirmkoordinaten). Es wird also jeden Frame mindestens eine Kopie des Vektors erstellt, macht also trotzdem 80.000 neue Vektoren pro Sekunde ;)

Spaßeshalber einfach mal ein (wenn auch sehr konstruierter) Benchmark dazu.

Code:
MyBenchmark.immutable                       avgt   10   4,033 ±  0,103   ns/op
MyBenchmark.immutable:·gc.alloc.rate        avgt   10  ≈ 10⁻⁴           MB/sec
MyBenchmark.immutable:·gc.alloc.rate.norm   avgt   10  ≈ 10⁻⁶             B/op
MyBenchmark.immutable:·gc.count             avgt   10     ≈ 0           counts
MyBenchmark.mutable                         avgt   10   4,174 ±  0,388   ns/op
MyBenchmark.mutable:·gc.alloc.rate          avgt   10  ≈ 10⁻⁴           MB/sec
MyBenchmark.mutable:·gc.alloc.rate.norm     avgt   10  ≈ 10⁻⁶             B/op
MyBenchmark.mutable:·gc.count               avgt   10     ≈ 0           counts

Sowohl Zeit als auch Speicherverbrauch sind nahezu identisch, die Laufzeitunterschiede liegen nur an Hintergrundlast.

Project Valhalla macht da noch keinen Unterschied, der dürfe aber dann zum Tragen kommen, wenn die Vektoren Instanzattribute sind - Value Types können dann direkt inlined werden, was einen deutlichen Vorteil für die immutable Klassen bedeuten dürfte (weil es dann keine "richtigen" Objekte mehr sind).

Java:
Vector p = new  Vector(0, 0);
Vector one = new Vector(1, 1);

p = p.add(one);
p = p.add(one);
p = p.add(one);
p = p.add(one);
p = p.add(one);
p = p.add(one);
p = p.add(one);
p = p.add(one);
p = p.add(one);
p = p.add(one);
return p.length();

Java:
class Vector {
        final int x;
        final int y;

        public Vector(final int x, final int y) {
            this.x = x;
            this.y = y;
        }

        public Vector add(Vector that) {
            return new Vector(this.x + that.x, this.y + that.y);
        }

        public double length() {
            return Math.sqrt(x * x + y * y);
        }
    }

Die Mutable Klasse analog dazu, nur werden die Werte direkt verändert und this returned.



80.000 mal pro Sekunde einen Vektor erzeugen, der wieder freigegeben werden muss.
"Freigeben" gibt es in Java nicht - Speicher wird nicht freigegeben.
Tote Objekte werden einfach völlig ignoriert, der GC bekommt absolut nichts von ihnen mit, nur lebende Objekte spielen eine Rolle.

Warum sollte ich unnötigerweise den GC beschäftigen, wenn eine nicht konstante Lösung effizienter ist.
...:rolleyes:
De einzige "Mehr"-Beschäftigung kommt in diesem Fall durch einen früheren GC-Lauf, also erst, wenn man so viel Garbage erzeugt hat, dass der GC deshalb öfter anspringt, als er es sonst getan hätte.

Ein Vektor ist 24byte (12 byte Header + 2 ints, erweitert auf vielfaches von 8) groß. Man kann so einige Tausend Vektoren erzeugen, bevor sich der GC genötigt fühlt, da einzugreifen ;)


The maximum theoretical heap limit for the 32-bit JVM is 4G. Due to various additional constraints such as available swap, kernel address space usage, memory fragmentation, and VM overhead, in practice the limit can be much lower. On most modern 32-bit Windows systems the maximum heap size will range from 1.4G to 1.6G. On 32-bit Solaris kernels the address space is limited to 2G. On 64-bit operating systems running the 32-bit VM, the max heap size can be higher, approaching 4G on many Solaris systems.

32bit-VMs? Ist das ein Text aus dem letztem Jahrtausend? ;)
Normal dürften heute 64 bit und durchaus auch Ram größer als 4GB seine, Für Java 9 gibts ja auf der Offiziellen Seite von Oracle nicht mal eine 32bit-VM ;)
 

Blender3D

Top Contributor
"Freigeben" gibt es in Java nicht - Speicher wird nicht freigegeben
Der GC macht die Arbeit für Dich. Und auch wenn Du es nicht glaubst belegt Jedes Objekt Speicher der verwaltet wird.
Das Allozieren des Speichers und das Freigeben muss von Ihm bewerkstelligt werden. Er ist genauso wie jeder Programmierer in dieser Richtung gefordert und bewegt sich in den gegebenen Grenzen. Der Compiler kann außerdem nicht alles optimieren.
gibt er nach außen nur Kopien des Asteroiden zurück
asteroid.move() gibt nach außen gar nichts zurück, würde ja der Idee der Kapselung widersprechen.


Spaßeshalber einfach mal ein (wenn auch sehr konstruierter) Benchmark dazu.
konstruiert ist der richtige Ausdruck. Der GC lässt sich eben nicht kontrollieren er ist nicht deterministisch.

Aber um das Thema abzuschließen, Du hast Deine Sicht auf diese Dinge und das ist in Ordnung so.
Der Punkt den ich einbringen wollte für den, der bereit ist ihn anzunehmen.
"Es gibt nie die perfekte Lösung, es gibt nur die bestmögliche Lösung und diese hängt immer vom Kontext des Problems ab." Stammt von meinem Mathematik Professor ( der hat auch für IBM gearbeitet ).
Sich auf gewisse Eigenschaften zu fixieren, bedeutet auch sich Möglichkeiten zu versperren.
 

mrBrown

Super-Moderator
Mitarbeiter
Und auch wenn Du es nicht glaubst belegt Jedes Objekt Speicher der verwaltet wird.
Jedes Objekt auf dem Heap(!), ja.
Aber nicht jedes new hat ein Objekt auf dem Heap oder überhaupt ein Objekt zur Folge. Siehe den Benchmark oben, wo kein Speicher auf dem Heap alloziert wird.

Das Allozieren des Speichers und das Freigeben muss von Ihm bewerkstelligt werden. Er ist genauso wie jeder Programmierer in dieser Richtung gefordert und bewegt sich in den gegebenen Grenzen. Der Compiler kann außerdem nicht alles optimieren.
Das Freigeben ist ein markieren eines Blocks als "frei", es gibt kein "freigeben" auf Objekt-Ebene.
Ob in dem Block ein oder eine Millionen tote Objekte liegen ist vollkommen egal und hat keinen Einfluss auf die Freigabe, relevant sind nur die lebenden Objekte in diesem Block.

asteroid.move() gibt nach außen gar nichts zurück, würde ja der Idee der Kapselung widersprechen.
Nö, ich hab auch nie behaupte das move das machen sollte. Aber irgendwann muss man mit der Position des Asteroiden arbeiten, ich hab doch extra ein Beispiel dafür gebracht. Wenn man nichts mit dem Punkt machen würde, wäre der ziemlich witzlos und man könnte gleich auf ihn verzichten...

konstruiert ist der richtige Ausdruck. Der GC lässt sich eben nicht kontrollieren er ist nicht deterministisch.
Das nicht-deterministische des GCs spielt bei diesem Benchmark aber keine Rolle, es wird ja überhaupt kein Heap-Speicher benutzt.
Man hätte vieles daran kritisieren können, aber ausgerechnet den Punkt, der da überhaupt nicht mit spielt?
 

Blender3D

Top Contributor
Vorweg: Ich schätze Dich als einen sehr guten Ratgeber in diesem Forum und meine Absicht ist es nicht Dich zu kritisieren. Ich wollte nur meine Sicht auf die Dinge darlegen.
1) Objekte die erzeugt werden brauchen Speicher.
Wenn ich sie nicht erzeuge weil ich sie gar nicht brauche, ist das also besser.
2) Der GC ist kein Wunderding das Speicherbedarf einfach ignoriert.
https://www.codecentric.de/publikation/der-garbage-collector-das-unbekannte-wesen/
Generation Sizing – auf die Größe kommt es an
Die Größenverhältnisse zwischen Young und Tenured Generation sind also entscheidend für die Performance. Erzeugt eine Anwendung sehr viel kurzlebigen Müll und ist die Young Generation zu klein, so wird es sehr schnell passieren, dass kurzlebige Objekte in der Tenured Generation landen. Dort kommt es dann zu gehäuften Major Collections, die auf die Performance drücken können.

3) Kapselung
a.move() bewegt Asteroid
a.draw() zeichnet diesen
usw.
Ich brauche den internen Vektor nicht zu kennen.
a.getPos() liefert zwar die Position als Kopie, aber die braucht man nicht oft also kein großes Problem.
4) 32 Bit Java läuft noch auf sehr vielen Maschinen. --> der Code sollte auch auf diesen gut funktionieren.
5) Benchmark
Da der GC nicht deterministisch ist, weiß man nicht wann der Aufwand für den GC größer wird. Also ist ein Benchmark der kurz einige Schleifen durchläuft nicht aussagekräftig.
Ein brauchbarer Benchmark benötigt realistische Rahmenbedingungen. z.B. Ein Spiel in dem Objekte erzeugt werden und Grafiken die ausgegeben werden, um den GC auch an seine Grenzen zu manövrieren. Außerdem eine entsprechende Laufzeit, weil der GC nicht deterministisch ist.
Ich hatte es eben auch mit diesem Problem mit meinem Pacman Clone zu tun. Mit konstanten Vektoren geschah genau das, was oben beschrieben wird. Nach einigen Minuten kam es zu einem Ruckeln, dass manchmal länger und manchmal nur sehr kurz war und manchmal auch lange nicht auftrat. Das Ruckeln konnte ich letztendlich durch nicht konstante Vektoren beheben. Das ist auch der Grund warum ich diese Erfahrung an Euch weitergeben wollte.
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
1) Objekte die erzeugt werden brauchen Speicher.
Wenn ich sie nicht erzeuge weil ich sie gar nicht brauche, ist das also besser.
Viele Objekte werden aber eben auch bei einem new nicht erzeugt, da die JVM das wegoptimieren kann ;)

2) Der GC ist kein Wunderding das Speicherbedarf einfach ignoriert.
https://www.codecentric.de
Generation Sizing – auf die Größe kommt es an
Die Größenverhältnisse zwischen Young und Tenured Generation sind also entscheidend für die Performance. Erzeugt eine Anwendung sehr viel kurzlebigen Müll und ist die Young Generation zu klein, so wird es sehr schnell passieren, dass kurzlebige Objekte in der Tenured Generation landen. Dort kommt es dann zu gehäuften Major Collections, die auf die Performance drücken können.
Für den G1 gilt das, afaik, nicht mehr - tendenziell sind da beide Variabel groß. Maximum für Young liegt bei 60%(?) des Heaps - die innerhalb eines Zyklus mit Müll zu füllen, der auch einen Zyklus überleben, dürfte recht schwer werden.


3) Kapselung
a.move() bewegt Asteroid
a.draw() zeichnet diesen
usw.
Ich brauche den internen Vektor nicht zu kennen.
a.getPos() liefert zwar die Position als Kopie, aber die braucht man nicht oft also kein großes Problem.
Dann eben Kollisionsdetektion als Beispiel - da müssen zwei Dinge gegenseitig die Position kennen ;)


4) Benchmark
Da der GC nicht deterministisch ist, weiß man nicht wann der Aufwand für den GC größer wird. Also ist ein Benchmark der kurz einige Schleifen durchläuft nicht aussagekräftig.

In dem Benchmark wird der allozierte Speicher gemessen, und das sind in beiden Fällen 0 Byte-Heapspeicher.
Da ist es völlig deterministische, wann der Aufwand zu groß wird - nämlich nie ;)
"kurz einige Schleifen" sind in dem Fall btw ein paar Millionen Mal.

Ein brauchbarere Benchmark braucht realistische Rahmenbedingungen. z.B. Ein Spiel in dem Objekte erzeugt werden und Grafiken die ausgegeben werden, um den GC auch an seine Grenzen zu manövrieren. Außerdem eine entsprechende Laufzeit, weil der GC nicht deterministisch ist.
Ich hatte es eben auch mit diesem Problem mit meine Pacman Clone zu tun. Mit konstanten Vektoren geschah genau das, was oben beschrieben wird. Nach einigen Minuten kam es zu einem Ruckeln, dass manchmal länger und manchmal nur sehr kurz war und manchmal auch lange nicht auftrat. Das Ruckeln konnte ich letztendlich durch nicht konstante Vektoren beheben. Das ist auch der Grund warum ich diese Erfahrung an Euch weitergeben wollte.

Das wäre doch mal ein interessanter Benchmark unter realen Bedingungen ;)
Ist der Code dazu irgendwo öffentlich? Wäre interessant, wie das mit GC-Tuning und/oder aktuellen JVMs aussieht.


Aber in dem ganzen Kontext fällt mir ein wunderbares Zitat ein:
Brian Goetz hat gesagt.:
Often, the way to write fast code in Java applications is to write dumb code -- code that is straightforward, clean, and follows the most obvious object-oriented principles.
 

Blender3D

Top Contributor
Das wäre doch mal ein interessanter Benchmark unter realen Bedingungen ;)
Ist der Code dazu irgendwo öffentlich? Wäre interessant, wie das mit GC-Tuning und/oder aktuellen JVMs aussieht.
Ich mache meinen Code nicht öffentlich. Hat ein paar tausend Codezeilen. Fakt ist, dass nicht konstante Objekte halfen.
Du kannst Dich ja an String und StringBuilder als Beispiel versuchen.
Aber ich habe einen kleinen Benchmark zum Vergleich mit einem konstanten Vektor und einem nicht konstanten Vektor geschrieben --->

upload_2018-1-8_21-51-42.png
Der Geschwindigkeitsunterschied ist deutlich messbar ( > 230%), was den Unterschied belegt, obwohl wir es hier noch nicht einmal mit einem Langzeittest zu tun haben, wo Grafiken Sounds und andere Aufgaben die Virtual Machine und den GC parallel fordern.
Anbei der Benchmark.
Code:
public class Vector {
    private double x;
    private double y;

    public Vector(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public Vector add(Vector v) {
        this.x += v.x;
        this.y += v.y;
        return this;
    }

}
Code:
public final class VectorConst {
    private final double x;
    private final double y;

    public VectorConst(double x, double y) {
        this.x = x;
        this.y = y;
    }

    public VectorConst add(VectorConst v) {
        return new VectorConst(x + this.x, y + this.y);
    }

}
Code:
public class Test {
    public static void main(String[] args) {
        long num = 80000;
        Vector v = new Vector(0, 0);
        Vector vAdd = new Vector(1, 1);
        VectorConst vC = new VectorConst(0, 0);
        VectorConst vAddC = new VectorConst(1, 1);

        for (int ii = 0; ii < 5; ii++) {
            System.out.print("      KONSTANT Vektor " + num + " Additionen ");
            long start = System.currentTimeMillis();
            for (long i = 0; i < num; i++)
                vC = vC.add(vAddC);
            long dauerConst = System.currentTimeMillis() - start;
            System.out.println("Dauer: " + dauerConst + " ms");

            System.out.print("nicht KONSTANT Vektor " + num + " Additionen ");
            start = System.currentTimeMillis();
            for (long i = 0; i < num; i++)
                v.add(vAdd);
            long dauerNotConst = System.currentTimeMillis() - start;
            System.out.println("Dauer: " + dauerNotConst + " ms");
            double factor = 0;

           
            if (dauerNotConst != 0) {
                factor =  dauerConst / (double) dauerNotConst;
            } else {
           
                factor = dauerConst >0 ? dauerConst : 0;
            }

            System.out.println("------------------------------------------------------");
            System.out.println(String.format("NICHT KONSTANT ist [%.2f] x  schneller als KONSTANT!", factor));
            System.out.println("------------------------------------------------------");
            num *= 10;
        }

    }
}
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
Ganz übersehen, dass hier noch was passiert ist :/

Du kannst Dich ja an String und StringBuilder als Beispiel versuchen.
Und trotzdem kommt niemand auf die Idee, Strings überall mit StringBuilder zu nutzen ;)
Immutable und Builder-Pattern findet man ziemlich häufig zusammen ;)

Aber ich habe einen kleinen Benchmark zum Vergleich mit einem konstanten Vektor und einem nicht konstanten Vektor geschrieben --->

würdest du mit mir konform gehen und sagen, dass deins annähernd äquivalent zu diesem JMH-Benchmark ist?
Bevor ich mir jetzt die Mühe mache und dann nachher doch irgendetwas wesentliches übersehe...
(Bevor du das kleine N kritisierst: das ganze wird mehrmals ausgeführt, insgesamt dürfte es deutlich mehr Aufrufe als in deiner Variante geben)

Code:
@Benchmark
    public void mutable() {
        Vector v = new Vector(0, 0);
        Vector vAdd = new Vector(1, 1);

        for (long i = 0; i < 1_000; i++) {
            v.add(vAdd);
        }
    }

    @Benchmark
    public void immutable() {
        VectorConst vC = new VectorConst(0, 0);
        VectorConst vAddC = new VectorConst(1, 1);

        for (long i = 0; i < 1_000; i++) {
            vC = vC.add(vAddC);
        }
    }

Der Geschwindigkeitsunterschied ist deutlich messbar ( > 230%), was den Unterschied belegt, obwohl wir es hier noch nicht einmal mit einem Langzeittest zu tun haben, wo Grafiken Sounds und andere Aufgaben die Virtual Machine und den GC parallel fordern.
Das sind durchaus ein paar GB an Speicher pro Sekunde, das unterschätzt man schnell ;)
 

Blender3D

Top Contributor
Genau das, was da steht? Man sollte einfach nicht unterschätzen, was in einem solchen Test an Speicher alloziert wird.
Die Frage die zu beantworten ist: "Stimmt etwas mit meinem Benchmark nicht, oder hast Du andere Ergebnisse erhalten als ich?"
Mein Benchmark beinhaltet alle notwendigen Klassen, um diesen nachvollziehen zu können.
Wenn Du damit ähnliche Ergebnisse bekommst wie die, die ich oben angeführt habe, dann stellt sich die Frage:
"Was willst Du mit Deinen alternativen nicht vollständig geposteten Benchmarks belegen?"
Der GC ist wie oben mehrfach erwähnt nicht deterministisch. Das bedeutet eine Schleife mit einigen Aufrufen lässt den Mehraufwand eben noch nicht erkennen den er mit Konstanten Objekten in gewissen Situationen hat.

Das sind durchaus ein paar GB an Speicher pro Sekunde, das unterschätzt man schnell ;)
Wenn Du damit meinst, dass bei einem hohen Datendurchsatz die Probleme spürbar sind, dann haben wir ja den Beleg dafür, dass es auf den Kontext ankommt ob man konstanten Objekte verwendet oder nicht.

Und trotzdem kommt niemand auf die Idee, Strings überall mit StringBuilder zu nutzen ;)
Immutable und Builder-Pattern findet man ziemlich häufig zusammen ;)
Soll ich daraus schließen, dass die Java Entwickler die Klasse Stringbuilder, ohne Grund hinzugefügt habe?
https://www.java-blog-buch.de/0308-stringbuffer-und-stringbuilder/
Ein weiteres Beispiel ist die Klasse Point. Sie findet in Java sehr oft eine Verwendung. z.B. im MouseListener ...
Code:
@Override
    public void mouseReleased(MouseEvent e) {   
        Point p = e.getPoint();
    }
Diese Klasse ist nicht konstant und besitzt auch noch öffentliche Attribute. Der Grund ist hier ebenfalls Geschwindigkeit.
Jetzt könnte man meinen die Typen die Java entwickelt haben sind alles Anfänger und machen nur Unsinn.
Wenn da nicht noch Benchmarks wären die genau das belegen. Es macht eben Sinn Dinge flexibel zu halten, und es dem Benutzer zu überlassen, eine für den Kontext vernünftige Lösung zu finden. Und genau das ist das Thema unseres Diskurses.
Aber jeder darf diesbezüglich seine eigene Meinung haben. Und ich will Dir Deine nicht nehmen.
Unterschiedliche Meinungen sind es die uns alle weiterbringen. ;)
Aber wie oben bereits erwähnt und das meine ich so wie ich es sage: Ich halte Dich für ein wertvolles Mitglied in diesem Forum mit einem breitgestreuten Wissen. Ich selbst habe auch schon von Deinen Tipps des Öfteren profitiert. Und ich hoffe, dass Du mir nicht :mad: bist, weil ich zu diesem Thema halt meine eigene Meinung habe.
 

mrBrown

Super-Moderator
Mitarbeiter
Die Frage die zu beantworten ist: "Stimmt etwas mit meinem Benchmark nicht, oder hast Du andere Ergebnisse erhalten als ich?"
Mein Benchmark beinhaltet alle notwendigen Klassen, um diesen nachvollziehen zu können.
Wenn Du damit ähnliche Ergebnisse bekommst wie die, die ich oben angeführt habe, dann stellt sich die Frage:
"Was willst Du mit Deinen alternativen nicht vollständig geposteten Benchmarks belegen?"
Eigentlich hab ich den Benchmark noch gar nicht ausgeführt - ich wollte den nur in einen JMH-Benchmark überführen, um ihn dann auszuführen.
Damit vermeidet man eben typische Probleme mit Warmup, Zeit-Messung, gegenseitiges Beeinflussen...

Falls du JMH nicht kennen solltest: das ist das Standard-Benchmark-Tool für Java. Vollständig sind meine beiden Methoden für den Benchmark schon, der Rest übernimmt eben das Framework.
Der GC ist wie oben mehrfach erwähnt nicht deterministisch. Das bedeutet eine Schleife mit einigen Aufrufen lässt den Mehraufwand eben noch nicht erkennen den er mit Konstanten Objekten in gewissen Situationen hat.
Mit JMH kann man zB auch den allozierten Speicher und den GC tracken. Das Framework ist genau dafür da: das nicht-deterministische möglichst zu eliminieren.
Oder welchen nicht erkennbaren Aufwand siehst du da?

Soll ich daraus schließen, dass die Java Entwickler die Klasse Stringbuilder, ohne Grund hinzugefügt habe?
https://www.java-blog-buch.de/0308-stringbuffer-und-stringbuilder/
Nein - der hat natürlich Vorteile. Aber mit gutem Grund ist String selbst immutable und StringBuilder/Buffer ist nicht als Ersatz dafür gedacht, sondern eben für einen anderen Anwendungsfall ;)

Ein weiteres Beispiel ist die Klasse Point. Sie findet in Java sehr oft eine Verwendung. z.B. im MouseListener ...
Diese Klasse ist nicht konstant und besitzt auch noch öffentliche Attribute. Der Grund ist hier ebenfalls Geschwindigkeit.
Die Klasse stammt aus den Anfangszeiten von Java und gehört zum Deprecated'en AWT, guck dir lieber das aktuelle Pendant aus JavaFX an ;)
An der gezeigten Stelle bekommt man übrigens nur eine defensive Kopie, da geht dann der Vorteil schon wieder flöten ;)

Jetzt könnte man meinen die Typen die Java entwickelt haben sind alles Anfänger und machen nur Unsinn.
Nein - natürlich machen sie nicht nur Unsinn. Deshalb gibt es ja so einen schönes Zitat von denen, dass man lieber gut design'ten Code (und damit im Performance-Sinne schlechten Code) schreiben solle :p
Und wenn man sich die Entwicklung des JDKs mal anguckt, geht das in vielen Fällen ganz eindeutig zu Immutable Datentypen ;)

Wenn da nicht noch Benchmarks wären die genau das belegen. Es macht eben Sinn Dinge flexibel zu halten, und es dem Benutzer zu überlassen, eine für den Kontext vernünftige Lösung zu finden. Und genau das ist das Thema unseres Diskurses.


Die "vernünftige Lösung" ist in den meisten Fällen ein auf Hören-Sagen beruhendes "mutable ist schneller deshalb muss man das nehmen", in vielen Fällen (dein erwähntes Spiel mag da durchaus eine Ausnahme gewesen sein) würde aber besseres Design zu gleichen Ergebnissen führen ;)
Und gleiches gilt für Benchmarks, die da das eine oder andere belegen - übrigens auch bei deinem Code, niemals würde man in einer echten Verwendung beide Klassen mischen und alles in nur einer Methode haben. Stattdessen wäre es eher kleine, häufig aufgerufene Methoden und keine Mischung der Klassen - genau deshalb wollte ich das in einen JMH-Benchmark überführen (der halt nebenbei noch andere Sachen bietet, zb läuft er dann hier gegen mehrere JVM-Versionen), ganz ohne irgendein Ergebnis vorwegnehmen zu wollen.
Gleiches würd ich gern mit dem "Mesh-Rendering" machen, das ist immerhin ein realer Anwendungsfall für Vektoren - nur würde ich vorher gern sicher gehen, dass meine Umsetzung mit mutable-Vektoren kein völliger Murks ist ;)

Und ich hoffe, dass Du mir nicht :mad: bist, weil ich zu diesem Thema halt meine eigene Meinung habe.
Nein, natürlich nicht - das Leben wäre ziemlich langweilig ohne Diskussionen ;)
 

Blender3D

Top Contributor
Eigentlich hab ich den Benchmark noch gar nicht ausgeführt - ich wollte den nur in einen JMH-Benchmark überführen, um ihn dann auszuführen.
Damit vermeidet man eben typische Probleme mit Warmup, Zeit-Messung, gegenseitiges Beeinflussen...
Mein Benchmark weißt den Unterschied deutlich auf. Auch mein PacmanClone läuft so jetzt super --> Je nach Kontext ist es eventuell vernünftiger Mutable Klassen zu verwenden. Ob das nun Warmup oder anderen internen Lösungen des GC geschuldet ist spielt da keine Rolle. Effekt da --> Mutable --> Effekt weg --> also in dem Fall besser gelöst!
Mein geposteter Benchmark zeigt außerdem, dass bei weniger Aufrufen kein Geschwindigkeitsunterschied besteht.
Vermutung:
Wahrscheinlich hat der GC einen Speicherbereich zur Verfügung der von Anfang an als eine Art Puffer dient. Erst wenn der voll wird muss er tätig werden. Dann erst kann man den Nachteil der Geschwindigkeit feststellen. Mein Benchmarks zeigt mit zunehmenden n auch:
Geschwindigkeit gleich -> 15 x schneller -> 2,3 x schneller -> 2,3 x schneller ....
upload_2018-1-23_13-48-3.png
Das deutet darauf hin, dass der GC sich auf den kurzlebigen Müll relativ gut einpendelt. Trotzdem beleibt am Ende ein Geschwindigkeitsnachteil ( also bezüglich Geschwindigkeit --> konstant != Nicht konstant ) .
Eventuell war das in einer früheren Version noch nicht so gut gelöst. Mein Pacman hatte mit der konstanten Variante sehr störende Ruckler, die in nicht vorhersehbaren Intervallen auftraten. War nicht schön. Ich löste das Problem mit nicht konstanten Vektoren ( War glaube ich noch Java 6 oder 7 ).
Eventuell ist das oben erwähnte Einpendeln, damals noch nicht implementiert gewesen. Wäre eine gut Erklärung für die sporadischen Ruckler die ohne Ende auftraten ( gleich schnell --> -15 mal Geschwindigkeit --> gleichschnell -->usw. ) .

Aber um das Thema nicht aus den Augen zu verlieren von dem wir hier sprechen:
Konstante Objekte sind im Allgemeinen nicht gleich schnell wie nicht konstante Objekte.
Also kann je nach Kontext des Problems ein nicht konstante Lösung die bessere Wahl sein.


In den meisten Fällen ist natürlich die konstante Lösung zu bevorzugen, weil diese intuitiver in der Benutzung ist.

C++ hat hier sowieso das bessere Sprachmittel da sind Objekte, die als konstant erklärt werden wirklich konstant.
Und der Compiler wirft einen Fehler, wenn der Benutzer auf einem konstanten Objekt eine nicht konstante Funktion aufruft. Aber auch hier sollten konstante Objekte gezielt eingesetzt werden.
:)
 
Zuletzt bearbeitet:

mrBrown

Super-Moderator
Mitarbeiter
Mein Benchmark weißt den Unterschied deutlich auf.
Dein Benchmark zeigt vor allem auch die Probleme beim schreiben von Benchmarks (deshalb gibt es dafür ein Framework).

Mein geposteter Benchmark zeigt außerdem, dass bei weniger Aufrufen kein Geschwindigkeitsunterschied besteht.
Nein, das zeigt nur, dass du falsch misst.
System.currentTimeMillis hat eine Auflösung von etwa 15ms, alle Werte darunter sagen also überhaupt nichts aus (einer der Gründe, ein Framework dafür zu nutzen).

Wahrscheinlich hat der GC einen Speicherbereich zur Verfügung der von Anfang an als eine Art Puffer dient. Erst wenn der voll wird muss er tätig werden. Dann erst kann man den Nachteil der Geschwindigkeit feststellen. Mein Benchmarks zeigt mit zunehmenden n auch:
Ja, Ähnlichkeiten mit Buffern gibt es da schon.
Aber nein, das hat hiermit nichts zu tun. Je nach JVM und verwendetem GC ist es auch durchaus unterschiedlich, wann der läuft.
Und nein, der GC hat keinen sonderlich großen Einfluss auf die Laufzeit, nur etwa 1% der Laufzeit dürfte für den GC "verloren" gehen (ein weiterer der Gründe, ein Framework dafür zu nutzen).

Geschwindigkeit gleich -> 15 x schneller -> 2,3 x schneller -> 2,3 x schneller ....
Die ersten beiden Werte sind wie gesagt irrelevant, da unter der Auflösung.

Das deutet darauf hin, dass der GC sich auf den kurzlebigen Müll relativ gut einpendelt. Trotzdem beleibt am Ende ein Geschwindigkeitsnachteil ( also bezüglich Geschwindigkeit --> konstant != Nicht konstant ) .
Der GC hat wie schon mehrmal gesagt nichts damit nicht zu tun.
Was sich einpendelt ist der Compiler (wobei "einpendeln" kompilieren meint), und das dürfte er bedingt durch das Design hier recht schlecht machen. (das "einpendeln" ist ein weiterer Grund fürs Framework)

Ich würde dir gerne mit einem entsprechenden Benchmark zeigen, den willst du aber anscheinend nicht sehen...

Immerhin länger her als meine ersten Kontakte mit Computern...

Eventuell ist das oben erwähnte Einpendeln, damals noch nicht implementiert gewesen. Wäre eine gut Erklärung für die sporadischen Ruckler die ohne Ende auftraten ( gleich schnell --> -15 mal Geschwindigkeit --> gleichschnell -->usw. ) .
Der Schluss aus den gemessenen Zeit ist wie gesagt falsch, näher dran am echten Wert ist konstant halb so schnell - in einem für den Hotspot-Compiler sehr schlecht optimierbaren Fall (der Performance-Unterschied zwischen int und float ist btw größer). Aber ja, die Compilertechnik ist seit dem ziemlich fortgeschritten, vor ~10 Jahren ist der Unterschied vermutlich ein ganzes Stück größer gewesen.

Konstante Objekte sind im Allgemeinen nicht gleich schnell wie nicht konstante Objekte.
Also kann je nach Kontext des Problems ein nicht konstante Lösung die bessere Wahl sein.
Den Punkt würde ich eben anders formulieren - Konstante Objekte können in vielen Fällen genauso schnell sein.
Bei keinem der hier gezeigten Beispiel gibt es mit vernünftigem Design und aktuellem Compiler (je nach Beispiel auch schon mit normalen HotSpot) spürbare Unterschiede. Übrigens auch nicht bei dem Beispiel aus dem anderem Thread.


C++ hat hier sowieso das bessere Sprachmittel da sind Objekte, die als konstant erklärt werden wirklich konstant.
Und der Compiler wirft einen Fehler, wenn der Benutzer auf einem konstanten Objekt eine nicht konstante Funktion aufruft. Aber auch hier sollten konstante Objekte gezielt eingesetzt werden.
Wie schon mal erwähnt: was ähnliches (naja, eigentlich sehr schlecht vergleichbares) wird es für Java mit der Einführung von Value-Types (die für den Nutzer nicht anders als immutable Objekte sind) auch geben.
 

Blender3D

Top Contributor
Der Schluss aus den gemessenen Zeit ist wie gesagt falsch, näher dran am echten Wert ist konstant halb so schnell - in einem für den Hotspot-Compiler sehr schlecht optimierbaren Fall (der Performance-Unterschied zwischen int und float ist btw größer). Aber ja, die Compilertechnik ist seit dem ziemlich fortgeschritten, vor ~10 Jahren ist der Unterschied vermutlich ein ganzes Stück größer gewesen.
Möglich aber vom Benchmark einmal abgesehen: Mit konstanten Objekten ruckelt das Spiel, aber nicht mit nicht konstanten Objekten. Ich schließe daraus, wenn ich an dem Programm nur von konstanten Objekten auf nicht konstante Objekte wechsle, ist das Problem gelöst. Warum ist der Schluss, dass hier ein Unterschied besteht falsch?
Effekt da --> Mutable --> Effekt weg --> also in dem Fall besser gelöst!
Soll ich wieder Konstant machen und das Ding ruckeln lassen, weil es ja keine Unterschiede gibt oder geben darf ?
 

mrBrown

Super-Moderator
Mitarbeiter
Möglich aber vom Benchmark einmal abgesehen: Mit konstanten Objekten ruckelt das Spiel, aber nicht mit nicht konstanten Objekten. Ich schließe daraus, wenn ich an dem Programm nur von konstanten Objekten auf nicht konstante Objekte wechsle, ist das Problem gelöst. Warum ist der Schluss, dass hier ein Unterschied besteht falsch?

Soll ich wieder Konstant machen und das Ding ruckeln lassen, weil es ja keine Unterschiede gibt oder geben darf ?
Wie gesagt - in Ausnahmefällen kann es durchaus einen größeren Unterschied machen.
Es ist aber nicht so, das der Unterschied während der Laufzeit zwischen gleich und 15mal langsamer schwankt, wie du es aus deinem Test geschlossen hast.

Und wie auch gesagt, möglicherweise hätte man den gleichen Effekt auch mit an anderen Stellen verändertem Design, neuerer JVM, anderem GC oder sonstigen beheben können - ohne Code kann man da allerdings nicht mehr zu sagen als "kann daran gelegen haben".
 
Zuletzt bearbeitet:
Ähnliche Java Themen
  Titel Forum Antworten Datum
O Welcher Object-Lock-Pool bei static Variablen? Java Basics - Anfänger-Themen 3
T variablen klassen übergreifend Java Basics - Anfänger-Themen 12
T Variablen Java Basics - Anfänger-Themen 1
N Verständnis Frage zu Variablen Java Basics - Anfänger-Themen 3
M Aufsummieren von variablen Wertegrößen Java Basics - Anfänger-Themen 17
M Mehrere Daten/ Variablen Speichern Java Basics - Anfänger-Themen 9
J Speichern von zwei Variablen durch Auslesen aus einem Numberfield Java Basics - Anfänger-Themen 2
ashi Variablen aufrufen Java Basics - Anfänger-Themen 17
U Warum kann ich, auf private Variablen zugreifen, wenn ich ein Objekt in der Klasse, die private Variablen hat erstelle und dort drauf zugreifen will? Java Basics - Anfänger-Themen 7
B Konkatenieren eines Strings und inkremtierenden Zahl zu einer INT Variablen Java Basics - Anfänger-Themen 7
A 2 Strings vergleichen in einer methode wenn man mit Globalen variablen arbeitet Java Basics - Anfänger-Themen 12
C Konstruktoren und Variablen Java Basics - Anfänger-Themen 42
F Auf Variablen eines Konstruktors zugreifen Java Basics - Anfänger-Themen 4
N Variable aus anderen Variablen in statischer Klasse berechnen/abspeichern? Java Basics - Anfänger-Themen 4
M Wie kann ich bei int-Variablen im exception handler auf bestimmte Strings reagieren? Java Basics - Anfänger-Themen 5
M Warum dürfen Objekte einer Klasse auf statische Variablen dieser Klasse referenzieren? Java Basics - Anfänger-Themen 10
B Variablen Variablen übertragen ohne Klassen Java Basics - Anfänger-Themen 5
B Methoden Methoden haben kein Zugriff auf variablen Java Basics - Anfänger-Themen 4
T Java Swing - Dreieck zeichnen mit verschiedenen Variablen Java Basics - Anfänger-Themen 8
Arif Vererbung Methodenvererbung mit finalen Variablen Java Basics - Anfänger-Themen 1
M Wie kann ich ein Objekt erstellen, wenn sich der Klassenname in einer Variablen befindet? Java Basics - Anfänger-Themen 10
S Variablen Variablen in einer Schleife erstellen lassen Java Basics - Anfänger-Themen 11
J Ich brauche Hilfe bei einem Code (Variablen speichern) Java Basics - Anfänger-Themen 29
F Variablen Werte einer Klasse überschreiben Java Basics - Anfänger-Themen 4
N Speichern von Werten in Variablen nach Schließen des Programms Java Basics - Anfänger-Themen 3
J Frage dazu Variablen klassenübergreifend zu verändern Java Basics - Anfänger-Themen 22
M Java Instanz-Variablen ? Java Basics - Anfänger-Themen 3
B Variablen von Methoden übertragen Java Basics - Anfänger-Themen 2
M Variablen umbenennen Java Basics - Anfänger-Themen 1
T Körper Brechnung - Lokale Variablen in Methoden übergeben Java Basics - Anfänger-Themen 10
P Zugriff auf Variablen anderer Klassen in Greenfoot Java Basics - Anfänger-Themen 1
mars90 Fehler in der Variablen Deklaration Java Basics - Anfänger-Themen 8
E Variablen in formatierter Ausgabe Java Basics - Anfänger-Themen 15
V Schleife für das Einlesen von Werten für int Variablen, die Bestandteil von Arrays sein sollen Java Basics - Anfänger-Themen 16
M Komisches Verhalten der Variablen Java Basics - Anfänger-Themen 6
H Variablen Multiplikation einer inkrementierten Variablen Java Basics - Anfänger-Themen 5
scratchy1 Variablen vertauschen wenn Bedingung "umgedreht" wird Java Basics - Anfänger-Themen 40
J Variablen mit einer anderen Klasse bekannt machen Java Basics - Anfänger-Themen 7
C Methoden Problem beim Speichern von Variablen Java Basics - Anfänger-Themen 1
A Übergreifende Variablen Java Basics - Anfänger-Themen 17
A Variablen Verständnisfrage bzgl. Variablen/Referenzen Java Basics - Anfänger-Themen 3
H Variablen Methode zum Abfragen von Variablen aus Subklassen Java Basics - Anfänger-Themen 9
F Liste nach einer Variablen sortieren Java Basics - Anfänger-Themen 6
L Variablen in anderen Klassen nutzen Java Basics - Anfänger-Themen 6
M For-Schleife durch zwei versch. Variablen begrenzen Java Basics - Anfänger-Themen 27
J Klassen Variablen in andere Klassen oder Methoden übernehmen Java Basics - Anfänger-Themen 1
P Liste auslesen und in Variablen speichern Java Basics - Anfänger-Themen 7
temi Redundante Variablen Java Basics - Anfänger-Themen 29
Aprendiendo Zweifel mit versteckter Variablen Java Basics - Anfänger-Themen 16
L Variablen einmal nur zu weisen Java Basics - Anfänger-Themen 62
D Statische Variablen/Methoden Java Basics - Anfänger-Themen 3
R Abfrage von Variablen in Unterklassen einer ArrayList Java Basics - Anfänger-Themen 9
M Listener für Button - Wert von Variablen verändern Java Basics - Anfänger-Themen 14
S Vererbung Variablen klassenübergreifend nutzen Java Basics - Anfänger-Themen 42
R Auf Variablen einer anderen Klasse zugreifen? Java Basics - Anfänger-Themen 1
D Fehlermeldung obwohl Variablen bereits deklariert sind? Java Basics - Anfänger-Themen 14
E 2 Probleme - Datum & private finale Variablen Java Basics - Anfänger-Themen 5
Aruetiise Variablen JFrame und Variablen Java Basics - Anfänger-Themen 3
L Variablen dekleration + reset Java Basics - Anfänger-Themen 16
T Übernahme einer Variablen im ActionListener/ActionEvent Java Basics - Anfänger-Themen 2
D Kapselung final Variablen mit Getter? Java Basics - Anfänger-Themen 2
C Variablen von einem JFrame in einen anderen übertragen Java Basics - Anfänger-Themen 3
P Interface Variablen-Inhalte werden nicht übergeben Java Basics - Anfänger-Themen 3
C Variablen in Schleifen außerhalb verwenden Java Basics - Anfänger-Themen 2
S Variablen Flexible Variablen Namen Java Basics - Anfänger-Themen 3
R Erste Schritte 3 Variablen hochzählen lassen Java Basics - Anfänger-Themen 1
RowdyN Variablen Variablen beliebig benennen? Java Basics - Anfänger-Themen 6
S OOP Variablen zwischen mehreren Klassen Java Basics - Anfänger-Themen 11
T Koordinatensystem zeichnen - Variablen merken? Quadratische Funktion zeichnen? Java Basics - Anfänger-Themen 5
H Variablen einer Schleife zwischenspeichern Java Basics - Anfänger-Themen 2
P Klassen Variablen von einer Klasse zur anderen Java Basics - Anfänger-Themen 5
H Objekt überschreibt Variablen vorheriger Objekte Java Basics - Anfänger-Themen 2
P Variablen in Excel speichern Java Basics - Anfänger-Themen 6
S PHP Aufruf mit mehreren Variablen Java Basics - Anfänger-Themen 2
F Variablen unterschiedlicher Datentypen Java Basics - Anfänger-Themen 6
S ActionListener und Statische Variablen Java Basics - Anfänger-Themen 4
Arif Vererbung Vererbung Variablen überschreiben Java Basics - Anfänger-Themen 1
L Vergleich zweier Variablen, mit Abweichung Java Basics - Anfänger-Themen 3
P Variablen einer Methode in andere Method übergeben Java Basics - Anfänger-Themen 6
G Variablen Verwendung von Variablen in anderer Klasse Java Basics - Anfänger-Themen 6
P Textfelder in Variablen speichern Java Basics - Anfänger-Themen 13
K arraygröße durch variablen Konstruktor? Java Basics - Anfänger-Themen 7
J Vererbung Variablen aus Superklasse übernehmen Java Basics - Anfänger-Themen 2
L Variablen aus TXT Datei auslesen und vergleichen. Java Basics - Anfänger-Themen 5
K Welchen Typ haben Variablen in Default-Methoden und in statischen Methoden in Schnittstellen? Java Basics - Anfänger-Themen 4
K Wieso muss man finale statische Variablen sofort oder eben im Konstruktor initialisieren? Java Basics - Anfänger-Themen 2
L zwei Variablen gleichzeitig übergeben Java Basics - Anfänger-Themen 6
J Vererbung privater Variablen Java Basics - Anfänger-Themen 7
D Klassen Verhalten von Klassenvererbung bei Variablen Java Basics - Anfänger-Themen 1
G Alle Objekte und Variablen automatisch ausgeben Java Basics - Anfänger-Themen 7
S OOP Werte von Vektoren mit 3 Variablen ausgeben lassen Java Basics - Anfänger-Themen 3
A Variablen aus einer Schleife gezielt auslesen Java Basics - Anfänger-Themen 11
A Methoden Zugriff auf eingelesene Variablen in der main Methode (ohne Änderung der Parameterliste) Java Basics - Anfänger-Themen 4
K Enigma, variablen übernehmen Java Basics - Anfänger-Themen 6
F Erste Schritte Dynamische Variablen Java Basics - Anfänger-Themen 15
N Variablen zurück casten Java Basics - Anfänger-Themen 3
M Repräsentation von variablen/OOP Java Basics - Anfänger-Themen 2
B Probleme beim einlesen einer short variablen für einen Array Java Basics - Anfänger-Themen 1
S Warum erlaubt ein while-Loop keine Variablen-Declaration wie der for-Loop..? Java Basics - Anfänger-Themen 6
S Variablen Variable erzeugen und Array mit Variablen befüllen Java Basics - Anfänger-Themen 26

Ähnliche Java Themen

Neue Themen


Oben