If ohne Then

Übersicht BlitzBasic Allgemein

Neue Antwort erstellen

 

Sirrus

Betreff: If ohne Then

BeitragDi, Jun 14, 2016 18:39
Antworten mit Zitat
Benutzer-Profile anzeigen
Da bei If ein Then und der Then-Anweisungsteil optional sind wenn Else da ist, verwende ich gewöhnlich "If <Ausdruck> Else", wenn ich einen Ausdruck auf 0 testen will.
Ein Freund kannte das so gar nicht, meinte aber dass "If Not(<Ausdruck>) Then" schneller wäre, konnte allerdings nicht sagen warum.
Darum habe ich ein kleines Testprogramm geschrieben:
Code: [AUSKLAPPEN]
Print "Teststart:"
t=MilliSecs()
For n=1 To 10000000
   If n-n Else A=1
Next
t=MilliSecs()-t
Print "If <Ausdruck> Else:     "+t+" Millisekunden"

t=MilliSecs()
For n=1 To 10000000
   If n-n=0 Then A=1
Next
t=MilliSecs()-t
Print "If <Ausdruck>=0 Then    "+t+" Millisekunden"

t=MilliSecs()
For n=1 To 10000000
   If Not(n-n) Then A=1
Next
t=MilliSecs()-t
Print "If Not(<Ausdruck>) Then "+t+" Millisekunden"

WaitKey
End
Bei mir ist "Else" mit Blitz3D deutlich am schnellsten, bei BlitzPlus jedoch ist <Ausdruck>=0 etwas schneller. Not(<Ausdruck>) ist immer am langsamsten. (Jedoch Blitz3D ist mehr als 4x so schnell wie BlitzPlus)

Mich würde interessieren, ob das jetzt an meinem alten PC(Vista) liegt, bzw was bei euch am schnellsten ist. Kann ja auch mal jemand mit BlitzMax probieren.

BladeRunner

Moderator

BeitragDi, Jun 14, 2016 19:45
Antworten mit Zitat
Benutzer-Profile anzeigen
Ins blaue geraten:
bei not wird der Ausruck erstmal noch mit der boolschen Algebra verknüft, was etwas mehr Code erzeugen dürfte.
Bei if else hingegen übersetzt der Compiler das direkt in eine Verzweigung.
Warum nun B+ bei dem einen schneller ist und B3d bei dem anderen kann ich nur raten.


Hab das Ganze unter BMax mal getestet, allerdings mit einer Milliarde iterationen statt 10 Millionen:
Debug:
If <Ausdruck> Else: 8453 Millisekunden
If <Ausdruck>=0 Then 8752 Millisekunden
If Not(<Ausdruck>) Then 8475 Millisekunden

non-debug:
If <Ausdruck> Else: 597 Millisekunden
If <Ausdruck>=0 Then 549 Millisekunden
If Not(<Ausdruck>) Then 552 Millisekunden

Habe dann noch testweise die Ausdücke an andere Stelle des Tests geschoben, die Ergebnisse blieben identisch.
Somit scheint unter Debugger else am schnellsten, in der nicht-Debugger-Version ist Ausdruck = 0 das flotteste. Wirklich viel nehmen die sich allerdings nicht, immerhin ist der Speed von Langsamstem zu Schnellstem gerade mal mit 3,5% (debugger) und knapp 8%(non-debug) erhöht. Das macht im Milliardenbereich mal ne drittel Sekunde aus, aber im normalen Programmablauf solte das nicht spürbar sein.
Zu Diensten, Bürger.
Intel T2300, 2.5GB DDR 533, Mobility Radeon X1600 Win XP Home SP3
Intel T8400, 4GB DDR3, Nvidia GF9700M GTS Win 7/64
B3D BMax MaxGUI

Stolzer Gewinner des BAC#48, #52 & #92

DAK

BeitragDi, Jun 14, 2016 22:53
Antworten mit Zitat
Benutzer-Profile anzeigen
Sodale, ich hab mir das auch mal angeschaut.

Meine Ergebnisse waren die Folgenden:

Code: [AUSKLAPPEN]


       B+ Debug      B+   B3D Debug     B3D
else:      4640   59.30        1847   59.72
=0  :      4818   58.91        1916   59.83
not :      4650   58.98        2359   58.90


Pro 10000000 Durchläufe. Die Nicht-Debug-Läufe haben 100 Mal mehr Durchläufe gehabt, deswegen runtergerechnet die zwei Nachkommastellen.

Mein BMax wirft irgendwie gerade lauter Linkerfehler, deswegen hab ich das jetzt nicht reingenommen, aber BR hat es ja ganz gut analysiert.

Das Erste, was auffällt, ist, dass (zumindest im Nicht-Debug-Modus) die Werte allesamt quasi gleich sind. Die Unterschiede zwischen dem besten und dem schlechtesten Wert sind 0.66% (B+), 1.37% (B3D) und 8.04% (BMax). Im klassischen Blitz ist hiermit die Debatte komplett zu Ende. Eine Optimierung, die rund 1% Performance bringt, ist es nicht mal wert angeschaut zu werden. Vor allem, weil die 1% Performance nur dann rauskommen, wenn man im gesamten Programm nichts anderes macht. Die rund 8% bei BMax sind schon ein wenig mehr, allerdings wird das auch wenig Leistung aus irgendeinem echten Programm holen.

