Performancekiller?

Status
Nicht offen für weitere Antworten.

tomkruse

Bekanntes Mitglied
Hi!

Da ich viel mit Handys mache und die ja eine sehr eingeschränke Prozessorleistung haben würde mich mal interessieren, was so die echten Performancekiller sind. Also was sollte man auf jeden Fall vermeiden, wenn man nicht verschwenderisch sein will?

Welche Tricks gibt es so, bzw. was muß man beachten?

Cu - Tom.
 
B

bygones

Gast
keine String aneinanderhängen mit dem String Object, sonder mit StringBuffer:

Statt:
Code:
String s = "";
for(int i = 0; i< 1000; i++) {
   s = " " + i;
}

Eher:
Code:
StringBuffer buffer = new StringBuffer(1000);
for(int i = 0; i< 1000; i++) {
  buffer.append(i);
}

Versuchen auf synchronized Methoden zu verzichten, versuchen Methoden mit wenig Paramter aufzurufen.

Eher LinkedList / ArrayList anstatt Vector

Sowohl bei Datei schreiben als auch beim lesen einen Buffe verwenden.

Ansonsten noch:
Jede Allozierung von Speicher kostet Rechenzeit. Der Speicher muß entweder vom Betriebssystem oder vom Laufzeitsystem der VM beschafft werden. Auch das (automatische) Initialisieren des Speichers kostet Zeit. Das Anlegen eines Arrays mit 1000 Elementen dauert wesentlich länger als das eines mit 10 Elementen.

Objekte mit aufwendigen Konstruktoren benötigen möglicherweise viel Zeit zur Initialisierung. Bei ihnen kann es sinnvoll sein, sie zu »recyceln«. Dazu werden sie nach Gebrauch in einer geeigneten Datenstruktur gesammelt und können dem nächsten Interessenten (alternativ zur Erzeugung eines neuen Objekts) zur Verfügung gestellt werden. Vor der Verwendung muß dieser das Objekt natürlich geeignet initialisieren.

Nicht mehr referenzierter Speicher belastet den Garbage Collector und erfordert CPU-Zeit, um dem Programm wieder zugeführt werden zu können.

In ungünstigen Fällen kann es sein, daß die VM den benötigten Speicher schrittweise in relativ kleinen Stücken beim Betriebssystem anfordert. Das kostet unter Umständen sehr viel Zeit. In diesem Fall kann es sinnvoll sein, mit Hilfe des Schalters -Xms den beim Start der VM anzufordernden Speicher auf einen höheren Wert einzustellen.

Große Mengen an temporären, kurzlebigen Objekten belasten die VM ebenfalls. Derartige Allokationsszenarien entstehen beispielsweise beim Modifizieren von Strings oder wenn primitive Typen mit Hilfe ihrer Wrapperklassen in Collections gespeichert werden sollen. In Abschnitt 49.3 zeigen wir ein harmlos aussehendes Programm, das beim Anlegen von 10 kByte Nutzdaten 75 MByte Datenmüll erzeugt.

Werden Referenzen auf Objekte nicht gelöscht, bleibt der zugeordnete Speicher belegt und der Garbage Collector kann ihn nicht wieder freigeben. Das belastet nicht nur die VM, die zunehmend neuen Speicher beim Betriebssystem anfordern muß, sondern führt früher oder später zum Absturz des Programms mit einem OutOfMemoryError.

Derartige Speicherlecks entstehen, wenn eigentlich nicht mehr benötigte Objekte an »lebenden« Referenzen hängen (also an Variablen, die im Programm noch benötigt werden). Lebende Referenzen sind die lokalen Variablen auf den Stacks aller laufenden Threads plus alle statischen Variablen des Programms. Zudem natürlich alle Variablen, die indirekt daran hängen. Als Programmierer sollte man diesbezüglich den statischen Variablen (insbesondere wenn sie auf Collections verweisen) besonderes Augenmerk schenken.

Um dem Garbage Collector die Arbeit zu erleichtern, kann es sinnvoll sein, ihn in Programmpausen von Zeit zu Zeit durch Aufruf der Methode gc der Klasse System explizit aufzurufen. Das Programm kann ihm auch dadurch helfen, daß nicht mehr benötigten Objektvariablen explizit der Wert null zugewiesen wird.
 

tomkruse

Bekanntes Mitglied
Hi!

Danke! Das hilft mir schon mal sehr weiter.
Irgendwo hab ich gelesen, daß der Typ long sehr langsam sein soll und der Typ int wesentlich schneller im Zugriff ist.
Arrays sind angeblich auch sehr langsam, weil bei jedem Zugriff ein null-check durchgeführt wird.

