2016-04-11 7 views
11

Ich habe eine for-Schleife, die 4096 Mal läuft und es sollte so schnell wie möglich sein. Leistung ist hier sehr wichtig. Derzeit verwende ich Getter-Methoden innerhalb der Schleife, die nur Werte oder Objekte aus Feldern zurückgeben, die sich während der Schleife nicht ändern.Java verwenden Getter in for-Schleife oder eine lokale Variable erstellen?

Beispiel:

for (;;) { 
    doSomething(example.getValue()); 
} 

Gibt es einen Overhead-Getter verwendet? Ist es schneller wie folgt?

Beispiel:

Object object = example.getValue(); 
for (;;) { 
    doSomething(object); 
} 

Wenn ja, gilt das auch für wie example.value öffentliche Felder zugreifen?

Edit: Ich System.out.println() nicht in der Schleife verwenden.

Bearbeiten: Einige Felder sind nicht final. Keine Felder sind volatile und keine Methode (Getter) ist synchronized.

+0

Das Speichern der Ausgabe in einer lokalen Zeichenfolge wäre schneller als ein Getter oder eine Objektereferenz. Aber das wird in "teensy" – Jamie

Antwort

7

Als Rogério answered, bekommen die Objektreferenz außerhalb der Schleife (Object object = example.getValue();) wird wahrscheinlich schneller sein (oder zumindest nie langsamer) als die Getter innerhalb der Schleife aufrufen, weil

  • im „worst“ -Fall example.getValue() könnte tatsächlich einige sehr rechenintensive Sachen im Hintergrund tun, obwohl diese getter methods angeblich "trivial" sein sollen. Indem Sie eine Referenz einmal zuweisen und erneut verwenden, führen Sie diese teure Berechnung nur einmal durch.
  • in der "besten" Fall, example.getValue() macht etwas trivial wie return value; und so die Zuordnung innerhalb der Schleife wäre nicht teurer als außerhalb der Schleife nach dem JIT-Compiler inlines the code.

jedoch wichtiger ist der Unterschied in der Semantik zwischen den beiden und ihren möglichen Auswirkungen in einem Multi-Threaded-Umgebung: Wenn der Zustand des Objekts example Änderungen in einer Weise, die example.getValue() zurückzukehren Verweise auf verschiedene Objekte verursacht, Es ist möglich, dass die Methode doSomething(Object object) bei jeder Iteration tatsächlich auf einer anderen Instanz von Object operiert, indem sie direkt doSomething(example.getValue()); aufruft. Auf der anderen Seite, durch einen Getter außerhalb der Schleife und rufen eine Referenz auf die Instanz zurückgegeben (Object object = example.getValue();) Einstellung wird doSomething(object); arbeiten auf objectn Zeiten für n Iterationen.

Dieser Unterschied in der Semantik kann dazu führen, dass sich das Verhalten in einer Umgebung mit mehreren Threads grundlegend von dem in einer Single-Thread-Umgebung unterscheidet. Außerdem muss dies kein tatsächliches "In-Memory" Multithreading-Problem sein: Wenn example.getValue() z.B. Datenbank-/HDD-/Netzwerkressourcen ist es möglich, dass sich diese Daten während der Ausführung der Schleife ändern, wodurch es möglich wird, dass ein anderes Objekt zurückgegeben wird, selbst wenn die Java-Anwendung selbst single-threaded ist.Aus diesem Grund sollten Sie überlegen, was Sie eigentlich mit Ihrer Schleife erreichen möchten, und dann die Option auswählen, die das beabsichtigte Verhalten am besten widerspiegelt.

0

Wenn Sie es so schnell wie möglich laufen, sollten Sie nicht System.out.println in kritischen Abschnitten verwenden.

Bezüglich Getter: Es ist leichter Overhead für Getter verwendet, aber man sollte darüber nicht stören. Java hat Getter- und Setter-Optimierung im JIT-Compiler. Also werden sie irgendwann durch nativen Code ersetzt.

+1

gemessen Ich verwende 'System.out.println()' nicht in meinem Code. Dies ist nur ein schlechtes Beispiel. – stonar96

4

Es hängt vom Getter ab.

