Benchmark: Compiler optimiert komplette Funktion weg



  • Erstmal danke für die Antworten!

    @CStoll: Optimierung abschalten ist ausgeschlossen. Will saubere Messwerte und keine Verfälschungen durch den Debugger.

    @rapso: den Reuturnwert ausgeben ist deshalb keine gute Idee, weil dann die Zeit für die Ausgabe mitgemessen wird, was ich nicht möchte



  • Dann machst du halt etwas anderes mit dem Rückgabewert, was möglichst bleibende Auswirkungen hat. Wenn du die Funktion nur mit "arithmeticSimple();" aufrufst, erkennt der Compiler, daß sie (a) keine internen Nebenwirkungen hat und (b) du mit ihrem Rückgabewert auch nichts anfangen willst. (a) könntest du umgehen, indem du die Funktion in eine eigene Übersetzungseinheit auslagerst, (b) indem du etwas mit dem Rückgabewert machst:

    clock_start();
    double result = arithmetikSimple();
    clock_stop();
    cout<<result;
    

    (aber damit hat der Compiler immer noch die Chance, die for()-Schleifen in der Funktion wegzuoptimieren - und alles auf ein simples "return ((iterations-1)+(iterations-1))*2.5;" zu kürzen)



  • Gill Bates schrieb:

    @rapso: den Reuturnwert ausgeben ist deshalb keine gute Idee, weil dann die Zeit für die Ausgabe mitgemessen wird, was ich nicht möchte

    Aufgrund dieser Aussage, hoffe ich, dass du weisst, dass deine benchmarkergebnisse vermutlich nur benchmarken werden wie gut du programmieren kannst (in der jeweiligen sprache), nicht wie faehig die sprachen/compiler sind.



  • CStoll schrieb:

    Dann machst du halt etwas anderes mit dem Rückgabewert, was möglichst bleibende Auswirkungen hat. Wenn du die Funktion nur mit "arithmeticSimple();" aufrufst, erkennt der Compiler, daß sie (a) keine internen Nebenwirkungen hat und (b) du mit ihrem Rückgabewert auch nichts anfangen willst. (a) könntest du umgehen, indem du die Funktion in eine eigene Übersetzungseinheit auslagerst, (b) indem du etwas mit dem Rückgabewert machst:

    clock_start();
    double result = arithmetikSimple();
    clock_stop();
    cout<<result;
    

    (aber damit hat der Compiler immer noch die Chance, die for()-Schleifen in der Funktion wegzuoptimieren - und alles auf ein simples "return ((iterations-1)+(iterations-1))*2.5;" zu kürzen)

    Genau das ist der Fall. Natürlich mache ich was mit dem letzten Rückgabewert. Ist dem Compiler aber Schnuppe und er schmeisst die Schleife weg.

    Was meinst du mit "eigene Übersetzungseinheit"? 😕



  • Gill Bates schrieb:

    CStoll schrieb:

    Dann machst du halt etwas anderes mit dem Rückgabewert, was möglichst bleibende Auswirkungen hat. Wenn du die Funktion nur mit "arithmeticSimple();" aufrufst, erkennt der Compiler, daß sie (a) keine internen Nebenwirkungen hat und (b) du mit ihrem Rückgabewert auch nichts anfangen willst. (a) könntest du umgehen, indem du die Funktion in eine eigene Übersetzungseinheit auslagerst, (b) indem du etwas mit dem Rückgabewert machst:

    clock_start();
    double result = arithmetikSimple();
    clock_stop();
    cout<<result;
    

    (aber damit hat der Compiler immer noch die Chance, die for()-Schleifen in der Funktion wegzuoptimieren - und alles auf ein simples "return ((iterations-1)+(iterations-1))*2.5;" zu kürzen)

    Genau das ist der Fall. Natürlich mache ich was mit dem letzten Rückgabewert. Ist dem Compiler aber Schnuppe und er schmeisst die Schleife weg.

    Was meinst du mit "eigene Übersetzungseinheit"? 😕

    Hoer auf CStoll und mach in der Schleife ein += und er kann die Schleife nicht wegoptimieren und gut ist. Und eine eigene Uebersetzungseinheit waer ein eigenes *.cpp-File. Aber dann wird der Compiler die innere Schleife immer noch wegschmeissen



  • += habe ich auch schon versucht. Bringt auch nix...

    rapso schrieb:

    Gill Bates schrieb:

    @rapso: den Reuturnwert ausgeben ist deshalb keine gute Idee, weil dann die Zeit für die Ausgabe mitgemessen wird, was ich nicht möchte

    Aufgrund dieser Aussage, hoffe ich, dass du weisst, dass deine benchmarkergebnisse vermutlich nur benchmarken werden wie gut du programmieren kannst (in der jeweiligen sprache), nicht wie faehig die sprachen/compiler sind.

    Das stimmt nicht so ganz, zumal ich die VC++-Version als nativen Code übersetze. (Hätte ich natürlich auch vorher schon erwähnen können... 🙄 )



  • Gill Bates schrieb:

    Das stimmt nicht so ganz, zumal ich die VC++-Version als nativen Code übersetze.

    ich glaube du missverstehst mich. Ich denke dass du (noch) zu wenig wissen von compilern und codeoptimierung hast, um das beste aus den sprachen raus zu kratzen. unter dieser voraussetzung ist es reiner zufall was dabei rauskommt.

    das ist nichts gegen dich ;).
    damit ich aber nicht nur auf dir einhacke, waere mein tip, dass du dir highlevel optimierungen anschaust. statt also zu hoffen dass ein compiler code generiert der 50% schneller ist als ein anderer, kannst du dir algorithmen einverleiben die oft 10, 100 oder 1000mal schnellere programme machen.
    falls du deinen benchmark machst um rauszufinden was du verwenden sollst, gehst du da ebenfalls nicht gut dran. eine sprache hat wichtigere kriterien als geschwindigkeit, schliesslich programmieren wir alle auch nicht 100% in assembler.



  • Gill Bates schrieb:

    += habe ich auch schon versucht. Bringt auch nix...

    Das glaub ich dir nicht. Wie schaut der Code aus, den du dann kompilierst hast?



  • Gill Bates schrieb:

    Hat jemand ne brauchbare Idee, wie man den Compiler daran hindern kann, obige Funktion als DeadCode anzusehen?

    Übergib den Startwert als Parameter.
    Hol dir den Startwert und die Anzahl der Iterationen von der Kommandozeile.
    Gib das Ergebnis aus (std::cout oder so).

    EDIT: du musst die Schleife natürlich etwas modifizieren, so dass auch wirklich alle Durchläufe gebraucht werden. Sonst kann der Compiler immer noch erkennen dass nur der Letzte Durchlauf relevant ist. Einfache Möglichkeit: Bilde ne Summe oder sowas. /EDIT

    Dadurch sorgst du dafür dass es 1) viel schwerer für den Compiler wird irgendwas zu optimieren und 2) der Compiler nicht einfach beschliessen kann "wert brauch ich sowieso nicht -> rechne ich garnicht erst aus".



  • schreib 'volatile' vor die variable, dann darf der compiler die nicht wegoptimieren und code, der mit der variablen arbeitet, auch nicht.
    🙂



  • unberechenbar schrieb:

    schreib 'volatile' vor die variable, dann darf der compiler die nicht wegoptimieren und code, der mit der variablen arbeitet, auch nicht.
    🙂

    und viele Optimierungen werden kaputt gemacht, womit der Vergleich schon wieder sinnlos ist 😉



  • Ich denke nen "sinnvollen" Benchmark könnte man mit etwas bekommen wo man sicher weiss dass der Compiler es nicht optimieren kann.
    z.B. eine Iterations-Schleife eines Fraktals ala Apfelmännchen (das Mandelbrot Dings halt).
    Und natürlich wie ich schon geschrieben habe dafür sorgen dass der Input zur Compile-Zeit nicht bekannt ist und der Output auch irgendwo rausgeschrieben wird, damit nicht gleich die ganze Funktion wegoptimiert werden muss.

    Dann kann der Compiler auch nichtmehr schummeln, auch ganz ohne volatile.



  • hustbaer schrieb:

    Dann kann der Compiler auch nichtmehr schummeln, auch ganz ohne volatile.

    das nenn man "Die beste Optimierung", am schnellsten ist immer noch das, was man erst garnicht machen muss. Die frage ist also eher wieso der c# compiler sowas nicht optimiert...



  • Irgendwie verstehe ich das ganz nicht. Er will testen, welcher Compiler besser optimierten Code ausspuckt. Aber er will bestimmte optimierungen dann doch wiederum verhindern? Wieso ausgerechnet die optimierung, die die Schleife weglässt? Wieso nicht auch andere optimierungen? Das ist doch Sinnlos.

    Wenn ich benchmarke, dann will ich was praxisnahes, was auch etwas über real existierende Probleme aussagt. Ich mach mir doch nicht vorher gedanken darüber, wie ich den Code absichtlich langsamer bekomme. Allein dadurch ist dem ganzen doch schon jegliche Aussagekraft geraubt worden. 😕



  • Er hat ja nur Angst, das der C++-Compiler besser ist. 😃



  • @Helium:
    Das ist ja nur ein erster einfacher Test. Daneben wird die Performance von Arrays, HashTables, Stringoperationen bis hin zu Datenbankanbindung und Performance von GUI-Controls (MFC, WinForms, Qt-Widgets) getestet.

    Übrigens hab ich's doch noch hinbekommen. Das += hat beim erstenmal nur "nicht so richtig gewollt". Jetzt gibt's Sinn.

    Wen's interessiert: Bei 5 Milliarden Schleifendurchläufen hat C++Implementation nur ca. 60% der Ausführungszeit des entsprechenden C#-Programms benötigt. 😋



  • Gill Bates schrieb:

    Wen's interessiert: Bei 5 Milliarden Schleifendurchläufen hat C++Implementation nur ca. 60% der Ausführungszeit des entsprechenden C#-Programms benötigt. 😋

    Interesant wuerde es erst sein wenn du eklaeren wuerdest weshalb das so ist.



  • rapso schrieb:

    Gill Bates schrieb:

    Wen's interessiert: Bei 5 Milliarden Schleifendurchläufen hat C++Implementation nur ca. 60% der Ausführungszeit des entsprechenden C#-Programms benötigt. 😋

    Interesant wuerde es erst sein wenn du eklaeren wuerdest weshalb das so ist.

    Weil C++ die ÜBER Sprache ist und und Punkte Sp33d alles weghaxx0rt!11



  • Wen's interessiert: Bei 5 Milliarden Schleifendurchläufen hat C++Implementation nur ca. 60% der Ausführungszeit des entsprechenden C#-Programms benötigt.

    Ich hoffe, du hast die 5 Milliarden als 64-Bit-Variable angelegt, denn in eine 32-Bit-Variable (int) passen nur 4.2 Mrd verschiedene Werte...



  • speedgottlol schrieb:

    rapso schrieb:

    Gill Bates schrieb:

    Wen's interessiert: Bei 5 Milliarden Schleifendurchläufen hat C++Implementation nur ca. 60% der Ausführungszeit des entsprechenden C#-Programms benötigt. 😋

    Interesant wuerde es erst sein wenn du eklaeren wuerdest weshalb das so ist.

    Weil C++ die ÜBER Sprache ist und und Punkte Sp33d alles weghaxx0rt!11

    Naja wenn man die Entwicklungszeit vergleicht mit >20 Jahre fuer den C++ Compiler und 4 Jahre fuer den C# Compiler.


Anmelden zum Antworten