2 Stimmen

Effizienz von Java-Code mit primitiven Typen

Ich möchte fragen, welcher Teil des Codes in Java effizienter ist? Code 1:

void f()
{
 for(int i = 0 ; i < 99999;i++)
 {
  for(int j = 0 ; j < 99999;j++)
  {
   //Some operations
  }
 }

}

Code 2:

void f()
{
 int i,j;
 for(i = 0 ; i < 99999;i++)
 {
  for(j = 0 ; j < 99999;j++)
  {
   //Some operations
  }
 }

}

Mein Lehrer sagte, dass die zweite besser ist, aber ich kann dieser Meinung nicht zustimmen.

31voto

polygenelubricants Punkte 362173

IT. TUT. MACHT. UNTERSCHIED.

Hören Sie mit der Mikro-Optimierung auf. Diese kleinen Tricks machen die Programme nicht viel schneller.

Konzentrieren Sie sich auf Optimierungen des Gesamtbildes und das Schreiben von lesbarem Code.

Deklarieren Sie Variablen dort, wo sie sinnvoll sind und wo es hilft, die Semantik des gesamten Codes im größeren Zusammenhang zu verstehen, nicht weil Sie denken es ist schneller an einem Ort als an einem anderen.

17voto

Anonymoose Punkte 5282

Ich würde die erste Variante der zweiten vorziehen, da sie die Schleifenvariablen aus dem restlichen Code der Methode heraushält. Da sie außerhalb der Schleife nicht sichtbar sind, kann man später nicht versehentlich auf sie verweisen.

Auch die anderen Antworten sind richtig: nicht über diese Art von Dingen für die Leistung zu sorgen. Aber tun aus Gründen der Lesbarkeit des Codes und um die Absicht des Programmierers an die nächste Person weiterzugeben, darüber nachdenken. Dies ist viel wichtiger als Mikro-Optimierung Bedenken.

Nun, das ist auf der Ebene der Java-Sprache (wie in Java Language Specification). Auf der Ebene der Java Virtual Machine macht es absolut keinen Unterschied, welche dieser beiden Sprachen Sie verwenden. Die Locals werden auf genau dieselbe Weise zugewiesen.

Wenn Sie sich nicht sicher sind, können Sie es jederzeit kompilieren und sehen, was passiert. Erstellen wir zwei Klassen, f1 und f2, für die beiden Versionen:

$ cat f1.java
public class f1 {
  void f() {
    for(int i = 0 ; i < 99999;i++) {
      for(int j = 0 ; j < 99999;j++) {
      }
    }
  }
}

$ cat f2.java
public class f2 {
  void f() {
    int i, j;
    for(i = 0 ; i < 99999;i++) {
      for(j = 0 ; j < 99999;j++) {
      }
    }
  }
}

Kompilieren Sie sie:

$ javac f1.java
$ javac f2.java

Und dekompilieren Sie sie:

$ javap -c f1 > f1decomp
$ javap -c f2 > f2decomp

Und vergleichen Sie sie:

$ diff f1decomp f2decomp
1,3c1,3
< Compiled from "f1.java"
< public class f1 extends java.lang.Object{
< public f1();
---
> Compiled from "f2.java"
> public class f2 extends java.lang.Object{
> public f2();

Es gibt absolut keinen Unterschied im Bytecode.

13voto

Stephen C Punkte 665668

Hüten Sie sich vor den Gefahren des Micro-Benchmarking!!!

Ich nahm den Code, wickelte eine Methode um die Außenseite, und führte das 10 Mal in einer Schleife. Ergebnisse:

50, 3, 
3, 0, 
0, 0, 
0, 0, 
....

Ohne tatsächlichen Code in den Schleifen sind die Compiler in der Lage, herauszufinden, dass die Schleifen keine nützliche Arbeit leisten, und sie vollständig zu optimieren. In Anbetracht der gemessenen Leistung vermute ich, dass diese Optimierung möglicherweise von javac .

Lektion 1: Compiler optimieren oft Code weg, der nutzlose "Arbeit" macht. Je intelligenter der Compiler ist, desto wahrscheinlicher ist es, dass so etwas passiert. Wenn Sie dies bei der Programmierung nicht berücksichtigen, kann ein Benchmark bedeutungslos sein.

Also habe ich die folgende einfache Berechnung in beide Schleifen eingefügt if (i < 2 * j) longK++; und ließ die Testmethode den endgültigen Wert von longK . Ergebnisse:

32267, 33382,
34542, 30136,
12893, 12900,
12897, 12889,
12904, 12891,
12880, 12891,
....

Wir haben offensichtlich verhindert, dass die Compiler die Schleife wegoptimieren. Aber jetzt sehen wir die Auswirkungen des JVM-Warmups in (in diesem Fall) den ersten beiden Paaren von Schleifeniterationen. Die ersten beiden Iterationspaare (ein Methodenaufruf) werden wahrscheinlich im reinen Interpretationsmodus ausgeführt. Und es sieht so aus, als würde die dritte Iteration tatsächlich parallel zum JIT laufen. Beim dritten Iterationspaar handelt es sich höchstwahrscheinlich um reinen nativen Code. Und von da an ist der Unterschied zwischen dem Timing der beiden Schleifenversionen nur noch Rauschen.

Lektion 2: Berücksichtigen Sie immer die Auswirkungen der JVM-Aufwärmphase. Dies kann die Benchmark-Ergebnisse sowohl auf der Mikro- als auch auf der Makroebene erheblich verzerren.

Fazit: Sobald die JVM aufgewärmt ist, gibt es keinen messbaren Unterschied mehr zwischen den beiden Versionen der Schleife.

6voto

cletus Punkte 596503

Die zweite ist schlimmer.

Warum? Weil die Schleifenvariable außerhalb der Schleife zugewiesen ist. i y j hat einen Wert, wenn die Schleife beendet ist. Das ist in der Regel nicht das, was Sie wollen. Die erste gibt den Schleifenvariablen einen Geltungsbereich, so dass sie nur innerhalb der Schleife sichtbar ist.

2voto

Marcelo Cantos Punkte 173498

Ich würde vermuten, dass es bei jeder halbwegs anständigen JVM-Implementierung absolut keinen Unterschied in der Effizienz gibt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X