Wenn es ein einfaches Getter ist, wird der JIT in-line es zu einem direkten Feldzugriff wie auch immer, so wird es keinen messbarer Unterschied. Vom Standpunkt des Stils aus, verwenden Sie den Getter - es ist weniger Code.

Wenn der Getter auf ein volatile Feld zugreift, gibt es einen zusätzlichen Speicherzugriffstreffer, da der Wert nicht im Register gespeichert werden kann, der Treffer ist jedoch sehr klein.

Wenn der Getter synchronized ist, wird die Verwendung einer lokalen Variablen messbar schneller sein, da Sperren nicht bei jedem Aufruf erhalten und freigegeben werden müssen, aber der Schleifencode wird den potenziell veralteten Wert des Felds zu der Zeit verwenden Getter wurde gerufen.

+0

Vielen Dank für Ihre Antwort. Der gesamte Code ist single-threaded. Daher gibt es keine flüchtigen Felder oder synchronisierte Methoden. Die Getter sind einfache Getter. – stonar96

4

Sie sollen einen lokalen Variable außerhalb der Schleife bevorzugen, aus den folgenden Gründen:

  1. Es neigt den Code leichter zu lesen/verstehen, durch verschachtelte Verfahren vermieden werden Anrufe wie doSomething(example.getValue()) in einer einzigen Codezeile und indem dem Code ermöglicht wird, dem von der Getter-Methode zurückgegebenen Wert einen besseren, spezifischeren Namen zu geben.
  2. Nicht alle Getter-Methoden sind trivial (dh sie manchmal etwas potenziell teure Arbeit tun), aber die Entwickler oft nicht bemerkt, ein bestimmtes Verfahren unter der Annahme, trivial und billig ist, wenn es wirklich nicht. In solchen Fällen kann der Code erhebliche Leistungseinbußen hinnehmen, ohne dass der Entwickler dies bemerkt. Extraktion in eine lokale Variable neigt dazu, dieses Problem zu vermeiden.
1

Es ist sehr einfach über die Leistung zu kümmern viel mehr als notwendig ist. Ich kenne das Gefühl.Einige Dinge zu beachten:

  1. 4096 ist nicht viel, also, wenn dies in einer extrem kurzen Zeit abgeschlossen ist, mach dir keine Sorgen über die Leistung so sehr.
  2. Wenn in dieser Schleife noch etwas Fernes teuer ist, spielt der Getter keine Rolle.
  3. Vorzeitige Optimierung ist die Wurzel allen Übels. Konzentrieren Sie sich darauf, Ihren Code zuerst korrekt und klar zu machen. Dann messen und profilieren Sie es und verkleinern Sie das teuerste Ding und kümmern Sie sich darum. Verbessere den tatsächlichen Algorithmus wenn möglich.

In Bezug auf Ihre Frage, ich weiß nicht genau, was die JIT tut, aber es sei denn, es mit Sicherheit nachweisen kann, dass example.getValue() oder example.value nicht in der Schleife ändert (was es sei denn, das Feld final zu tun ist hart und der Getter ist trivial), dann gibt es logisch keine Möglichkeit, es zu vermeiden, den Getter wiederholt in der früheren Probe aufzurufen, da dies riskieren würde, das Verhalten des Programms zu ändern. Die wiederholten Anrufe sind mit Sicherheit eine gewisse Menge an zusätzlicher Arbeit.

Nachdem das alles gesagt, erstellen Sie die lokale Variable außerhalb der Schleife, ob es schneller ist oder nicht, weil es klarer ist. Vielleicht überrascht dich das, aber guter Code ist nicht immer der kürzeste. Absichtserklärung und andere Informationen sind äußerst wichtig. In diesem Fall macht die lokale Variable außerhalb der Schleife jedem, der den Code liest, klar, dass sich das Argument doSomething nicht ändert (besonders wenn Sie es endgültig machen), was nützlich ist, um es zu wissen. Andernfalls müssen sie möglicherweise etwas mehr graben, um sicherzustellen, dass sie wissen, wie sich das Programm verhält.

+0

"* dann gibt es logischerweise keine Möglichkeit, es zu vermeiden, den Getter wiederholt aufzurufen *" => die Getter-Methode kann immer noch inline sein, egal was es tut ... – assylias

+0

@assylias ja, aber das ist noch etwas Arbeit, sogar wenn es nur den Wert eines Feldes überprüft. –