Code:
x=a[5];
x=x+3;
x=x*2;
x++;
a[5]=x;

ist angeblich schneller als

Code:
a[5]=a[5]+3;
a[5]=a[5]*2;
a[5]++;

Ok, ist sicher nicht so dramatisch, weil nicht sooo viele Zugriffe auf das Array stattfinden, aber in einer
Schleife könnte sich das schon eher auswirken, oder?

Cu - Tom.
 

jptc.org

Bekanntes Mitglied
tomkruse hat gesagt.:
Hi!
Irgendwo hab ich gelesen, daß der Typ long sehr langsam sein soll und der Typ int wesentlich schneller im Zugriff ist.
Arrays sind angeblich auch sehr langsam, weil bei jedem Zugriff ein null-check durchgeführt wird.

Ok, ist sicher nicht so dramatisch, weil nicht sooo viele Zugriffe auf das Array stattfinden, aber in einer
Schleife könnte sich das schon eher auswirken, oder?

Also im Allgemeinen sollte man in Schleifen vermeiden irgendwelche Methoden für den Zugriff auf Variablen aufzurufen (da fallen auch Arrays drunter). Es ist schneller vor dem Aufruf der Schleife diese Werte auszulesen und in temporäre Variablen zu speichern. Müssen in der Schleife immer neue Werte ausgelesen werden, so empfiehlt isch die Variablen dafür vor den Schleife zu definieren und in der Schleife immer wieder zu überschreiben.

Int ist schneller als long, da ja die Verarbeitung von int optimiert ist (nicht java sondern der prozessor kann damit besser umgehen). naja will man zeit sparen kann man auch gerne auf short, byte und (char) verzichten, da diese Typen intern immer wie int behandelt werden.

Karsten Voigt
http://www.java-performance-portal.org
 

tomkruse

Bekanntes Mitglied
Hi!

Und schon wieder habe ich etwas gelesen was mich etwas verwundert hat: Der Zugriff auf public Membervariablen scheint deutlich schneller zu sein als wenn man das Ganze über Zugriffsmethoden realisiert. Auch wenn die Zugriffs methoden mehr dem Grundsatz vom Objektorientiertem Design entsprechen, so kosten sie doch einiges. Methodenaufrufe scheinen überhaupt ziemlich was "extra zu kosten". Also es ist zwar übersichtlich, alles in handliche Methoden zu zerlegen, aber es dürfte einiges bringen, Methoden, die nur an wenigen Stellen aufgerunfen werden lieber zu "inlinen".

Cu - Tom.
 

schalentier

Gesperrter Benutzer
@tomkruse
wo hast du das her, wuerd mich mal interessieren. klar ist ein methodenaufruf langsamer als auf ne membervar selbst, da einfach mehr code auszufuehren ist. allerdings glaub ich kaum, dass sich das signifikant auf die performance auswirkt. da bringt glaub ich das schluesselwort "final" mehr, wodurch die spaete bindung verhindert wird.
gibt es inline als keyword bei java?
sollte das nicht eigentlich ein guter jit-compiler selbst erkennen und machen?

achso:
arrayzugriffe sind langsamer, da bei jedem zugriff (lesend und schreibend) ein ArrayBoundCheck durchgefuehrt wird, so dass im zweifelsfall eine OutOfBoundsException geworfen wird, anstatt irgendwelchen speicher zu ueberschreiben.
Is ein richtiges Array eigentlich spuerbar schneller als eine ArrayList oder was vergleichbares?

gl&hf
 
B

bygones

Gast
@tobias:

Zitat stammt aus dem Javabuch http://www.javabuch.de

Achja da steht auch noch:

