[VC++’08]Unterschied zwischen if/else-if und switch
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
[VC++’08]Unterschied zwischen if/else-if und switch
Hi,
Hat es irgendeinen besonderen, mir unbekannten – z.B. in der Spezifikation festgelegten – Grund, dass Visual C++ switch-Statements anders behandelt als gleichwertige if/else-if-Statements, oder ist das bloß Schlampigkeit?
Speziell geht es darum, dass switches auf höchster Optimierungsstufe fast immer zu Lookup-Tables oder Binärbäumen umgewandelt werden und damit recht fix sind, während gleichwertige if/else-if-Statements unoptimiert bleiben und dann (bei entsprechend vielen Möglichkeiten) viel mehr Mnemonics produzieren.
Gruß, Ky
Hat es irgendeinen besonderen, mir unbekannten – z.B. in der Spezifikation festgelegten – Grund, dass Visual C++ switch-Statements anders behandelt als gleichwertige if/else-if-Statements, oder ist das bloß Schlampigkeit?
Speziell geht es darum, dass switches auf höchster Optimierungsstufe fast immer zu Lookup-Tables oder Binärbäumen umgewandelt werden und damit recht fix sind, während gleichwertige if/else-if-Statements unoptimiert bleiben und dann (bei entsprechend vielen Möglichkeiten) viel mehr Mnemonics produzieren.
Gruß, Ky
- Aramis
- Moderator
- Beiträge: 1458
- Registriert: 25.02.2009, 19:50
- Echter Name: Alexander Gessler
- Wohnort: 2016
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Wohl eher 'Schlampigkeit', wenn man es so nennen kann.
switch'es sind vom Sprachstandard ja - neben der verringerten Schreibarbeit im Vergleich zur äquivalenten if/else-Bauweise -bereits als Hinweis an den Optimizer vorgesehen. Ich denke nicht, dass es die Aufgabe des Optimizers ist Dinge zu optimieren, die man bei klarem Kopf sowieso nicht machen würde, z.B. Fensternachrichten mit gigantischen if/else-Blöcken behandeln ...
Mag auch sein dass die einschlägigen Benchmarks keine Stellen haben, an denen diese Optimierung nützlich wäre ... :D
switch'es sind vom Sprachstandard ja - neben der verringerten Schreibarbeit im Vergleich zur äquivalenten if/else-Bauweise -bereits als Hinweis an den Optimizer vorgesehen. Ich denke nicht, dass es die Aufgabe des Optimizers ist Dinge zu optimieren, die man bei klarem Kopf sowieso nicht machen würde, z.B. Fensternachrichten mit gigantischen if/else-Blöcken behandeln ...
Mag auch sein dass die einschlägigen Benchmarks keine Stellen haben, an denen diese Optimierung nützlich wäre ... :D
- kimmi
- Moderator
- Beiträge: 1405
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Vielleicht wollten die Autoren auch einfach vermeiden, daß irgendwelche Optimierungen in den id / else if / else Blöcken böse Side-Effects auslösen. Schließlich müßte man dann die Optimierung in irgendeiner Form mit der Semantik des Ausdrucks koppeln, was auch nicht gerade der weisheits letzter Schluß ist.
Gruß Kimmi
Gruß Kimmi
- Lord Delvin
- Establishment
- Beiträge: 597
- Registriert: 05.07.2003, 11:17
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Der Grund wird wohl sein, dass switch statesments in C++ für gleichwertige Auswahl und (verschachtelte)if/else blöcke für sehr ungleichmäßige vorgesehen sind.
Wenn du z.b. durch dein bild läufst und immer was anders machen musst, wenn du einen rand triffst, dann passiert das ja nur 1 + breite(oder höhe) mal, während das andere breite*höhe - F mal passiert, d.h. wenn du dein if/else entsprechend hinschreibst, dann stimmt das if statement quasi immer und der prozessor tut so als wäre es ein unbedingter sprung, was zu einer erheblich schnelleren ausführungszeit führt.
Das es von der Sprache her nicht gleichwertig ist, sieht man ja auch schon am argumenttyp von if bzw. switch.
Gruß
Wenn du z.b. durch dein bild läufst und immer was anders machen musst, wenn du einen rand triffst, dann passiert das ja nur 1 + breite(oder höhe) mal, während das andere breite*höhe - F mal passiert, d.h. wenn du dein if/else entsprechend hinschreibst, dann stimmt das if statement quasi immer und der prozessor tut so als wäre es ein unbedingter sprung, was zu einer erheblich schnelleren ausführungszeit führt.
Das es von der Sprache her nicht gleichwertig ist, sieht man ja auch schon am argumenttyp von if bzw. switch.
Gruß
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Ich habe der Klarheit wegen Pseudo-Code:
Code: Alles auswählen
SomeEnum Value;
// switch-Version
switch(Value) {
case Enum1:
return 0;
case Enum2:
return 1;
case Enum3:
return 2;
default:
throw Error;
};
// if/elseif-Version
if(Enum1 == Value)
return 0;
else if(Enum2 == Value)
return 1;
else if(Enum3 == Value)
return 2;
else
throw Error;
Natürlich, genau dafür ist der Optimizer doch da … und naja, die Blöcke sind vollkommen homogen und bestehen nur aus return Value;, if/else habe ich nur gewählt, weil es einfacher wartbar ist. Weiterhin hätte ich erwartet, dass es in den Entscheidungsbäumen, die der Compiler baut, eh gleich aussieht und deshalb vom Optimizer auch gleich behandelt wird.Aramis hat geschrieben:Ich denke nicht, dass es die Aufgabe des Optimizers ist Dinge zu optimieren, die man bei klarem Kopf sowieso nicht machen würde, z.B. Fensternachrichten mit gigantischen if/else-Blöcken behandeln ...
Genau das befürchte ich auch.Aramis hat geschrieben:Mag auch sein dass die einschlägigen Benchmarks keine Stellen haben, an denen diese Optimierung nützlich wäre ... :D
Von diesen Side-Effects habe ich auch gelesen. Es ging darum, dass der Compiler die Ausführungsreihenfolge der Blöcke aufrecht erhalten muss. Kapiert habe ich das aber nicht, denn das muss er bei switch ja genauso tun (ohne break; „flutscht“ die Ausführung unten durch)?kimmi hat geschrieben:Vielleicht wollten die Autoren auch einfach vermeiden, daß irgendwelche Optimierungen in den id / else if / else Blöcken böse Side-Effects auslösen. Schließlich müßte man dann die Optimierung in irgendeiner Form mit der Semantik des Ausdrucks koppeln, was auch nicht gerade der weisheits letzter Schluß ist.
Du hast – von Compiler-spezifischen Erweiterungen mal abgesehen – ja keine Möglichkeit, der CPU mitzuteilen, welcher Zweig öfter ausgeführt werden wird. Außerdem läuft die Sprungvorhersage der CPU durch die Tabellen, die switch erzeugt, erheblich schneller durch (weil keine Vergleichsoperation durchgeführt werden muss) und ist damit auch bei großer Streuung zwischen oft oder selten genutzten Zweigen performanter.Lord Delvin hat geschrieben:Der Grund wird wohl sein, dass switch statesments in C++ für gleichwertige Auswahl und (verschachtelte)if/else blöcke für sehr ungleichmäßige vorgesehen sind.
- Lord Delvin
- Establishment
- Beiträge: 597
- Registriert: 05.07.2003, 11:17
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Das glaubst du jetzt nicht wirklich? Ich mein wie willst du denn dann wissen, was deine nächste Operation ist? Du darfst in ein switch ja auch default und "löchrige" werte verwenden. D.h. du musst MINDESTENS einen vergleich durchführen, auf den du vor deinem Sprung warten musst.Krishty hat geschrieben: Außerdem läuft die Sprungvorhersage der CPU durch die Tabellen, die switch erzeugt, erheblich schneller durch (weil keine Vergleichsoperation durchgeführt werden muss) und ist damit auch bei großer Streuung zwischen oft oder selten genutzten Zweigen performanter.
Des weiteren hab ich mich mit der CPU sagen ob das genommen wird vielleicht ungechickt ausgedrückt. Die CPU rät das natürlich selbständig, aber wenn du in ner kurzen schleife ein if verwendest, dass einen vergleich und daran gebunden einen Sprung hat, dann wirst du feststellen, dass dein code schneller zuende ist, wenn die auswertung der bedingung nahezu konstant ist.
Btw. ist das if / else if / else if ... in den allermeisten fällen ein zeichen dafür, dass man was falsch gemacht hat, wenn man C++ programiert.
Gruß
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Natürlich sind löchrige switches und default-Werte eine Ausnahme (obwohl der Compiler auch dort mit binären Entscheidungsbäumen besser wegkommt als mit unoptimiertem if/else). In meinem Beispiel kommt auch immer mindestens ein Vergleich für default bzw das finale else vor. Die Sache sind die nicht-default-Sprünge … ich zeige dir, wie das in Pseudo-Assembler aussieht, default, letztes else, Prolog und Epilog der Klarheit halber außen vor (ja, meine Funktion macht tatsächlich nichts anderes als enums zwischen 0 und x entgegenzunehmen und dafür andere Werte zurückzugeben):Lord Delvin hat geschrieben:Das glaubst du jetzt nicht wirklich? Ich mein wie willst du denn dann wissen, was deine nächste Operation ist? Du darfst in ein switch ja auch default und "löchrige" werte verwenden. D.h. du musst MINDESTENS einen vergleich durchführen, auf den du vor deinem Sprung warten musst.Krishty hat geschrieben: Außerdem läuft die Sprungvorhersage der CPU durch die Tabellen, die switch erzeugt, erheblich schneller durch (weil keine Vergleichsoperation durchgeführt werden muss) und ist damit auch bei großer Streuung zwischen oft oder selten genutzten Zweigen performanter.
Code: Alles auswählen
enum EValue {
Enum1 = 0,
Enum2 = 1,
Enum3 = 2
};
EValue Value = …
switch(Value) {
case Enum1:
return 3;
case Enum2:
return 2;
case Enum3:
return 1;
};
00 jump [01 + Value * 2]
01 push 3
02 return
03 push 2
04 return
05 push 1
06 return
if(Enum1 == Value)
return 3;
else if(Enum2 == Value)
return 2;
else if(Enum3 == Value)
return 1;
00 compare Value, 0
01 jump not equal [04]
02 push 3
03 return
04 compare value, 1
05 jump not equal [08]
06 push 2
07 return
08 push 1
09 return
Okay, das mag stimmen (für diesen Fall kenne ich mich zuwenig mit der Sprungvorhersage aus).Lord Delvin hat geschrieben:Des weiteren hab ich mich mit der CPU sagen ob das genommen wird vielleicht ungechickt ausgedrückt. Die CPU rät das natürlich selbständig, aber wenn du in ner kurzen schleife ein if verwendest, dass einen vergleich und daran gebunden einen Sprung hat, dann wirst du feststellen, dass dein code schneller zuende ist, wenn die auswertung der bedingung nahezu konstant ist.
Die Funktion wird aus endlich rekursiven Templates zusammengesetzt (jedes else ist ein Aufruf) … würde ich sie von Hand schreiben, würde ich wohl switch oder einen selbstgeschriebenen Lookup einsetzen … aber Compiler-generiertem Code traue ich immer mehr als eigenem, darum möchte ich den Compiler dazu kriegen, das gefälligst richtig zu optimieren. Habe übrigens auch mal den Operator ?: ausprobiert, leider mit selbem Ergebnis wie else-if.Lord Delvin hat geschrieben:Btw. ist das if / else if / else if ... in den allermeisten fällen ein zeichen dafür, dass man was falsch gemacht hat, wenn man C++ programiert.
- kimmi
- Moderator
- Beiträge: 1405
- Registriert: 26.02.2009, 09:42
- Echter Name: Kim Kulling
- Wohnort: Luebeck
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Ich wage zu behaupten, daß die Zeit in den meisten Codes nicht zu großen Teilen in den if / else if / else Blöcken hängt, sondern noch von einigen anderen Parametern beeinflußt wird. Hast du festhestellt, daß du in deinen Vergleichen zu viel Zeit verbringst oder wie kommt es, daß du dir gerade diese Ecke so genau betrachtest? Ich frage aus reiner Neugierde.
Gruß Kimmi
Gruß Kimmi
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Ich habe gerade mit der Optimierung einer Klasse angefangen, dabei ist mir dieser Code besonders ins Auge gefallen, weil er in drei Funktionen vorkommt, die von der Initialisierung über innere Schleifen bis zur Freigabe laufend aufgerufen werden. Noch dazu stehe ich vor einem Space-Time-Tradeoff, da ich ihre Ergebnisse auch einfach speichern könnte, statt jedes Mal den Auswahlblock zu durchlaufen …kimmi hat geschrieben:Ich wage zu behaupten, daß die Zeit in den meisten Codes nicht zu großen Teilen in den if / else if / else Blöcken hängt, sondern noch von einigen anderen Parametern beeinflußt wird. Hast du festhestellt, daß du in deinen Vergleichen zu viel Zeit verbringst oder wie kommt es, daß du dir gerade diese Ecke so genau betrachtest? Ich frage aus reiner Neugierde.
Bevor ich #pragma inline_recursion gefunden habe, hat der Compiler noch jedes else als eigenen Funktionsaufruf kompiliert(!) …
Benchmarks habe ich nicht, im Internet geht man von 10 bis 30% Performance-Unterschied zwischen switch und if-else aus … so schnell, wie die Funktion nun ist, ist das – für das Programm als großes Ganzes – vernachlässigbar. Da ich aber eh schon über dem Assembler hockte, wollte ich wissen, warum die Funktion jetzt zwar ausreichend, aber nicht optimal kompiliert wird. Ihr kennt mich ja, solche winzigen Inkonsistenzen können mich auf die Palme bringen.
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Der Geschwindigkeitsunterschied zwischen if/else und switch liegt darin das if/else eine Vergleichoperation auf Assebler-Ebene(Maschienencode) ist und switch eine Sprungoperation.
d.h:... ist auf Assember-Ebene 5 Vergeichsoperationen + 5 Sprungbefehle -> also 10 Befehle
... ist auf Assember-Ebene 1 Sprungoperation + 1 Sprungbefehle für das break -> also 2 Befehle
Das ist auch der Grund warum switch in der Regel nur Basistypen unterstützt, wobei manche Compiler auch komplexe Typen zulassen und diese dann via Bäume abbilden.
Das ist zumindest was ich von 15 Jahren im Programmierunterricht gelernt hatte.
mfg
Karl
d.h:
Code: Alles auswählen
if(i == 1) {}
else if(i == 2) {}
else if(i == 3) {}
else if(i == 4) {}
else if(i == 5) {}
Code: Alles auswählen
switch(i)
{
case 1:
{} break;
case 2:
{} break;
case 3:
{} break;
case 4:
{} break;
case 5:
{} break;
}
Das ist auch der Grund warum switch in der Regel nur Basistypen unterstützt, wobei manche Compiler auch komplexe Typen zulassen und diese dann via Bäume abbilden.
Das ist zumindest was ich von 15 Jahren im Programmierunterricht gelernt hatte.
mfg
Karl
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
… und ziemlich deckungsgleich mit meinem Code-Beispiel oben :)
Jetzt würde mich nur interessieren, warum der Compiler if/else nicht darauf prüft, ob sie sich wie ein switch optimieren lassen. Ich meine, nach der Optimierung bleibt vom ursprünglichen Code nichts mehr übrig, soviel Datenflussanalyse, Schleifentransformation etc da durchgeführt wird – da kann es doch unmöglich „durchrutschen“, ohne dass es einen bestimmten Grund gibt?
Könnte jemand testen, ob der das GCC schafft? Würde mich mal interessieren.
Jetzt würde mich nur interessieren, warum der Compiler if/else nicht darauf prüft, ob sie sich wie ein switch optimieren lassen. Ich meine, nach der Optimierung bleibt vom ursprünglichen Code nichts mehr übrig, soviel Datenflussanalyse, Schleifentransformation etc da durchgeführt wird – da kann es doch unmöglich „durchrutschen“, ohne dass es einen bestimmten Grund gibt?
Könnte jemand testen, ob der das GCC schafft? Würde mich mal interessieren.
-
- Moderator
- Beiträge: 2138
- Registriert: 25.02.2009, 13:37
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Zur allgemeinen Verwirrung hab ich noch ein bisschen google bedient und dieses gefunden:
Bin mir nicht sicher was die mit "far apart" meinen und warum das einen Unterschied machen sollte...
http://www.eventhelix.com/realtimemantr ... ppcode.htmPlace case labels in narrow range
If the case labels are in a narrow range, the compiler does not generate a if-else-if cascade for the switch statement. Instead, it generates a jump table of case labels along with manipulating the value of the switch to index the table. This code generated is faster than if-else-if cascade code that is generated in cases where the case labels are far apart. Also, performance of a jump table based switch statement is independent of the number of case entries in switch statement.
Place frequent case labels first
If the case labels are placed far apart, the compiler will generate if-else-if cascaded code with comparing for each case label and jumping to the action for leg on hitting a label match. By placing the frequent case labels first, you can reduce the number of comparisons that will be performed for frequently occurring scenarios. Typically this means that cases corresponding to the success of an operation should be placed before cases of failure handling.
Break big switch statements into nested switches
The previous technique does not work for some compilers as they do not generate the cascade of if-else-if in the order specified in the switch statement. In such cases nested switch statements can be used to get the same effect.
To reduce the number of comparisons being performed, judiciously break big switch statements into nested switches. Put frequently occurring case labels into one switch and keep the rest of case labels into another switch which is the default leg of the first switch.
Bin mir nicht sicher was die mit "far apart" meinen und warum das einen Unterschied machen sollte...
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Meines Wissens nach versucht der Compiler bei eingeschalteter Optimierung auch alle If's durch Sprungbefehle (z.B. switch, aber auch einfache jumps) zu ersetzen. Dies gelingt auch meist bei einfachen Statements, wobei das switch-Statement das Optimum ist was es zu erreichen gilt.
@ Alexander Kornrumpf
Im Assember unterscheidet man zwischen "short", "near" und "far". Short ist jener Code der innerhalb der nächsten 128 Bytes liegt. Near ist der Code des aktuellen Code-Segments, ein Code-Segment hat eine Größe von 64 KByte. Far ist der Rest der Applikation, der nicht im aktuellen Code-Segment liegt.
Aus diesem Grund versuchen die Compiler auch switches, if's aber auch Funktionen immer innerhalb eines Code-Segments unterzubringen da dies die Schnellste Code-Ausführung zur Folge hat.
http://de.wikibooks.org/wiki/Assembler_ ... _Schleifen
@ Alexander Kornrumpf
Im Assember unterscheidet man zwischen "short", "near" und "far". Short ist jener Code der innerhalb der nächsten 128 Bytes liegt. Near ist der Code des aktuellen Code-Segments, ein Code-Segment hat eine Größe von 64 KByte. Far ist der Rest der Applikation, der nicht im aktuellen Code-Segment liegt.
Aus diesem Grund versuchen die Compiler auch switches, if's aber auch Funktionen immer innerhalb eines Code-Segments unterzubringen da dies die Schnellste Code-Ausführung zur Folge hat.
http://de.wikibooks.org/wiki/Assembler_ ... _Schleifen
- Schrompf
- Moderator
- Beiträge: 5050
- Registriert: 25.02.2009, 23:44
- Benutzertext: Lernt nur selten dazu
- Echter Name: Thomas
- Wohnort: Dresden
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Ich glaube nicht, dass hier noch die alte NEAR und FAR-Semantic aus 16Bit-Zeiten gemeint ist. Ich vermute eher, es geht hier schlicht um Instruction Caches - je weiter vorne die case-Blöcke im switch kommen, desto näher am Auswerte-Code liegt der Code dazu. Sprich, die nahen Code-Blöcke sind "wärmer" im Cache als die hinteren.
Früher mal Dreamworlds. Früher mal Open Asset Import Library. Heutzutage nur noch so rumwursteln.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Nein, sie meinen folgenden Fall:
Hier liegen die cases – vom Wertebereich – soweit auseinander, dass der Compiler keinen Jump-Table generieren kann (schließlich müsste der 0xBAADF00D groß sein) und wird stattdessen if-else einsetzen. Ich glaube durchaus, dass bei dieser Entscheidung kkrahls Near-Far-Semantik zum Einsatz kommt, und zwar im Sinne von: „Wie weit läge ein Jump-Table-Eintrag maximal entfernt, passte er noch ins aktuelle Segment?“.
In kkrahls Code-Beispiel liegen alle Werte zwischen 1 und 5 („narrow range“) und können damit optimiert werden.
Code: Alles auswählen
switch(Value) {
case 0x0:
return 3;
case 0x111:
return 2;
case 0xBAADF00D:
return 1;
}
In kkrahls Code-Beispiel liegen alle Werte zwischen 1 und 5 („narrow range“) und können damit optimiert werden.
-
- Moderator
- Beiträge: 2138
- Registriert: 25.02.2009, 13:37
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Da hätte ich auch selbst drauf kommen können, stimmt.Krishty hat geschrieben:Nein, sie meinen folgenden Fall:Hier liegen die cases – vom Wertebereich – soweit auseinander, dass der Compiler keinen Jump-Table generieren kann (schließlich müsste der 0xBAADF00D groß sein) und wird stattdessen if-else einsetzen. Ich glaube durchaus, dass bei dieser Entscheidung kkrahls Near-Far-Semantik zum Einsatz kommt, und zwar im Sinne von: „Wie weit läge ein Jump-Table-Eintrag maximal entfernt, passte er noch ins aktuelle Segment?“.Code: Alles auswählen
switch(Value) { case 0x0: return 3; case 0x111: return 2; case 0xBAADF00D: return 1; }
In kkrahls Code-Beispiel liegen alle Werte zwischen 1 und 5 („narrow range“) und können damit optimiert werden.
Re: [VC++’08]Unterschied zwischen if/else-if und switch
GCC:
Mein billig-Beispiel (switch fuer 0,1,2 und default Zweig vs. if-elseif...else, jeweils mit printfs in den Zweigen) mit gcc 4.3.3 liefert bei -O3 (fast) identischen Code. Ohne Sprungtabellen, mit direkten Vergleichen. Einzig die Reihenfolge der Vergleiche ist verschieden. Andere gcc-Versionen hab ich gerad nicht hier.
Gruss!
Joerg
Mein billig-Beispiel (switch fuer 0,1,2 und default Zweig vs. if-elseif...else, jeweils mit printfs in den Zweigen) mit gcc 4.3.3 liefert bei -O3 (fast) identischen Code. Ohne Sprungtabellen, mit direkten Vergleichen. Einzig die Reihenfolge der Vergleiche ist verschieden. Andere gcc-Versionen hab ich gerad nicht hier.
Gruss!
Joerg
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Ich glaub nicht, dass das Krishty viel helfen wird:) Es geht schließlich darum, ob der Compiler if/elseif zu ner Sprungtabelle optimieren kann.Jörg hat geschrieben:Mein billig-Beispiel (switch fuer 0,1,2 und default Zweig vs. if-elseif...else, jeweils mit printfs in den Zweigen) mit gcc 4.3.3 liefert bei -O3 (fast) identischen Code. Ohne Sprungtabellen, mit direkten Vergleichen. Einzig die Reihenfolge der Vergleiche ist verschieden. Andere gcc-Versionen hab ich gerad nicht hier.
Kannst du das begründen? Ich hab eigentlich in letzter Zeit festgestellt, dass die if/else if Variante deutlich leserlicher ist und weniger Codezeilen braucht als die switch Variante. Außerdem gibt es weniger Einrückprobleme, keine Unkonsistenzen wegen Blöcken, die man bei switches braucht, wenn man Variablen anlegt, keine Fehlerquellen durch vergessene breaks oder absichtlich weggelassene breaks, und außerdem sieht man bei if/elseif immer, womit der Wert verglichen wird. Bei nem case 5 muss man immer hochscrollen, wenn man mal vergessen hat, womit nun 5 eigentlich verglichen wird.Lord Delvin hat geschrieben:Btw. ist das if / else if / else if ... in den allermeisten fällen ein zeichen dafür, dass man was falsch gemacht hat, wenn man C++ programiert.
Oder meinst du nur ganz allgemein, dass switches böse sind und durch modernere Sachen wie Polymorphie ausgetauscht werden sollen? Das geht zwar, ist aber nicht immer sinnvoll.
Ciao
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Doch doch, ich habe ja gesagt, dass mich das interessiert. Wenn GCC das optimiert kriegt, kann ich mir sicher sein, dass es ein Bug/fehlendes Feature von Visual C++ ist. Da gibt es allerdings ein Problem: Mit Funktionsaufrufen erzeugt auch Visual C++ keine Sprungtabelle, sondern wertet einzeln aus. Jörg, könntest du das nochmal testen und diesmal printf() nicht direkt aufrufen, sondern die Zweige so konstruieren, dass sie nur ints/char *s zurückgeben?Helmut hat geschrieben:Ich glaub nicht, dass das Krishty viel helfen wird:)
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Auch ohne Funktionsaufrufe erzeugt der gcc in beiden Faellen cmp-Sequenzen.
Es ist gut moeglich, dass es da noch Parameter gibt, welche diese Entscheidung des Compilers beeinflussen, bin zu faul, da nochmal das Handbuch zu lesen.
Wenn das wirklich ein Bottleneck bei Dir ist, dann schau doch, wie Du generell diese Art von dynamischer Entscheidung vermeidest, vllt. geht das einfacher und hilft dann auch bei mehreren Platformen. Koennte mir vorstellen, dass er auf ARM z.B. auf die Spruenge verzichtet und conditional moves verwendet.
Es ist gut moeglich, dass es da noch Parameter gibt, welche diese Entscheidung des Compilers beeinflussen, bin zu faul, da nochmal das Handbuch zu lesen.
Wenn das wirklich ein Bottleneck bei Dir ist, dann schau doch, wie Du generell diese Art von dynamischer Entscheidung vermeidest, vllt. geht das einfacher und hilft dann auch bei mehreren Platformen. Koennte mir vorstellen, dass er auf ARM z.B. auf die Spruenge verzichtet und conditional moves verwendet.
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Es ist kein Bottleneck, ich frage mich eben nur, warum der Compiler das nicht optimiert … aber wenn GCC das auch nicht macht, ist es wohl einfach nur ein nicht implementiertes Optimizer-Feature :/
Ich danke euch für die Hilfe.
Ich danke euch für die Hilfe.
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Hm, also ich finde das ehrlich gesagt schon ok so, wie es ist.
D.h. "switch"s werden (z.b. als binärbaum) optimiert, "if"s nicht.
Warum? Kann ich einfach erklären:
Punkt 1:
"if"s können alles vergleichen, "switch"s nur zahlen.
Damit ist wohl klar, dass eine standardmäßige optimierung als binärbaum einfach umzusetzen ist für "switch"s, aber bei überladenen ==, <=, usw. operatoren eher schwierig sein könnte.
Punkt 2:
Der Programmierer hat die Kontrolle.
In keiner anderen Sprache ist man so systemnah wie bei C/C++.
Wenn ich weiss, dass bestimmte Fälle oft oder selten vorkommen, dann kann ich die prüf-reihenfolge mit "if"s vorgeben. "switch"s nutzte ich, wenn die cases ausgewogen sind (oder unbekannt, um den schnellsten worst-case fall zu haben).
Der Optimizer würde ja sonst meine Optimierung wegoptimieren, da hab ich schon lieber selbst die Kontrolle.
Und wenn das wirklich mal ein Bottleneck werden sollte, muss man sowieso in ASM proggen, um da noch maßgeblich was rauszuholen, und da werden die "if"s mit Sicherheit nicht alleine den Ausschlag geben...
D.h. "switch"s werden (z.b. als binärbaum) optimiert, "if"s nicht.
Warum? Kann ich einfach erklären:
Punkt 1:
"if"s können alles vergleichen, "switch"s nur zahlen.
Damit ist wohl klar, dass eine standardmäßige optimierung als binärbaum einfach umzusetzen ist für "switch"s, aber bei überladenen ==, <=, usw. operatoren eher schwierig sein könnte.
Punkt 2:
Der Programmierer hat die Kontrolle.
In keiner anderen Sprache ist man so systemnah wie bei C/C++.
Wenn ich weiss, dass bestimmte Fälle oft oder selten vorkommen, dann kann ich die prüf-reihenfolge mit "if"s vorgeben. "switch"s nutzte ich, wenn die cases ausgewogen sind (oder unbekannt, um den schnellsten worst-case fall zu haben).
Der Optimizer würde ja sonst meine Optimierung wegoptimieren, da hab ich schon lieber selbst die Kontrolle.
Und wenn das wirklich mal ein Bottleneck werden sollte, muss man sowieso in ASM proggen, um da noch maßgeblich was rauszuholen, und da werden die "if"s mit Sicherheit nicht alleine den Ausschlag geben...
- Krishty
- Establishment
- Beiträge: 8316
- Registriert: 26.02.2009, 11:18
- Benutzertext: state is the enemy
- Kontaktdaten:
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Das ist der erste nachvollziehbare Grund, den ich dafür höre :) Aber auch der ist hier nicht perfekt anwendbar:NytroX hat geschrieben:Wenn ich weiss, dass bestimmte Fälle oft oder selten vorkommen, dann kann ich die prüf-reihenfolge mit "if"s vorgeben. "switch"s nutzte ich, wenn die cases ausgewogen sind (oder unbekannt, um den schnellsten worst-case fall zu haben).
Die switch-Version wird hier nicht etwa zu einem Binärbaum optimiert, sondern – weil die cases eng (und sogar chronologisch) vorliegen – zu einem Jump-Table. Selbst, wenn ich die if-elses absichtlich so angeordnet hätte, dass der häufigste Fall zuerst vorkäme, wäre der resultierende Vergleich immernoch langsamer als die Jump-Table, die komplett ohne Vergleiche auskommt.
Lägen die cases weiter auseinder, so dass der Compiler einen Binärbaum generieren müsste, dann wäre das unoptimierte if-else definitiv schneller, da hast du durchaus Recht. Aber wir sprechen hier von einem Jump-Table, der (– afaik –) immer die schnellste Lösung ist, egal wie häufig welcher Zweig ausgeführt wird.
Daran ist grundsätzlich nichts falsch, weil man wirklich in kaum einer anderen Sprache eine solche Kontrolle über den generierten Code hat – aber das kann man nicht pauschalisieren! C++ erlaubt auch hochabstrakte Programmierung und, in meinem Fall, Meta-Programmierung. Da ich das Programm nicht schreibe, sondern durch templates generieren lasse kann ich an dieser Stelle kein switch einbauen, sondern muss mich bemühen, den Compiler in die Richtung eines möglichst gleichwertigen Konstruktes zu schubsen. Dass er die Optimierung scheinbar nur aufgrund anderer Schlüsselwörter verweigert, ärgert mich.NytroX hat geschrieben:Punkt 2:
Der Programmierer hat die Kontrolle.
In keiner anderen Sprache ist man so systemnah wie bei C/C++.
Ja, manchmal habe ich echt die Schnauze von den Hochsprachen und den Compiler-Zickereien voll und will es nurnoch auf die gute, alte Geoff-Crammond-Art und -Weise machen. Dann schreibe ich hundert Zeilen perfekten Assembler-Code, bilde mir ein, dass Assembler immer die bessere Wahl sei und werde bei der nächsten Funktion in Hinsicht auf die Performance vom Compiler gedemütigt :DNytroX hat geschrieben:Und wenn das wirklich mal ein Bottleneck werden sollte, muss man sowieso in ASM proggen, um da noch maßgeblich was rauszuholen, und da werden die "if"s mit Sicherheit nicht alleine den Ausschlag geben...
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Nicht immer. Stell Dir vor, die Tabelle liegt mal nicht im Cache, weil dein Datenlayout an anderer Stelle mit der Tabelle kollidiert. Und schon geht die Eierei los, so nach dem Motto: Wie sag ich dem Compiler, 'wo' meine Tabelle am besten liegen sollte ;) Oder schlimmer, auf Architekturen, deren Programm- und Datenspeicher unterschiedliche Latenzen aufweisen (embedded Bereich bei getrennten Anbindungen durchaus ueblich), kann es vorteilhaft sein, auf einen Lookup zu verzichten.Krishty hat geschrieben: Jump-Table, der (– afaik –) immer die schnellste Lösung ist, egal wie häufig welcher Zweig ausgeführt wird.
Sicherlich gibt es irgendwo einen Punkt, an den beide Varianten gleich schnell sind. Dafuer muesste man aber die Haeufigkeitsverteilung der 'cases' kennen usw. Es ist wichtig, die Alternativen zu kennen, die man hat, falls mal ein Bottleneck an dieser Stelle auftritt. Solange es aber nur Code-Kosmetik ist, dann jedem das seine :)
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Hm, soweit ich weiß liegt die Lookuptabelle nicht im Datenspeicher, sondern direkt im Code. Sogar sehr nah an der Instruction, die den Lookup ausführt. Ich würde also schon sagen, dass die Tabelle in jedem Fall schneller ist.Jörg hat geschrieben:Nicht immer. Stell Dir vor, die Tabelle liegt mal nicht im Cache, weil dein Datenlayout an anderer Stelle mit der Tabelle kollidiert. Und schon geht die Eierei los, so nach dem Motto: Wie sag ich dem Compiler, 'wo' meine Tabelle am besten liegen sollte ;) Oder schlimmer, auf Architekturen, deren Programm- und Datenspeicher unterschiedliche Latenzen aufweisen (embedded Bereich bei getrennten Anbindungen durchaus ueblich), kann es vorteilhaft sein, auf einen Lookup zu verzichten.
Ich hab mir mal die Mühe gemacht den gcc zu testen. Während er einen switch-Code mit aufeinanderfolgenden cases zu ner LookupTabelle macht, wird das if/else if-Äquivalent nicht optimiert. Also genauso doof wie der von MS. Die Demo vom Intelcompiler ist bei mir leider schon abgelaufen... Dem würde ich am ehesten zutrauen, sowas optimieren zu können.
Ciao
Re: [VC++’08]Unterschied zwischen if/else-if und switch
Ja da hast Du natuerlich voellig recht. Ich habe compiler-generierte Sprungtabellen mit normalen Lookup-Tabellen verwechselt, ziemlich daemlich heute.