Das hier ist eine absolute Mikrooptimierung. In dem Beispiel von BladeRunner würde man pro einer Milliarde if-Vergleichen 45 Millisekunden gewinnen. Pro If-Vergleich macht das satte 0.045 Nanosekunden Einsparung. Das ist immer noch komplett witzlos.

Viele Leute, die mit Optimierungen nicht viel am Hut haben, versuchen durch solche Tricks ein paar Nanosekunden rauszuquetschen, programmieren dabei aber auf Algorithmus-Level komplett ineffizient und verlieren in der Größenordnung von 10 Millisekunden. In diesem Fall, programmier so wie es dir leichter lesbar ist, und wie der Code besser verständlich ist. Der Performance-Gewinn, den du davon haben wirst, der ist absolut marginal.
Gewinner der 6. und der 68. BlitzCodeCompo

Thunder

BeitragMi, Jun 15, 2016 0:54
Antworten mit Zitat
Benutzer-Profile anzeigen
Interessante Beobachtung! Aber:

Vertauscht mal die Tests der Varianten. Ich bekomme beim Original (mit 1 Mrd. Durchläufe):
Zitat:
Teststart:
If <Ausdruck> Else: 161 Millisekunden
If <Ausdruck>=0 Then 99 Millisekunden
If Not(<Ausdruck>) Then 87 Millisekunden


und mit Vertauschung:
Zitat:
Teststart:
If <Ausdruck>=0 Then 181 Millisekunden
If Not(<Ausdruck>) Then 102 Millisekunden
If <Ausdruck> Else: 73 Millisekunden


BlitzMax generiert übrigens für alle 3 Loops äquivalenten Assembler-Code
Code: [AUSKLAPPEN]
1. Loop:
1. Loop:
                jmp     _21
        _6:
                mov     eax,edx
                sub     eax,edx
                cmp     eax,0
                je      _22
                jmp     _23
        _22:
        _23:
        _4:
                add     edx,1
        _21:
                cmp     edx,100000000
                jle     _6

2. Loop:

                jmp     _25
        _11:
                mov     eax,edx
                sub     eax,edx
                cmp     eax,0
                jne     _26
        _26:
        _9:
                add     edx,1
        _25:
                cmp     edx,100000000
                jle     _11


3. Loop:
                jmp     _27
        _15:
                mov     eax,edx
                sub     eax,edx
                cmp     eax,0
                jne     _28
        _28:
        _13:
                add     edx,1
        _27:
                cmp     edx,100000000
                jle     _15


Was man hier erkennt ist meiner Meinung nach der Branch Predictor in der CPU, der nach der 1. Loop
seine predictions angepasst hat und deswegen die Pipeline der CPU bei den nächsten zwei Loops nicht
so oft stallt.

PS: ich testete nur mit BlitzMax (auf Linux)
Meine Sachen: https://bitbucket.org/chtisgit https://github.com/chtisgit
 

Sirrus

BeitragMi, Jun 15, 2016 1:37
Antworten mit Zitat
Benutzer-Profile anzeigen
Danke an alle
war auch interessant, eure Zeiten zu sehen, die sind bei mir nicht so viel schlechter, wie ich erwartet hatte

besonders der Assembler-Code von Thunder ist interessant
Ich kann also die Form verwenden, in der für mich mein Progamm am übersichtlichsten ist, der Code bleibt gleich Wink

DAK

BeitragSa, Jun 18, 2016 16:29
Antworten mit Zitat
Benutzer-Profile anzeigen
Ich hab probiert, den B+ und B3D-Code zu disassemblieren, hat aber mangels sinnvoller (sprich teurer) Disassembler-Software nicht so recht funktionieren.

Aber ich denke, auch dort ist der Code recht gleich.

Solche Microbenchmarks sind aber gerade wegen Effekten, wie sie Thunder anspricht, recht wertlos. Verschiedene CPUs arbeiten den gleichen Code verschieden schnell ab. Manchmal auch der Compiler kann da gut dazwischenpfuschen. Ich hab mal so ein Benchmark gehabt, und war verblüfft, wie genial schnell der getestete Code gelaufen ist. Bis ich dann rausgefunden habe, dass der Compiler den getesteten Code für überflüssig befunden (er hat ja nichts Sinnvolles getan) und ihn wegoptimiert hat.

Dadurch, das verschiedene CPUs anders arbeiten, ist, je nach CPU, die eine oder andere Variante mal schneller oder langsamer, gerade, wenn der Unterschied so marginal ist.

Das mit der Branch Prediction von Thunder macht schon allein wegen der Einsparung Sinn. Die 0.045 Nanosekunden entsprechen etwa einem Zehnteltakt einer 2 GHz-CPU. Das ist also viel zu wenig, als was durch eine einzige Instruktion weniger eingespart werden kann.
Eine andere Möglichkeit ist, das sich der Prozessor hochtaktet. Aktuelle CPUs laufen auf einem niedrigen Takt, wenn nicht viel los ist (zum Stromsparen) und takten sich dann automatisch hoch, wenn die CPU auf Volllast fährt. Das braucht allerdings ein wenig, kann also auch sein, dass die CPU dann erst nach dem halben ersten Durchlauf auf Volltakt fährt.
Gewinner der 6. und der 68. BlitzCodeCompo

Neue Antwort erstellen


Übersicht BlitzBasic Allgemein

Gehe zu:

Powered by phpBB © 2001 - 2006, phpBB Group