Geschwindigkeitsvergleich - Select/Case vs If

  • Ich habe gerade mal ein bisschen mit dem timer herumgespielt und dabei festgestellt, dass SELECT/CASE-Abfragen etwas schneller sind als gleichartige if-abfragen

    Ich wüsste nun gerne, woran das liegt, bzw warum man dann überhaupt noch if verwenden sollte....

    könnt ihr überhaupt bestätigen, dass Case schneller ist?

    PS: Ich bin nicht sicher wo das hier hingehört, Komplexitätsfragen gehören vieleicht auch unter "profi/entwickler"?! - naja entsprchend verschieben wenn ihrs für nötig haltet

    2 Mal editiert, zuletzt von winikator24654 (21. August 2007 um 20:05)

  • If nutzen wenns kuerzer als Select ist =)
    Ansonsten sind die paar MS ja relativ egal oder ?

  • select case ist aber selbst bei 1 eintrag schneller als if (zumdeinst nach dem was ich ausprobiert habe)

    klar sind ein paar ms an sich egal - wenn man aber große schleifen hat sumiert sich das ziemlich....

  • Da es einen else-case gibt wüßte ich nicht, wo das unmöglich sein sollte

    Der Code:

    $time1 = Timerinit()
    ;IF-Abfrage
    $timer1 = TimerDiff ($time1)

    $time2 = Timerinit()
    ;Select Case
    $timer2 = TimerDiff ($time2)

    msgbox (0,"",$timer1 & @CRLF & $timer2)

  • Bei groesseren Schleifen nimmt man ja auch Select, weil das uebersichtlicher ist und nicht weil das schneller ist von daher wayne...

  • eine große schleife setzt ja keine große if/case-abfrage voraus, die übersicht benötigt

  • In den meisten (allen?) Sprachen ist ein Select/Case schneller als If/ElseIf was aber erst bei wirklich großen Schleifen auffällt.Viel wichtiger als die Art der Conditionally Statements ist für die Geschwindigkeit die Reihenfolge der Auswertung. Also die Fälle die am häufigsten vorkommen könnten, sollten immer oben stehen, damit die Bedingung möglichst schnell erfüllt ist, wie bei allen anderen Abfragen eben auch.

    Und wenn ihr euch hier schon um Millisekunden rumschlägt (und das bei Sprache die eh durch nen Interpreter muß :D ) dann sollten wir uns auch noch über solche "Bremsen" unterhalten:

    Spoiler anzeigen
    [autoit]

    Dim $aArray[10000000]

    [/autoit] [autoit][/autoit] [autoit]

    $time1 = Timerinit()
    For $i = 0 to UBound($aArray)
    Next
    $timer1 = TimerDiff ($time1)
    ;==========================
    $time2 = Timerinit()
    $end = UBound($aArray)
    For $i = 0 To $end
    Next
    $timer2 = TimerDiff ($time2)

    [/autoit] [autoit][/autoit] [autoit]

    msgbox (0,"",$timer1 & @CRLF & $timer2)

    [/autoit]

    Die zweite Schleife ist schneller. :)

    Oder das hier:

    Spoiler anzeigen
    [autoit]

    $loop = 1000

    [/autoit] [autoit][/autoit] [autoit]

    $iTime = TimerInit()
    $i=0
    Do
    $i +=1
    Until $i>=$loop
    $timer1 = TimerDiff ($iTime)
    ;===============================================================================
    $iTime = TimerInit()
    $i=0
    While 1
    $i += 1
    If $i >= $loop Then ExitLoop
    Wend
    $timer2 = TimerDiff ($iTime)
    ;===============================================================================
    $iTime = TimerInit()
    $i=0
    While $i < $loop
    $i += 1
    Wend
    $timer3 = TimerDiff ($iTime)
    ;===============================================================================
    $z=0
    $iTime = TimerInit()
    For $i = 0 To $loop-1
    $z += 1
    Next
    $timer4 = TimerDiff ($iTime)

    [/autoit] [autoit][/autoit] [autoit]

    Msgbox(0,"",$timer1 & @crlf & $timer2 & @crlf & $timer3 & @crlf & $timer4)

    [/autoit]

    Viele Grüße und viel Spaß beim Optimieren!
    Stilgar

    2 Mal editiert, zuletzt von Stilgar (22. August 2007 um 10:27)

  • @winikator24654

    Dein kleines Testskript ist relativ ungeeignet um die Zeitdauer einer Methode zu bestimmen.
    Jede Methode wird dabei ja nur ein einziges mal aufgerufen.
    Da spielen noch so viele zufällige Faktoren rein die die Ausführung beeinflussen - du wirst eigentlich fast immer vollkommen andere Zahlenwerte sehen.
    Hinzu kommt das in dem Fall auch die Reihenfolge der Methodenausführung eine Rolle spielt - dreh die beiden Funktionen mal in der Reihenfolge um und du wirst sehen das die Daten sich sehr krass ändern.

    Willst du also verlässliche Werte haben musst du mehrere Durchläufe machen und dann den Mittelwert bilden - dann wird es erst aussagekräftig.

    Für den einfachen Fall der "2-spurigen" Verzweigung ( ;) ), also dem If-Else Fall sollten alle Methoden eigentlich fast gleich schnell in der Ausführung sein.
    Erst bei größeren Verzweigungen mit mehr Möglichkeiten sollte Select oder Switch Vorteile vorweisen können.

    Hab das auch mal getestet - mit folgendem Skript:

    Spoiler anzeigen
    [autoit]

    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Global $ZeitInit, $RahmenZeit, $ZeitDiff
    Global $ZeitErgebnisse[1][2], $MsgAusgabe

    [/autoit] [autoit][/autoit] [autoit]

    Global $N = 10000

    [/autoit] [autoit][/autoit] [autoit]

    #region Zeitaufwand des Methodenrahmens bestimmen
    $ZeitInit = TimerInit()

    [/autoit] [autoit][/autoit] [autoit]

    For $E = 1 To $N
    $X = 1000

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    ExitLoop
    WEnd
    Next
    $RahmenZeit = TimerDiff($ZeitInit) / $N
    #endregion

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    #region Zeitaufwand der Methode bestimmen
    _Initialisiere("Switch")

    [/autoit] [autoit][/autoit] [autoit]

    $ZeitInit = TimerInit()

    [/autoit] [autoit][/autoit] [autoit]

    For $E = 1 To $N
    $X = 1000

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    Switch $X
    Case 0
    ExitLoop
    Case Else
    $X -= 1
    EndSwitch
    WEnd
    Next
    $ZeitDiff = TimerDiff($ZeitInit) / $N - $RahmenZeit
    $ZeitErgebnisse[UBound($ZeitErgebnisse) - 1][0] = $ZeitDiff

    [/autoit] [autoit][/autoit] [autoit]

    #endregion

    [/autoit] [autoit][/autoit] [autoit]

    #region Zeitaufwand der Methode bestimmen
    _Initialisiere("Select")

    [/autoit] [autoit][/autoit] [autoit]

    $ZeitInit = TimerInit()

    [/autoit] [autoit][/autoit] [autoit]

    For $E = 1 To $N
    $X = 1000

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    Select
    Case $X = 0
    ExitLoop
    Case Else
    $X -= 1
    EndSelect
    WEnd
    Next
    $ZeitDiff = TimerDiff($ZeitInit) / $N - $RahmenZeit
    $ZeitErgebnisse[UBound($ZeitErgebnisse) - 1][0] = $ZeitDiff

    [/autoit] [autoit][/autoit] [autoit]

    #endregion

    [/autoit] [autoit][/autoit] [autoit]

    #region Zeitaufwand der Methode bestimmen
    _Initialisiere("If-Else")

    [/autoit] [autoit][/autoit] [autoit]

    $ZeitInit = TimerInit()
    For $E = 1 To $N
    $X = 1000

    [/autoit] [autoit][/autoit] [autoit]

    While 1
    If $X = 0 Then
    ExitLoop
    Else
    $X -= 1
    EndIf
    WEnd
    Next

    [/autoit] [autoit][/autoit] [autoit]

    $ZeitDiff = TimerDiff($ZeitInit) / $N - $RahmenZeit
    $ZeitErgebnisse[UBound($ZeitErgebnisse) - 1][0] = $ZeitDiff

    [/autoit] [autoit][/autoit] [autoit]

    #endregion

    [/autoit] [autoit][/autoit] [autoit]

    #region Auswerten und Vergleichen
    SplashOff()
    _ArraySort($ZeitErgebnisse, 0, 1, -1, 2)

    [/autoit] [autoit][/autoit] [autoit]

    $MsgAusgabe = ' Name Zeit Verhältnis' & @CRLF & '-------------------------------------------------' & @CRLF

    [/autoit] [autoit][/autoit] [autoit]

    For $t = 1 To $ZeitErgebnisse[0][0]
    $MsgAusgabe &= StringFormat("%- 20s %0 10.3f ms %5.1f", $ZeitErgebnisse[$t][1], $ZeitErgebnisse[$t][0], Round($ZeitErgebnisse[$t][0] / $ZeitErgebnisse[1][0], 1)) & @CRLF
    Next

    [/autoit] [autoit][/autoit] [autoit]

    $MsgAusgabe &= '-------------------------------------------------' & @CRLF & @CRLF

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite($MsgAusgabe & @CRLF & @CRLF)
    ConsoleWrite($RahmenZeit)
    #endregion

    [/autoit] [autoit][/autoit] [autoit]

    Func _Initialisiere($Name)
    ReDim $ZeitErgebnisse[UBound($ZeitErgebnisse) + 1][2]
    $ZeitErgebnisse[0][0] += 1
    $ZeitErgebnisse[UBound($ZeitErgebnisse) - 1][1] = $Name
    SplashTextOn("Test der Methode:", @CRLF & $ZeitErgebnisse[UBound($ZeitErgebnisse) - 1][1], 250, 60)
    EndFunc ;==>_Initialisiere

    [/autoit]

    Herausgekommen sind bei mir dann folgende Ergebnisse:

    Code
    Name                    Zeit        Verhältnis
    -------------------------------------------------
    Select                  00005.963 ms      1.0
    If-Else                 00006.106 ms      1.0
    Switch                  00006.174 ms      1.0
    -------------------------------------------------

    Der Unterschied ist tatsächlich eigentlich fast nicht vorhanden ...

  • Interessante Meßmethode :)

    Und dank Vorhersagen werden die Durchläufe optimiert und schneller, oder? *g*

    Einfach ein einem reellen Programm testen wie sich alles verhält was ohne Profiler auch nicht gerade gut klappt.

    Hier gibts sicher ein paar gute Anregungen:
    http://www.agner.org/optimize/optimizing_cpp.pdf

    wobei man bedenken sollte, das das für eine compilierte Sprache ist, bei einem Interpreter wie AutoIt sieht das schon wieder ganz anderst aus, der muß halt machen was da steht und das ist nicht immer optimal.

    Und auch wenns compiliert ist gibts "kleine" Differenzen:
    http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

    Viele Grüße
    Stilgar

    3 Mal editiert, zuletzt von Stilgar (22. August 2007 um 13:31)

  • Xenobiologist
    ein interessanter Link, danke!

    AspirinJunkie
    Das ein einmaliger test nicht viel bringt ist mir auch klar. Ich habe ja etliche durchläufe gemacht (gut, manuell und ohne mittelwert - aber der obere war stehts deutlich langsamer als der untere ("deutlich" ist natürlich definitionssache bei der skalierung :))

  • also beim ersten beispiel verstehe ich nicht warum die zweite schleife schneller ist. hätte nur vom code her auf jedenfall gedacht die erste ist schneller

  • Wahrscheinlich macht AutoIt genau das was dort steht:

    - Bei der ersten Schleife wird bei jedem Schleifendurchgang UBound überprüft.
    - Bei der zweiten Schleife steht das Ubound vor dem Schleifenkopf und wird somit nur einmal überprüft, also ist diese schneller.