Viele Java-Bücher beschreiben die switch
Aussage als schneller als die if else
Aussage. Aber ich habe nirgendwo herausgefunden, warum der Wechsel schneller ist als wenn .
Beispiel
Ich habe eine Situation, in der ich einen von zwei Artikeln auswählen muss. Ich kann beide verwenden
switch (item) {
case BREAD:
//eat Bread
break;
default:
//leave the restaurant
}
oder
if (item == BREAD) {
//eat Bread
} else {
//leave the restaurant
}
unter Berücksichtigung von item und BREAD ist ein konstanter int-Wert.
Was ist im obigen Beispiel schneller in Aktion und warum?
java
switch-statement
Jacob van Lingen
quelle
quelle
Antworten:
Weil es spezielle Bytecodes gibt, die in vielen Fällen eine effiziente Auswertung von switch-Anweisungen ermöglichen.
Wenn mit IF-Anweisungen implementiert, hätten Sie eine Prüfung, einen Sprung zur nächsten Klausel, eine Prüfung, einen Sprung zur nächsten Klausel und so weiter. Mit switch lädt die JVM den zu vergleichenden Wert und durchläuft die Wertetabelle, um eine Übereinstimmung zu finden, die in den meisten Fällen schneller ist.
quelle
switch
darf nicht in einentableswitch
Bytecode-Befehl übersetzt werden - es kann einlookupswitch
Befehl werden, der ähnlich wie ein if / else funktioniert. (ii) Selbst eintableswitch
Bytecode-Befehl kann abhängig von den Faktoren von der JIT zu einer Reihe von if / else kompiliert werden wie die Anzahl voncase
s.tableswitch
vsloopuswitch
: stackoverflow.com/questions/10287700/…Eine
switch
Anweisung ist nicht immer schneller als eineif
Anweisung. Es skaliert besser als eine lange Liste vonif-else
Anweisungen, daswitch
eine Suche basierend auf allen Werten durchgeführt werden kann. Für einen kurzen Zustand wird es jedoch nicht schneller und könnte langsamer sein.quelle
switch
wären seine 3 oder mehr Vorschläge klarer, wenn nicht schneller.Die aktuelle JVM verfügt über zwei Arten von Switch-Byte-Codes: LookupSwitch und TableSwitch.
Jeder Fall in einer switch-Anweisung hat einen ganzzahligen Offset. Wenn diese Offsets zusammenhängend sind (oder größtenteils zusammenhängend ohne große Lücken) (Fall 0: Fall 1: Fall 2 usw.), wird TableSwitch verwendet.
Wenn die Offsets mit großen Lücken verteilt sind (Fall 0: Fall 400: Fall 93748: usw.), wird LookupSwitch verwendet.
Kurz gesagt, der Unterschied besteht darin, dass TableSwitch in konstanter Zeit ausgeführt wird, da jedem Wert innerhalb des Bereichs möglicher Werte ein spezifischer Bytecode-Offset zugewiesen wird. Wenn Sie der Anweisung einen Versatz von 3 geben, muss sie 3 vorausspringen, um den richtigen Zweig zu finden.
Der Suchschalter verwendet eine binäre Suche, um den richtigen Codezweig zu finden. Dies läuft in O (log n) Zeit, was immer noch gut, aber nicht die beste ist.
Weitere Informationen hierzu finden Sie hier: Unterschied zwischen LookupSwitch und TableSwitch von JVM?
Verwenden Sie diesen Ansatz, um festzustellen, welcher am schnellsten ist: Wenn Sie drei oder mehr Fälle haben, deren Werte aufeinanderfolgend oder nahezu aufeinanderfolgend sind, verwenden Sie immer einen Schalter.
Wenn Sie 2 Fälle haben, verwenden Sie eine if-Anweisung.
In jeder anderen Situation ist der Wechsel höchstwahrscheinlich schneller, aber nicht garantiert, da die Binärsuche in LookupSwitch auf ein schlechtes Szenario stoßen könnte.
Beachten Sie außerdem, dass die JVM JIT-Optimierungen für if-Anweisungen ausführt, die versuchen, den heißesten Zweig zuerst im Code zu platzieren. Dies wird als "Verzweigungsvorhersage" bezeichnet. Weitere Informationen hierzu finden Sie hier: https://dzone.com/articles/branch-prediction-in-java
Ihre Erfahrungen können variieren. Ich weiß nicht, dass die JVM keine ähnliche Optimierung auf LookupSwitch ausführt, aber ich habe gelernt, den JIT-Optimierungen zu vertrauen und nicht zu versuchen, den Compiler zu überlisten.
quelle
switch
eine noch leistungsfähigere Sprachfunktion darstellen werden. Der Musterabgleich ermöglicht beispielsweise eine viel flüssigere und leistungsfähigereinstanceof
Suche. Ich denke jedoch, dass man davon ausgehen kann, dass für grundlegende Switch / If-Szenarien die von mir erwähnte Regel weiterhin gilt.Wenn Sie also vorhaben, viel Paketspeicher zu haben, sind die Kosten heutzutage nicht wirklich hoch und die Arrays sind ziemlich schnell. Sie können sich auch nicht auf eine switch-Anweisung verlassen, um automatisch eine Sprungtabelle zu generieren. Daher ist es einfacher, das Sprungtabellenszenario selbst zu generieren. Wie Sie im folgenden Beispiel sehen können, gehen wir von maximal 255 Paketen aus.
Um das folgende Ergebnis zu erhalten, brauchen Sie eine Abstraktion. Ich werde nicht erklären, wie das funktioniert. Hoffentlich haben Sie ein Verständnis dafür.
Ich habe dies aktualisiert, um die Paketgröße auf 255 festzulegen, wenn Sie mehr benötigen, als Sie für (id <0) || prüfen müssen (id> Länge).
Bearbeiten, da ich in C ++ häufig eine Sprungtabelle verwende, zeige ich jetzt ein Beispiel für eine Sprungtabelle für Funktionszeiger. Dies ist ein sehr allgemeines Beispiel, aber ich habe es ausgeführt und es funktioniert korrekt. Beachten Sie, dass Sie den Zeiger auf NULL setzen müssen. C ++ führt dies nicht automatisch wie in Java aus.
Ein weiterer Punkt, den ich ansprechen möchte, ist das berühmte Teilen und Erobern. Meine über 255 Array-Idee könnte also auf nicht mehr als 8 if-Anweisungen als Worst-Case-Szenario reduziert werden.
Dh aber denken Sie daran, es wird chaotisch und schwer, schnell zu verwalten, und mein anderer Ansatz ist im Allgemeinen besser, aber dies wird in Fällen verwendet, in denen Arrays es einfach nicht schneiden. Sie müssen Ihren Anwendungsfall herausfinden und wann jede Situation am besten funktioniert. Genauso wie Sie keinen dieser Ansätze verwenden möchten, wenn Sie nur wenige Überprüfungen haben.
quelle
0 <= id < packets.length
und sicherstellenpackets[id]!=null
und dann tunpackets[id].execute(data)
?Auf Bytecode-Ebene wird die Subjektvariable nur einmal von einer Speicheradresse in der von Runtime geladenen strukturierten .class-Datei in das Prozessorregister geladen, und dies ist in einer switch-Anweisung enthalten. Während in einer if-Anweisung eine andere jvm-Anweisung von Ihrem Code kompilierenden DE erzeugt wird, muss jede Variable in Register geladen werden, obwohl dieselbe Variable wie in der nächsten vorangegangenen if-Anweisung verwendet wird. Wenn Sie sich mit Codierung in Assemblersprache auskennen, ist dies an der Tagesordnung. Obwohl Java-kompilierte Coxen kein Bytecode oder direkter Maschinencode sind, ist das bedingte Konzept hiervon immer noch konsistent. Nun, ich habe versucht, tiefere technische Aspekte beim Erklären zu vermeiden. Ich hoffe, ich hatte das Konzept klar und entmystifiziert. Danke dir.
quelle