if-else langsamer als switch



  • Morris Szyslak schrieb:

    hab noch eine Idee, if-else zu vermeiden, weiß aber nicht, ob das trotz der Division einen Geschwindigkeitsvorteil bringt:

    switch (ic/13)
      {
      case 0: break;//<=12
      case 1: break;//>=13
      }
    

    was is bei ic = 30?



  • 103% Leistung schrieb:

    CStoll schrieb:

    bei switch brauchst du nur eine Adresse aus der Sprungtabelle raussuchen und findest dort den korrekten Anweisungsblock.

    Was aber nur geht, wenn die case werte es zulassen, dass mann eine Sprungtabelle aufbauen kann.

    Du kannst dem Compiler schon vertrauen, daß er das bei einem standardkonformen switch() auch hinbekommt 😉

    @DipplInsch: Du mußt nicht unbedingt jedes if() rausschmeißen wollen - und für so eine Zwei-Wege-Auswahl macht es noch keinen großen Unterschied zwischen if und switch (wie gesagt, das zweite if ist redundant).



  • DipplInsch schrieb:

    Ich schreibe gerade an einer Regelung und mir ist aufgefallen, dass if-else-Konstruktionen um Größenordnungen langsamer als switch Anweisungen sind.

    Mit solchen Aussagen sollte man vorsichtig sein, da sie nicht allgemein gültig sind. Was schneller ist, hänt sehr stark von der Anzahl der Zweige ab. Auch Platform, Compiler und Optimierungseinstellungen spielen eine große Rolle.

    Bei einer if-else-Kaskade müssen alle Bedingungen ausgewertet werden, bis du bei der richtigen ankommst - bei switch brauchst du nur eine Adresse aus der Sprungtabelle raussuchen und findest dort den korrekten Anweisungsblock.

    Das hört sich erstmal nach einem klaren Vorteil für switch an. Dank Branch-Prediction, Speculative Execution usw. kann das Pendel aber auch durchaus mal in Richtung if-else ausschlagen.

    Wie immer gilt: im Zweifelsfall messen, nicht raten.



  • CStoll schrieb:

    (wie gesagt, das zweite if ist redundant).

    Wieso ist das redundant?

    Grüße



  • DipplInsch schrieb:

    CStoll schrieb:

    (wie gesagt, das zweite if ist redundant).

    Wieso ist das redundant?

    Wenn der Test auf ic<=12 fehlgeschlagen ist, ist ic>=13 in jedem Fall erfüllt 😉 (außer du hantierst mit Gleitkommawerten)



  • Tschuldigung für den Doppelbeitrag aber wo wir gerade dabei sind. Wie soll man das messen?

    HumeSikkins schrieb:

    Wie immer gilt: im Zweifelsfall messen, nicht raten.

    Ausserdem war das kein Raten sondern eine Schlussfolgerung aufgrund des Programmverhaltens. 😃

    Grüße



  • CStoll schrieb:

    103% Leistung schrieb:

    CStoll schrieb:

    bei switch brauchst du nur eine Adresse aus der Sprungtabelle raussuchen und findest dort den korrekten Anweisungsblock.

    Was aber nur geht, wenn die case werte es zulassen, dass mann eine Sprungtabelle aufbauen kann.

    Du kannst dem Compiler schon vertrauen, daß er das bei einem standardkonformen switch() auch hinbekommt 😉

    switch(a)
        {
        case 1:
            break;
        case 253:
            break;
        case 333:
            break;
        case 1444:
            break;
        case 5555:
            break;
        case 6643:
            break;
        case 11227:
            break;
        case 23458:
            break;
        case 23559:
            break;
        case 25550:
            break;
        }
    

    Wie sieht jetzt die Tabelle aus? Soll die 25550 Einträge haben von denen fast alle leer sind.



  • Mit einer perfekten Hashfunktion ging das schon theoretisch, die Frage ist, ob Compiler sowas machen 😉



  • Compiler sind bei if UND switch üblicherweise recht doof, weil sie nicht wissen können für welchen Fall sie optimieren sollen.
    Und "alles wird schneller" Optimierungen (z.B. eben Jumptable ohne vorherigen Rangecheck o.ä.) sind oft nicht möglich bzw. ist es oft schwierig für den Compiler zu erkennen dass sie (problemlos/gefahrlos) möglich sind.

    Was bei einigen Anwendungen möglich ist: lookup Tables verwenden.
    z.B. kann man ein Clamping eines unsigned ints auf 0-255 ohne "if" durchführen, z.B. so:

    unsigned int x = ...;
    // clamp:
    unsigned int const lut[2] = {0, 0xff};
    x |= lut[(x & 0x7fffff00) != 0];
    x &= 0xff;
    

    Das "(x & 0x7fffff00) != 0" sieht zwar wie ein verstecktes "if" aus, da es aber direkt in einen Index umgewandelt wird können manche Compiler intern ein billiges "conditional set" oder "conditional move" draus machen statt einem teuren "conditional jump".

    Genauso könnte man diese Technik u.U. verwenden wenn man je nach Bedingung einen von 2 ganz unterschiedlichen Werten auswählen möchte. Wenn das "if" mehr weh tut als beide Werte zu berechnen (d.h. einen zu berechnen obwohl man ihn garnicht braucht) bringt das auch was.



  • 103% Leistung schrieb:

    CStoll schrieb:

    103% Leistung schrieb:

    CStoll schrieb:

    bei switch brauchst du nur eine Adresse aus der Sprungtabelle raussuchen und findest dort den korrekten Anweisungsblock.

    Was aber nur geht, wenn die case werte es zulassen, dass mann eine Sprungtabelle aufbauen kann.

    Du kannst dem Compiler schon vertrauen, daß er das bei einem standardkonformen switch() auch hinbekommt 😉

    switch(a)
        {
        case 1:
            break;
        case 253:
            break;
        case 333:
            break;
        case 1444:
            break;
        case 5555:
            break;
        case 6643:
            break;
        case 11227:
            break;
        case 23458:
            break;
        case 23559:
            break;
        case 25550:
            break;
        }
    

    Wie sieht jetzt die Tabelle aus? Soll die 25550 Einträge haben von denen fast alle leer sind.

    Das implementiert der Compiler als binary search tree und blöcke die fortlaufend sind, kann er dann wieder als tabellen implementieren.


Anmelden zum Antworten