Switch-Anweisungen sind aufgrund von Compiler-Optimierungen in der Regel schneller als gleichwertige if-else-if-Anweisungen (wie z. B. in diesem Artikel beschrieben ).
Wie funktioniert diese Optimierung tatsächlich? Hat jemand eine gute Erklärung?
Switch-Anweisungen sind aufgrund von Compiler-Optimierungen in der Regel schneller als gleichwertige if-else-if-Anweisungen (wie z. B. in diesem Artikel beschrieben ).
Wie funktioniert diese Optimierung tatsächlich? Hat jemand eine gute Erklärung?
Antworten:
Der Compiler kann gegebenenfalls Sprungtabellen erstellen. Wenn Sie beispielsweise den Reflektor verwenden, um den erzeugten Code zu betrachten, werden Sie feststellen, dass der Compiler bei großen Schaltern von Zeichenfolgen tatsächlich Code generiert, der eine Hash-Tabelle verwendet, um diese zu versenden. Die Hash-Tabelle verwendet die Zeichenfolgen als Schlüssel und delegiert sie case
als Werte an die Codes.
Dies hat eine asymptotisch bessere Laufzeit als viele verkettete if
Tests und ist sogar für relativ wenige Saiten schneller.
Dies ist eine leichte Vereinfachung, wie dies normalerweise bei jedem modernen Compiler der if..else if ..
Fall ist , der auf eine Sequenz stößt , die von einer Person trivial in eine switch-Anweisung konvertiert werden könnte. Aber nur um zusätzlichen Spaß zu machen, ist der Compiler nicht durch die Syntax eingeschränkt, sondern kann intern "switch" -ähnliche Anweisungen generieren, die eine Mischung aus Bereichen, einzelnen Zielen usw. enthalten - und dies kann (und tut) dies sowohl für switch als auch für if. .else Aussagen.
Wie auch immer, eine Erweiterung von Konrads Antwort ist, dass der Compiler möglicherweise eine Sprungtabelle generiert, aber das ist nicht unbedingt garantiert (noch wünschenswert). Aus einer Vielzahl von Gründen tun Sprungtabellen den Zweigprädiktoren auf modernen Prozessoren schlechte Dinge, und die Tabellen selbst tun schlechte Dinge, um das Verhalten zwischenzuspeichern, z.
switch(a) { case 0: ...; break; case 1: ...; break; }
Wenn ein Compiler tatsächlich eine Sprungtabelle dafür generieren würde, wäre es wahrscheinlich langsamer als der alternative if..else if..
Stilcode, da die Sprungtabelle die Verzweigungsvorhersage besiegt.
Die No-Match-Statistiken sind möglicherweise nicht gut.
Wenn Sie die Quelle tatsächlich herunterladen, ist bekannt, dass die Werte für keine Übereinstimmung 21 sind, sowohl im if- als auch im switch-Fall. Ein Compiler sollte in der Lage sein, zu abstrahieren und zu wissen, welche Anweisung jederzeit ausgeführt werden sollte, und eine CPU sollte in der Lage sein, Vorhersagen richtig zu verzweigen.
Der interessantere Fall ist, wenn meiner Meinung nach nicht jeder Fall bricht, aber das war möglicherweise nicht der Umfang des Experiments.
Switch- / Case-Anweisungen sind in der Regel schneller und 1-stufig, aber wenn Sie mit 2 oder mehr beginnen, dauern Switch- / Case-Anweisungen 2-3 Mal so lange wie verschachtelte if / else-Anweisungen.
Dieser Artikel enthält einige Geschwindigkeitsvergleiche, in denen die Geschwindigkeitsunterschiede hervorgehoben werden, wenn solche Anweisungen verschachtelt sind.
Beispiel: Code gemäß den Tests lautet wie folgt:
if (x % 3 == 0)
if (y % 3 == 0)
total += 3;
else if (y % 3 == 1)
total += 2;
else if (y % 3 == 2)
total += 1;
else
total += 0;
else if (x % 3 == 1)
if (y % 3 == 0)
total += 3;
else if (y % 3 == 1)
total += 2;
else if (y % 3 == 2)
total += 1;
else
total += 0;
else if (x % 3 == 2)
if (y % 3 == 0)
total += 3;
else if (y % 3 == 1)
total += 2;
else if (y % 3 == 2)
total += 1;
else
total += 0;
else
if (y % 3 == 0)
total += 3;
else if (y % 3 == 1)
total += 2;
else if (y % 3 == 2)
total += 1;
else
total += 0;
in der Hälfte der Zeit beendet, die die entsprechende switch / case-Anweisung zum Ausführen benötigt hat:
switch (x % 3)
{
case 0:
switch (y % 3)
{
case 0: total += 3;
break;
case 1: total += 2;
break;
case 2: total += 1;
break;
default: total += 0;
break;
}
break;
case 1:
switch (y % 3)
{
case 0: total += 3;
break;
case 1: total += 2;
break;
case 2: total += 1;
break;
default: total += 0;
break;
}
break;
case 2:
switch (y % 3)
{
case 0: total += 3;
break;
case 1: total += 2;
break;
case 2: total += 1;
break;
default: total += 0;
break;
}
break;
default:
switch (y % 3)
{
case 0: total += 3;
break;
case 1: total += 2;
break;
case 2: total += 1;
break;
default: total += 0;
break;
}
break;
}
Ja, es ist ein rudimentäres Beispiel, aber es veranschaulicht den Punkt.
Eine Schlussfolgerung könnte also sein, switch / case für einfache Typen zu verwenden, die nur eine Ebene tief sind, aber für komplexere Vergleiche und mehrere verschachtelte Ebenen die klassischen if / else-Konstrukte verwenden?
Der einzige Vorteil des If-Over-Falls besteht darin, dass die Häufigkeit des Auftretens des ersten Falls merklich zunimmt.
Ich bin mir nicht sicher, wo genau der Schwellenwert liegt, aber ich verwende die Groß- und Kleinschreibung, es sei denn, der erste "fast immer" besteht den ersten Test.