2016-11-15 3 views
2

In Dov Java-Leistungen Bulka und Skalierbarkeit: Volume 1 erwähnt der Autor, dass wegen der mit etwas wieJava Arraylist for-Schleife Optimierung

for (int i = 0; i < vector.size(); i++) { 
    // do something that does not modify vector size... 
} 

ist eigentlich eine kleine Optimierung Ausgabe über ein ArrayList looping somit konstante Berechnung von vector.size(), was impliziert, dass etwas wie

int size = vector.size();  
for (int i = 0; i < size; i++) { 
    // do something that does not modify vector size... 
} 

tatsächlich etwas effizienter ist. Als das Buch im Jahr 2000 geschrieben wurde, verwendete der Autor das Sun 1.2.2 JDK.

Hat noch diese Bedingung für neue JDKs halten? Oder ist die Java-Kompilierung jetzt intelligent genug, um diese Ineffizienzen zu beseitigen, trotz ihrer geringen Größe?

EDIT: Ich mach dir keine Sorgen über diese kleinen Optimierungen in meinem Code; Ich war einfach neugierig auf die Entwicklung des JDK.

+9

Wenn Sie nicht beweisen können, dass das wiederholte Aufrufen von 'size()' einen Engpass in Ihrem Code darstellt, verschwenden Sie keine Zeit mit solchen Mikrooptimierungen. In jedem Fall ist diese Optimierung nur unter bestimmten Bedingungen sicher (d. H. Dass kein anderer Thread die Größe von "Vektor" ändern kann, während Sie sich in der Schleife befinden). –

+4

Verwenden Sie eine 'for-each'-Schleife und sorgen Sie sich nicht um die Interna der' List <> '. In java 8 'List # forEach (...)' wird es noch mehr verstecken. – bradimus

+2

Die Grundregel von "mache nichts in einer Schleife, außer du musst es unbedingt in der Schleife machen" gilt immer noch. Ganz gleich, wie sich die Technologie entwickelt, es ist eine gute Sache, weniger Berechnungen in einer Schleife durchzuführen. –

Antwort

1

Gilt dieser Zustand auch für neuere JDKs? Oder ist die Java-Kompilierung jetzt intelligent genug, um diese Ineffizienzen zu beseitigen, trotz ihrer geringen Größe?

In Anbetracht des "Java-Compilers" javac hat sich nichts geändert und wird es wahrscheinlich auch nie werden. Es ist nicht seine Aufgabe, irgendwelche Optimierungen zu tun. So gesehen ist der erzeugte Bytecode sinnlos.

Optimierungen werden zur Laufzeit vom JIT Compiler (Oracle Hotspot) durchgeführt. Es kann sicherlich eine solche triviale Methode inline und es kann höchstwahrscheinlich auch die Größe in einem Register zwischenspeichern, so dass der Speicherzugriff beseitigt wird. Dazu muss es alles in die Methode einbinden können - sonst würde es keine Garantie geben, dass sich vector.size nicht ändert.

PS: Das eigentliche Leistungsproblem könnte die Verwendung von Vector sein, was seit Jahren eine ziemlich sinnlose Klasse ist. Bevorzugen Sie ArraysList.

3

Überprüfung in einer Schleife Byte-Code:

12: iload_3 
13: aload_2 
14: invokeinterface #4, 1   // InterfaceMethod java/util/List.size:()I 
19: if_icmpge  31 
22: iinc   1, 1 
25: iinc   3, 1 
28: goto   12 

es in einem variablen Byte-Code umsetzen:

10: aload_2 
11: invokeinterface #4, 1   // InterfaceMethod java/util/List.size:()I 
16: istore_3 
17: iconst_0 
18: istore  4 
20: iload   4 
22: iload_3 
23: if_icmpge  35 
26: iinc   1, 1 
29: iinc   4, 1 
32: goto   20 

Scheint, wie es sie jedes Mal, hat den Aufruf, also eigentlich in einer Variablen setzen ist schneller, ich würde mir darüber keine Sorgen machen. Seien Sie sich bewusst, dass ich ein Neuling im Byte-Code bin und ich möglicherweise komplett falsch liege.

+0

Der Bytecode ist zu 100% irrelevant, siehe [meine Antwort] (http://stackoverflow.com/a/40631555/581205). – maaartinus

1

Dies ist die Implementierung von size() int Arraylist-Klasse

/** 
* Returns the number of elements in this list. 
* 
* @return the number of elements in this list 
*/ 
public int size() { 
    return size; 
} 

In diesem Fall, da es in Objekt Eigenschaft, so dass sie nur ein Funktionsaufruf und gibt den Wert der Größe gespeichert wird (es nicht berechnen). Hier geht es nur darum, einen Funktionsaufruf zu verhindern.

Es ist definitiv sinnvoll, die Größe in einer Variablen zu speichern, wenn die Methode size() bei jedem Aufruf über die Liste zum Berechnen der Größe iteriert.

+0

Nein. Der Methodenaufruf wird immer dann angezeigt, wenn es darauf ankommt. Es kann Fälle geben, in denen das nicht passiert und ich freue mich darauf, einen echten Fall zu sehen. – maaartinus

1

Da size() ist eine Methode, wenn sie jedes Mal in der Schleife ausgewertet wird langsamer sein, als es einmal zu bewerten und in einer Variablen gespeichert werden. Das Problem besteht nicht darin, dass die Array-Größe erneut berechnet wird. es ist vielmehr der Aufwand, eine Funktion überhaupt aufzurufen. Dies wird die Leistung beeinträchtigen, unabhängig davon, was in der Methode enthalten ist (obwohl natürlich eine lange, langsame, komplizierte Funktion mehr Schaden anrichtet als ein einfacher Getter).

Ich war vorsichtig zu sagen „wenn“ es jedes Mal ausgewertet, da Compiler kann entscheiden, die Funktion aufrufen, um inline, die den Aufwand beseitigen würde, und die Schleife wird genauso schnell sein. Dies ist das Gleiche wie die for-each vs. generic for loop-Debatte. Wenn die for-each-Funktionsaufrufe nicht inline sind, ist es langsamer als eine allgemeine for-Schleife ohne Funktionsaufrufe.

Es gibt definitiv Fälle, in denen dies einen großen Unterschied in der Leistung machen kann, so dass es gut ist, kennt diese Feinheiten zu sein. Echtzeit-Signalverarbeitungsalgorithmen, die einen hohen Durchsatz aufweisen müssen, sind gute Beispiele für Programme, die auf unnötigen Overhead reagieren können. Zugegeben, diese werden normalerweise nicht in Java geschrieben, aber es ist gut, über diese Dinge Bescheid zu wissen.