Seit der Version 1.2 des JDK stehen mit den Klassen LinkedList und ArrayList auch alternative Listenimplementierungen zur Verfügung, die anstelle von Vector verwendet werden können. Hier ist jedoch Vorsicht geboten, soll das Programm nicht langsamer laufen als vorher. Die Klasse LinkedList implementiert die Datenstruktur in klassischer Form als doppelt verkettete Liste ihrer Elemente. Zwar entfallen dadurch die Kopiervorgänge, die beim Erweitern des Arrays erforderlich waren. Durch die Vielzahl der allozierten Objekte, in denen die Listenelemente und die Zeiger gespeichert werden müssen, und die teilweise ineffiziente Implementierung einiger Grundoperationen (insbesondere add) hat sich LinkedList jedoch im Test als relativ ineffizient herausgestellt. Wesentlich bessere Ergebnisse gab es mit der Klasse ArrayList. Sie ist ähnlich wie Vector implementiert, verzichtet aber (wie die meisten 1.2er Collections) auf die synchronized-Attribute und ist daher - insbesondere bei aktiviertem JIT und Zugriff mit add und get sehr - performant.
.....
Seit der Version 1.2 des JDK stehen mit den Klassen LinkedList und ArrayList auch alternative Listenimplementierungen zur Verfügung, die anstelle von Vector verwendet werden können. Hier ist jedoch Vorsicht geboten, soll das Programm nicht langsamer laufen als vorher. Die Klasse LinkedList implementiert die Datenstruktur in klassischer Form als doppelt verkettete Liste ihrer Elemente. Zwar entfallen dadurch die Kopiervorgänge, die beim Erweitern des Arrays erforderlich waren. Durch die Vielzahl der allozierten Objekte, in denen die Listenelemente und die Zeiger gespeichert werden müssen, und die teilweise ineffiziente Implementierung einiger Grundoperationen (insbesondere add) hat sich LinkedList jedoch im Test als relativ ineffizient herausgestellt. Wesentlich bessere Ergebnisse gab es mit der Klasse ArrayList. Sie ist ähnlich wie Vector implementiert, verzichtet aber (wie die meisten 1.2er Collections) auf die synchronized-Attribute und ist daher - insbesondere bei aktiviertem JIT und Zugriff mit add und get sehr - performant.
 

tomkruse

Bekanntes Mitglied
schalentier hat gesagt.:
@tomkruse
sollte das nicht eigentlich ein guter jit-compiler selbst erkennen und machen?

Hi!

Ich programmiere hauptsächlich auf mobilen Devices und da ist schon von vorneherein nicht so eine enorme Rechenleistung zu erwarten. Dementsprechend wirkt sich zusätzlicher Code schon ziemlich aus. Einen guten jit-Compiler kann man auf Handys leider ebensowenig voraussetzen wie einen guten GarbageCollector.

Wie ist das nun nochmal genau mit final? Was erreicht man damit (in punkto performance) und warum eigentlich?

Cu - Tom.
 

schalentier

Gesperrter Benutzer
Sagt dir das schluesselwort "virtual" von c++ was? final ist genau das gegenteil.

hier zwei einfache klassen:
Code:
class Foo {
  public void doIt(){ sout("Hi von Foo"); }
}
class Bar extends Foo {
  public void doIt() { sout("Hi von Bar"); }
}
...
Foo foo = new Foo();
foo.doIt(); // "Hi von Foo"
foo = new Bar();
foo.doIt(); // "Hi von Bar"

Das heisst, wenn ein Aufruf an foo.doIt() geht, muss die vm erstmal testen, welche doIt-Methode ausgefuehrt werden soll. (doIt() in Foo oder eine doIt() von einer Klasse, die von Foo abgeleitet ist)
Das nennt man spaete bindung und es benoetigt eine gewisse zeit nur fuer den test, welche methode genutzt werden soll.

mit dem schluesselwort final unterbindet man genau das. man kann also eine methode nicht mehr ueberschreiben, was einen geschwindigkeitsvorteil bringt.
 

tomkruse

Bekanntes Mitglied
Hi!

Ok, so ähnlich habe ich mir das eh gedacht. danke.
Nur, wenn ich jetzt die Methode doIt() der Classe bar final mache, dann ändert das nichts daran, daß immer noch nachgesehen werden muß, welches doIt() jetzt ausgeführt werden muß, das von Bar oder das von Foo, oder sehe ich das jetzt falsch?

Cu - Tom.
 

schalentier

Gesperrter Benutzer
Code:
class Foo { 
  public final void doIt(){ sout("Hi von Foo"); } 
} 
class Bar extends Foo { 
  public void doIt() { sout("Hi von Bar"); } 
  //--- COMPILER FEHLER ////
}

eine final methode kann man nicht ueberschreiben, da kommt ein compilerfehler
 

tomkruse

Bekanntes Mitglied
Hi!

Ich meinte, wenn man die Methode, die die andere überschreibt final macht. Das kann man ja.

Cu - Tom.
 
Status
Nicht offen für weitere Antworten.
Ähnliche Java Themen
  Titel Forum Antworten Datum
Chris_1980 PNG der Performancekiller Allgemeine Java-Themen 12

Ähnliche Java Themen


Oben