Verschachtelte If-Abfragen oder doch abbrechen?
-
Warum compiliert es keiner, um mal den ASM-Code anzuschauen?
-
volkard schrieb:
Warum compiliert es keiner, um mal den ASM-Code anzuschauen?
Weil das die Aufgabe von theliquidwave, wenn er sich dafür interessiert. Das es Compilerabhängig ist, was da produziert wird habe ich ja bereits gesagt.
-
Ich kann mir da nur zwei Szenarien vorstellen (Pseudo-Code).
Im ersten Fall:
JumpIfGreater L_IfEnd Return :L_IfEnd [...Code...]Im zweiten Fall:
JumpIfSmaller L_IfEnd [...Code...] :L_IfEnd ReturnSogesehen müsste die erste Variante schneller sein, da kein Spruch erforderlich ist. Andererseits könnte der Compiler die Situation auch anders lösen. Das ganze dürfte sich maximal um eine Operation unterscheiden. Ich gehe aber davon aus, dass der Compiler sowas selbstständig optimiert.
-
Gibt ja auch noch so spaßige Sachen wie Branch Prediction in der CPU, die man in der Praxis auch berücksichtigen muss.
Besser ist das, was leichter zu lesen ist. In Sachen Performance kann man keine allgemein gültige Aussage machen.
-
theliquidwave schrieb:
Was ist besser?
Vielleicht ist die richtige Wahl des Datentypen von x besser. Ist x unsigned, so wird "Macro-fusion" der betroffenen Befehle in der Intel CPU aktiv, was angeblich perfomanter sein soll (s. ähnliche Diskussion hier http://www.c-plusplus.net/forum/viewtopic-var-t-is-252326-and-postdays-is-0-and-postorder-is-asc-and-start-is-60.html).
D.h. so was wäre wahrscheinlich besser:unsigned int x; ... if (x != 0u) { ... } }Also alles unsigned, auch die Null (0u und nicht einfach 0)...
Ich habe es natürlich so nicht getestet und es könnte sein, dass es alles Quatsch ist.
-
#include <iostream> #include <ctime> int x=std::time(0); void bla() { #if 1 if (x < 1) { return; } std::cout<<""; #else if (x > 0) { std::cout<<""; } #endif } int main() { bla(); }gcc version 4.4.0 (GCC)
g++ -O3 -march=native --save-temps main.cppcall ___main movl _x, %eax testl %eax, %eax jle L6 movl $0, 8(%esp) movl $LC0, 4(%esp) movl $__ZSt4cout, (%esp) call __ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i L6: xorl %eax, %eax leave retgegen
call ___main movl _x, %eax testl %eax, %eax jle L6 movl $0, 8(%esp) movl $LC0, 4(%esp) movl $__ZSt4cout, (%esp) call __ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i L6: xorl %eax, %eax leave ret
-
theliquidwave schrieb:
Hi.
Dass man die Optimierungen nicht bemerken wird ist mir klar, nur es interessierte mich gerade.Danke!
Gruß
Zunächst gehen wir mal durch die IDE in den Compiler. Der ist eigentlich dazu ständig solche Optimierungen durchzuführen, er wird schon wissen, ob "x >= 1" oder "x > 0" schneller sein wird. Oder ob das abbrechen am Anfang der Funktion technisch sinnvoll ist oder nicht. Das ist auf Quelltext-Ebene eher eine Frage des Aussehen (oder Unternehmensvorschiften).
Schlussendlich hängt es vom Prozessor ab auf dem die Bits laufen. Soweit ich weiß, gibt es bei den allermeisten Befehlssätzen auf den CPU allgemein ein JUMPIFN und ein JUMPIFZ also wenn ein Register negativ ist oder 0. Also x == 0 und x < 0 sind dann ein einziger Befehl. (Abgesehn von Positions des Wertes ermitteln, Wertpositions Offset relativ zur Klasse bstimmen, aus dem Speicher laden, etc..)
Eigentlich kann dir das aber egal sein, denn, wenn du eine (ungecachte) Speicheroperation durchführst ist das garantiert (selbst ohne Pipeline-Hazards) langsamer als etliche "if"-Befehle. Wie schon von anderen gesagt, solltest du solche "Kleinigkeiten" immer den Compiler überlassen, er weiß' schon was er macht.
-
Danke an Alle.
@ abc.w: Man kann's auch übertreiben
Gruß
-
abc.w schrieb:
Vielleicht ist die richtige Wahl des Datentypen von x besser. Ist x unsigned, so wird "Macro-fusion" ...
Mit unsigned wird statt jle nur je gemacht.
Dein Macro Fusion sagt, daß test lieber zu nehmen ist als cmp. Ist hier eh der Fall. Und nur kurzzeitwissen, bis Intel cmp nachgerüstet hat, nehme ich an.
-
volkard schrieb:
... Dein Macro Fusion sagt, daß test lieber zu nehmen ist als cmp. Ist hier eh der Fall....
Ich habe jetzt echt eine Weile gebraucht, um zu kapieren, warum der Compiler in deinem Testprogramm test mit jle verwendet... Eigentlich trivial, es liegt ja an den Bedingungen, einmal (x < 1) und einmal (x > 0). In diesem Fall kann man ja test verwenden, sozusagen mit Null vergleichen, und dann jle für kleiner gleich Null Abfrage. Laut Intel-Doku funktioniert Macro-fusion bei test mit allen bedingten Sprüngen. Das ist ok.
Ändert man aber in der Funktion die Bedingungen, z.B. einmal (x < 2) und einmal (x > 1), muss der Compiler cmp generieren:080486c0 <bla()>: 80486c0: 55 push %ebp 80486c1: 89 e5 mov %esp,%ebp 80486c3: 83 ec 18 sub $0x18,%esp 80486c6: 83 3d d4 a0 04 08 01 cmpl $0x1,0x804a0d4 80486cd: 7e 1c jle 80486eb <bla()+0x2b> 80486cf: c7 44 24 08 07 00 00 movl $0x7,0x8(%esp) 80486d6: 00 80486d7: c7 44 24 04 3c 89 04 movl $0x804893c,0x4(%esp) 80486de: 08 80486df: c7 04 24 40 a0 04 08 movl $0x804a040,(%esp) 80486e6: e8 c9 fe ff ff call 80485b4 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, int)@plt> 80486eb: c9 leave 80486ec: c3 retFolge: keine Macro-fusion, wegen int x

Mit unsigned Version:
80486c0 <bla()>: 80486c0: 55 push %ebp 80486c1: 89 e5 mov %esp,%ebp 80486c3: 83 ec 18 sub $0x18,%esp 80486c6: 83 3d d4 a0 04 08 01 cmpl $0x1,0x804a0d4 80486cd: 76 1c jbe 80486eb <bla()+0x2b> 80486cf: c7 44 24 08 07 00 00 movl $0x7,0x8(%esp) 80486d6: 00 80486d7: c7 44 24 04 bc 88 04 movl $0x80488bc,0x4(%esp) 80486de: 08 80486df: c7 04 24 40 a0 04 08 movl $0x804a040,(%esp) 80486e6: e8 d5 fe ff ff call 80485c0 <std::basic_ostream<char, std::char_traits<char> >& std::__ostream_insert<char, std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*, int)@plt> 80486eb: c9 leave 80486ec: c3 ret 80486ed: 8d 76 00 lea 0x0(%esi),%esiAlso cmp mit jbe, also Macro-fusion

-
abc.w schrieb:
...
Danke für die feine Erklärung.