Gibt es auf die eine oder andere Weise Leistungsvorteile? Ist es Compiler / VM-spezifisch? Ich benutze Hotspot.
quelle
Gibt es auf die eine oder andere Weise Leistungsvorteile? Ist es Compiler / VM-spezifisch? Ich benutze Hotspot.
Erstens: Sie sollten nicht die Wahl zwischen statisch und nicht statisch auf der Grundlage der Leistung treffen.
Zweitens: In der Praxis macht es keinen Unterschied. Hotspot kann sich für eine Optimierung entscheiden, die statische Aufrufe für eine Methode schneller und nicht statische Aufrufe für eine andere Methode beschleunigt.
Drittens: Ein Großteil des Mythos um statische und nicht statische Elemente basiert entweder auf sehr alten JVMs (die nicht annähernd der von Hotspot vorgenommenen Optimierung entsprachen) oder auf einigen bekannten Trivia zu C ++ (bei denen ein dynamischer Aufruf einen weiteren Speicherzugriff verwendet) als ein statischer Anruf).
Vier Jahre später...
Okay, in der Hoffnung, diese Frage ein für alle Mal zu klären, habe ich einen Benchmark geschrieben, der zeigt, wie die verschiedenen Arten von Anrufen (virtuell, nicht virtuell, statisch) miteinander verglichen werden.
Ich habe es auf ideone ausgeführt , und das habe ich bekommen:
(Eine größere Anzahl von Iterationen ist besser.)
Wie erwartet sind virtuelle Methodenaufrufe die langsamsten, nicht virtuelle Methodenaufrufe sind schneller und statische Methodenaufrufe sind noch schneller.
Was ich nicht erwartet hatte, war, dass die Unterschiede so ausgeprägt waren: Es wurde gemessen, dass virtuelle Methodenaufrufe mit weniger als der Hälfte der Geschwindigkeit nicht virtueller Methodenaufrufe ausgeführt wurden, die wiederum ganze 15% langsamer als statische Aufrufe ausgeführt wurden. Das zeigen diese Messungen; Die tatsächlichen Unterschiede müssen in der Tat etwas ausgeprägter sein, da mein Benchmarking-Code für jeden virtuellen, nicht virtuellen und statischen Methodenaufruf einen zusätzlichen konstanten Aufwand hat, eine ganzzahlige Variable zu inkrementieren, eine boolesche Variable zu überprüfen und eine Schleife durchzuführen, wenn dies nicht der Fall ist.
Ich nehme an, die Ergebnisse variieren von CPU zu CPU und von JVM zu JVM. Probieren Sie es also aus und sehen Sie, was Sie erhalten:
Es ist anzumerken, dass dieser Leistungsunterschied nur für Code gilt, der nichts anderes tut, als parameterlose Methoden aufzurufen. Unabhängig davon, welchen anderen Code Sie zwischen den Aufrufen haben, werden die Unterschiede verringert, und dies schließt die Parameterübergabe ein. Tatsächlich wird der Unterschied von 15% zwischen statischen und nicht virtuellen Aufrufen wahrscheinlich vollständig durch die Tatsache erklärt , dass der
this
Zeiger nicht an die statische Methode übergeben werden muss. Es würde also nur eine relativ kleine Menge Code erfordern, um zwischen den Aufrufen triviale Dinge zu erledigen, damit der Unterschied zwischen verschiedenen Arten von Aufrufen so weit verwässert wird, dass keinerlei Nettoauswirkungen mehr auftreten.Auch virtuelle Methodenaufrufe existieren aus einem bestimmten Grund. Sie haben einen Zweck zu erfüllen und werden mit den effizientesten Mitteln implementiert, die von der zugrunde liegenden Hardware bereitgestellt werden. (Der CPU-Befehlssatz.) Wenn Sie in Ihrem Wunsch, sie durch Ersetzen durch nicht virtuelle oder statische Aufrufe zu beseitigen, bis zu einem Jota zusätzlichen Codes hinzufügen müssen, um ihre Funktionalität zu emulieren, ist Ihr resultierender Netto-Overhead gebunden nicht weniger sein, sondern mehr. Möglicherweise viel, viel, unergründlich viel, mehr.
quelle
VirtualTest | 488846733 -- NonVirtualTest | 480530022 -- StaticTest | 484353198
bei meiner OpenJDK-Installation. FTR: Das stimmt sogar, wenn ich denfinal
Modifikator entferne . Übrigens. Ich musste dasterminate
Feld machenvolatile
, sonst wurde der Test nicht beendet.VirtualTest | 12451872 -- NonVirtualTest | 12089542 -- StaticTest | 8181170
. Nicht nur, dass OpenJDK auf meinem Notebook 40-mal mehr Iterationen ausführt, der statische Test hat immer einen um 30% geringeren Durchsatz. Dies könnte ein ART-spezifisches Phänomen sein, da ich auf einem Android 4.4-Tablet ein erwartetes ErgebnisVirtualTest | 138183740 -- NonVirtualTest | 142268636 -- StaticTest | 161388933
Nun, statische Aufrufe können nicht überschrieben werden (sind also immer Kandidaten für Inlining) und erfordern keine Nichtigkeitsprüfungen. HotSpot führt eine Reihe cooler Optimierungen durch, zum Beispiel Methoden, die diese Vorteile möglicherweise zunichte machen, aber sie sind mögliche Gründe, warum ein statischer Aufruf schneller sein kann.
Dies sollte sich jedoch nicht auf Ihr Design auswirken - Code auf die lesbarste und natürlichste Weise - und sich nur dann um diese Art der Mikrooptimierung kümmern, wenn Sie nur einen Grund haben (den Sie so gut wie nie werden).
quelle
Es ist compiler- / VM-spezifisch.
Daher lohnt es sich wahrscheinlich nicht, sich darum zu kümmern, es sei denn, Sie haben dies als ein wirklich kritisches Leistungsproblem in Ihrer Anwendung identifiziert. Vorzeitige Optimierung ist die Wurzel allen Übels usw.
Ich habe jedoch gesehen, dass diese Optimierung in der folgenden Situation zu einer erheblichen Leistungssteigerung führt:
Wenn das oben Gesagte auf Sie zutrifft, kann es sich lohnen, es zu testen.
Es gibt noch einen weiteren guten (und möglicherweise sogar noch wichtigeren!) Grund, eine statische Methode zu verwenden: Wenn die Methode tatsächlich eine statische Semantik aufweist (dh logisch nicht mit einer bestimmten Instanz der Klasse verbunden ist), ist es sinnvoll, sie statisch zu machen diese Tatsache zu reflektieren. Erfahrene Java-Programmierer werden dann den statischen Modifikator bemerken und sofort denken "aha! Diese Methode ist statisch, benötigt also keine Instanz und manipuliert vermutlich nicht den instanzspezifischen Status". Sie haben also die statische Natur der Methode effektiv kommuniziert ...
quelle
Wie in früheren Postern bereits erwähnt: Dies scheint eine vorzeitige Optimierung zu sein.
Es gibt jedoch einen Unterschied (ein Teil der Tatsache, dass nicht statische Aufrufe einen zusätzlichen Druck eines Angerufenen auf den Operandenstapel erfordern):
Da statische Methoden nicht überschrieben werden können, werden zur Laufzeit keine virtuellen Suchvorgänge für einen statischen Methodenaufruf durchgeführt. Dies kann unter Umständen zu einem beobachtbaren Unterschied führen.
Die Differenz auf einer Byte-Code - Ebene ist , daß ein nicht statischer Methodenaufruf wird durch getan
INVOKEVIRTUAL
,INVOKEINTERFACE
oderINVOKESPECIAL
während eines statischen Methodenaufruf durch erfolgtINVOKESTATIC
.quelle
invokespecial
da sie nicht virtuell ist.Es ist unglaublich unwahrscheinlich, dass ein Unterschied in der Leistung von statischen und nicht statischen Aufrufen einen Unterschied in Ihrer Anwendung bewirkt. Denken Sie daran, dass "vorzeitige Optimierung die Wurzel allen Übels ist".
quelle
7 Jahre später ...
Ich habe kein großes Vertrauen in die Ergebnisse, die Mike Nakis gefunden hat, weil sie einige häufig auftretende Probleme im Zusammenhang mit Hotspot-Optimierungen nicht ansprechen. Ich habe Benchmarks mit JMH instrumentiert und festgestellt, dass der Overhead einer Instanzmethode auf meinem Computer etwa 0,75% gegenüber einem statischen Aufruf beträgt. Angesichts des geringen Overheads denke ich, dass dies außer bei den latenzempfindlichsten Vorgängen wohl nicht das größte Problem bei einem Anwendungsdesign ist. Die zusammenfassenden Ergebnisse meines JMH-Benchmarks lauten wie folgt:
Sie können den Code hier auf Github ansehen.
https://github.com/nfisher/svsi
Der Benchmark selbst ist ziemlich einfach, zielt jedoch darauf ab, die Eliminierung von totem Code und das ständige Falten zu minimieren. Es gibt möglicherweise andere Optimierungen, die ich übersehen habe, und diese Ergebnisse variieren wahrscheinlich je nach JVM-Version und Betriebssystem.
quelle
ops/s
Mikrooptimierung für andere Metriken als hauptsächlich in einer ART-Umgebung haben könnte (z. B. Speichernutzung, reduzierte .oat-Dateigröße usw.). Kennen Sie relativ einfache Tools / Methoden, mit denen Sie versuchen könnten, diese anderen Metriken zu vergleichen?Für die Entscheidung, ob eine Methode statisch sein soll, sollte der Leistungsaspekt irrelevant sein. Wenn Sie ein Leistungsproblem haben, können Sie den Tag nicht retten, wenn Sie viele Methoden statisch machen. Allerdings sind statische Methoden mit ziemlicher Sicherheit nicht langsamer als jede Instanzmethode, in den meisten Fällen geringfügig schneller :
1.) Statische Methoden sind nicht polymorph, daher muss die JVM weniger Entscheidungen treffen, um den tatsächlich auszuführenden Code zu finden. Dies ist ein strittiger Punkt im Zeitalter von Hotspot, da Hotspot Instanzmethodenaufrufe mit nur einer Implementierungssite optimiert, sodass sie dieselbe Leistung erbringen.
2.) Ein weiterer subtiler Unterschied besteht darin, dass statische Methoden offensichtlich keine "diese" Referenz haben. Dies führt zu einem Stapelrahmen, der einen Steckplatz kleiner als der einer Instanzmethode mit derselben Signatur und demselben Text ist ("dies" wird in Steckplatz 0 der lokalen Variablen auf Bytecode-Ebene eingefügt, während für statische Methoden Steckplatz 0 für den ersten verwendet wird Parameter der Methode).
quelle
Es kann einen Unterschied geben, und es kann für einen bestimmten Code in beide Richtungen gehen, und es kann sich sogar mit einer geringfügigen Veröffentlichung der JVM ändern.
Dies ist definitiv Teil der 97% der kleinen Wirkungsgrade, die Sie vergessen sollten .
quelle
TableView
nach Millionen von Datensätzen.Theoretisch günstiger.
Die statische Initialisierung wird auch dann durchgeführt, wenn Sie eine Instanz des Objekts erstellen, während statische Methoden keine Initialisierung durchführen, die normalerweise in einem Konstruktor durchgeführt wird.
Ich habe dies jedoch nicht getestet.
quelle
Wie Jon bemerkt, können statische Methoden nicht überschrieben werden. Das einfache Aufrufen einer statischen Methode kann daher bei einer ausreichend naiven Java-Laufzeit schneller sein als das Aufrufen einer Instanzmethode.
Aber selbst wenn Sie an dem Punkt angelangt sind, an dem Sie Ihr Design durcheinander bringen möchten, um ein paar Nanosekunden zu sparen, wirft dies nur eine andere Frage auf: Brauchen Sie eine Methode, die sich selbst überschreibt? Wenn Sie Ihren Code ändern, um eine Instanzmethode in eine statische Methode umzuwandeln, um hier und da eine Nanosekunde zu sparen, und sich dann umdrehen und Ihren eigenen Dispatcher implementieren, ist Ihr Dispatcher mit ziemlicher Sicherheit weniger effizient als der erstellte bereits in Ihre Java-Laufzeit.
quelle
Ich möchte den anderen großartigen Antworten hier hinzufügen, dass es auch von Ihrem Fluss abhängt, zum Beispiel:
Achten Sie darauf, dass Sie bei jedem Aufruf ein neues MyRowMapper-Objekt erstellen.
Stattdessen schlage ich vor, hier ein statisches Feld zu verwenden.
quelle