You aren't smarter than your compiler!



  • Hallo.

    Dieser Spruch ist wahrscheinlich der gefährlichste und unwahrste in der gesamten modernen Programmierung. Er führt dazu, dass man sich nicht mehr viele Gedanken über Low-Level-Zeug macht, sondern dem Compiler blind vertraut. So habe ich auch gedacht, bevor ich mich dann tatsächlich selbst davon hab' überzeugen wollen. Dank Compiler Explorer geht das ja heutzutage ganz einfach.

    Wie sich in unzähligen Tests herausgestellt hat, sind auch heutige C++-Compiler immer noch extrem schlecht im Optimieren und händisch ist man da noch immer besser bedient.
    Beispiel: 50% mehr Code unter MSVC, wenn man dem Compiler vertraut: https://godbolt.org/g/hZAeza

    Auch viele der vermeintlichen Zero-Overhead-Mechanismen aus C++, wie z.B. die neuen std::initializer_list s, kommen zu einem hohen Preis:
    https://godbolt.org/g/ROqzNi
    https://www.c-plusplus.net/forum/341741

    Wie seht ihr das?


  • Mod

    Blutegel schrieb:

    Wie seht ihr das?

    Wie leider so oft im Leben, wird diese Widerrede fast immer von genau den Leuten geäußert, für die der Ratschlag in erster Linie gedacht ist.



  • Hier noch eine Perle: https://godbolt.org/g/uWTVjz
    Idiomatisch wäre m.M.n. MinElement1 und vermutlich würden die meisten Programmierer in der Praxix auch diese Funktion schreiben.
    Mein Kernpunkt ist, dass ich gewisse Funktionen auf leicht unterschiedliche Arten schreiben kann und der "smarte" Compiler dennoch für alle komplett unterschiedlichen Code generiert. So eine Black-Box möchte ich schlicht nicht vertrauen. Das Zitat ist also total irreführend.

    SeppJ schrieb:

    Wie leider so oft im Leben, wird diese Widerrede fast immer von genau den Leuten geäußert, für die der Ratschlag in erster Linie gedacht ist.

    Willst du damit sagen, ich sei ein Anfänger und solle daher den Ratschlag beherzigen? Aufgrund welcher Beobachtung hast du das geschlossen?
    Inb4 "premature optimization is the root of all evil".....



  • die Frage ist was sagen die Leute auf der GCC/clang mailing lists - der einzige Weg sinnvoll Feedback zu bekommen



  • Blutegel schrieb:

    Hallo.

    Dieser Spruch ist wahrscheinlich der gefährlichste und unwahrste in der gesamten modernen Programmierung. Er führt dazu, dass man sich nicht mehr viele Gedanken über Low-Level-Zeug macht, sondern dem Compiler blind vertraut.

    Nicht vertrauen, sonder wissen was er macht. Dann kann meine Implementation dem Compiler die Optimierung vereinfachen.

    Ansonsten denke ich, dass der Compiler, insbesondere bei größeren Projekten, dem menschlichen Optimierer in der Regel überlegen ist.


  • Mod

    Blutegel schrieb:

    SeppJ schrieb:

    Wie leider so oft im Leben, wird diese Widerrede fast immer von genau den Leuten geäußert, für die der Ratschlag in erster Linie gedacht ist.

    Willst du damit sagen, ich sei ein Anfänger und solle daher den Ratschlag beherzigen? Aufgrund welcher Beobachtung hast du das geschlossen?
    Inb4 "premature optimization is the root of all evil".....

    Wenn du es unbedingt so genau wissen möchtest (ich habe es in meinem ersten Beitrag extra nicht gesagt):
    Nein, das heißt nicht, dass ich dich für einen Anfänger halte. Ein Anfänger macht sich ersten um so etwas keine großen Gedanken, oder wenn doch, dann weiß er es gut genug, um auf die Weisheit der Altvorderen zu hören.

    Die Altvorderen verstehen den Spruch und wissen, was damit gemeint ist, wo es richtig ist, darauf zu hören und wo und warum nicht - siehe z.B. Schlangenmensch.

    Und dazwischen sind die Leute, die gerade genug gelernt haben, dass sie nun meinen, alles besser zu wissen, und nun wie ein Teenager gegen alles rebellieren. Dabei gäbe es eigentlich keinen Grund dazu, ihnen fehlt bloß Weitblick und Gelassenheit.



  • Blutegel schrieb:

    Wie sich in unzähligen Tests herausgestellt hat, sind auch heutige C++-Compiler immer noch extrem schlecht im Optimieren und händisch ist man da noch immer besser bedient.
    Beispiel: 50% mehr Code unter MSVC, wenn man dem Compiler vertraut: https://godbolt.org/g/hZAeza

    Deine beiden Funktionen machen nicht das selbe, daher generiert der Compiler unterschiedlichen Code.

    float f = 0.f;
    	f = -f;
    	// f is now negative zero
    	vec v = { f, f, f };
    
    	std::cout << "VerticalSum1: " << VerticalSum1(v) << "\n";
    	std::cout << "VerticalSum2: " << VerticalSum2(v) << "\n";
    

    =>

    VerticalSum1: 0
    VerticalSum2: -0
    

    Wenn du willst dass für beide Funktionen equivalenter Code generiert wird, dann gib /fp:fast mit (MSVC) oder definiere beide Funktionen in einer Region ala

    #pragma float_control(push)
    #pragma float_control(precise, off)
    #pragma float_control(except, off)
    
    // <code>
    
    #pragma float_control(pop)
    

    Dann bekommst du als Ergebnis 2x "-0", weil der Compiler dann auf strikte IEEE-Konformität scheisst.

    ----

    Man sieht also: es ist wieder mal der Compiler schlauer als der der behauptet schlauer als der Compiler zu sein. Weil der Compiler weiss was er hier zu tun hat, und Code generiert der es auch tut.



  • hustbaer schrieb:

    ...

    Ok, daran habe ich nicht gedacht. Du hast Recht.

    hustbaer schrieb:

    Man sieht also: es ist wieder mal der Compiler schlauer als der der behauptet schlauer als der Compiler zu sein. Weil der Compiler weiss was er hier zu tun hat, und Code generiert der es auch tut.

    Und die beiden anderen, viel einschneidenderen Fälle gekonnt ignoriert, um ein voreiliges Fazit ziehen zu können?



  • Ein Compiler ist nicht vom einem Computer geschrieben sondern von sehr smarten Menschen 😉



  • Nett von Dir ... Ich schreibe nämlich gerade einen 🙂



  • Blutegel schrieb:

    hustbaer schrieb:

    Man sieht also: es ist wieder mal der Compiler schlauer als der der behauptet schlauer als der Compiler zu sein. Weil der Compiler weiss was er hier zu tun hat, und Code generiert der es auch tut.

    Und die beiden anderen, viel einschneidenderen Fälle gekonnt ignoriert, um ein voreiliges Fazit ziehen zu können?

    Kein voreiliges Fazit. Ich beziehe mich auf den einen Fall. Und der demonstriert dass du Beispiele bringst die du nicht verstehst bzw. nicht ausreichend beurteilen kannst (bzw. genauer: zumindest ein solches Beispiel).

    Das heisst nicht dass du in den anderen Fällen nicht trotzdem Recht haben kannst - ich treffe darüber einfach keine Aussage. Unter anderem weil ich mir die anderen Fälle nicht genauer angesehen habe.

    ----

    Davon abgesehen: Grundsätzlich stimmt es natürlich dass Compiler oft für equivalenten C++ Code ganz unterschiedlichen Maschinencode generieren - der auch ganz unterschiedlich schnell sein kann. Weil kein Compiler einen allmächtigen Optimizer hat. Dummerweise kann sich das allerdings auch mit jeder Compiler-Version/Revision ändern -- also welche Variante schneller ist. Wenn du Beispiele findest die bei ganz unterschiedlichen Compilern alle gleich ausgehen, dann kann man vermutlich wirklich darauf schliessen dass der eine C++ Code vermutlich leichter zu optimieren ist als der andere. Und ggf. dass ein bestimmtes Feature momentan "nicht gratis" ist, auch wenn es das theoretisch sein sollte bzw. sein könnte.

    Wichtig sind allerdings die Schlüsse die man daraus zieht. Und die sollten nicht "ich kann das besser" sein, denn damit kann man schnell gewaltig auf die Nase fliegen. Wenn man allerdings Code hat wo es auf das letzte Bisschen Performance ankommt, dann sollte man Profilen und/oder den generierten Code verschiedener Varianten inspizieren. Bzw. wenn man sehr erfahren und sehr mutig ist, dann kann man auch mal an Stellen die wichtig aber nicht SO wichtig sind was hinschreiben wo man einfach schätzt dass die Performance besser sein wird. Die Erfahrung zeigt aber dass man sich damit eben sehr schnell verschätzt.

    Und die Erfahrung zeigt weiters, dass Leuten die über das hier von dir aufgeworfene Thema mit einer bestimmten Ausdrucksweise diskutieren, eher nicht zu den Leuten gehören die das ganze wirklich gut und sachlich beurteilen können.



  • Ich hb schon Ende der 80iger den Code von TurboC optimiert. Optimierung ist halt so ein Ding. Entweder man kann es oder eben nicht.
    Die ganze Geschichte ist aber schon ca. 30 Jahre her.
    Assembler-output ausgeben lassen und dann die Register ändern und ähnliche Geschichten ...



  • Bloss gut dass sich in den letzten Jahren bei den Optimizern so-gut-wie nichts getan hat.



  • Blutegel schrieb:

    Hallo.

    Dieser Spruch ist wahrscheinlich der gefährlichste und unwahrste in der gesamten modernen Programmierung. Er führt dazu, dass man sich nicht mehr viele Gedanken über Low-Level-Zeug macht, sondern dem Compiler blind vertraut. So habe ich auch gedacht, bevor ich mich dann tatsächlich selbst davon hab' überzeugen wollen. Dank Compiler Explorer geht das ja heutzutage ganz einfach.

    Wie sich in unzähligen Tests herausgestellt hat, sind auch heutige C++-Compiler immer noch extrem schlecht im Optimieren und händisch ist man da noch immer besser bedient.
    Beispiel: 50% mehr Code unter MSVC, wenn man dem Compiler vertraut: https://godbolt.org/g/hZAeza

    Auch viele der vermeintlichen Zero-Overhead-Mechanismen aus C++, wie z.B. die neuen std::initializer_list s, kommen zu einem hohen Preis:
    https://godbolt.org/g/ROqzNi
    https://www.c-plusplus.net/forum/341741

    Wie seht ihr das?

    Für 99.99% des Codes der täglich geschreiben wird ist das total irrelevant. Bei weit über 50% des Codes ist es sogar egal, ob der beste Algorithmus oder die besten Datenstrukturen verwendet werden. Die Regel, dass 80% der Zeit in 20% des Codes verbraten werden passt relativ gut.

    Wenn du in einem Bewerbungsgespräch sagst, dass du schnellen Code gegenüber lesbarem Code bevorzugst, bist du bei den meisten schon raus.



  • Ich interpretiere den Spruch für mich ganz anders und schreibe deswegen meinen Code unter der folgenden Priorisierung :

    1. fail-safe/robust Code
    2. lesbarer Code

    --- falls Code zu langsam
    3) Optimierung des Algorithmus
    *es bringt meist viel mehr, sich die Komplexität des Algorithmus anzuschauen und dort anzusetzen. "binary vs linear search" bringe ich als Beispiel mal an
    *
    --- falls Code immernoch zu langsam
    4) profile --> disassemble --> optimize

    In den Schritten 1-3 vertraue ich dem Compiler blind ...
    An Punkt 4 bin ich in den letzten Jahren noch nicht angelangt, meist war eher die Wahl des Algorithmus total für die Tonne ...


  • Mod

    -Emil- schrieb:

    An Punkt 4 bin ich in den letzten Jahren noch nicht angelangt, meist war eher die Wahl des Algorithmus total für die Tonne ...

    Das sehe ich ähnlich. Optimierung auf Profiler Ebene ist erst notwendig, wenn man die Grenzen seiner Maschine ausloten will. Viele sollten erstmal den Ansatz optimieren, dort kann man ganze Komplexitätsklassen rausholen.



  • -Emil- schrieb:

    [i]es bringt meist viel mehr, sich die Komplexität des Algorithmus anzuschauen und dort anzusetzen. "binary vs linear search" bringe ich als Beispiel mal an

    Wobei nicht gesagt ist dass binary immer schneller ist. 😉


Anmelden zum Antworten