Wird const eigentlich optimiert?



  • Hallo Leute,

    ich frage mich ob es sinnvoll ist const als schlüsselwort zu verwenden. (außer das man bei einer erneuten zuordnung ein compiliertfehler zu sehen bekommt).

    Optimiert der Compiler dann irgendwas?

    Ich habe hier oft im Forum gelesen dass so etwas nicht gern gesehen wird:

    void func(const double, const double);
    

    Dagegen hat sich bei so etwas niemand beschwert:

    void func()
    {
      const tmp = getA();
      ...
    }
    

  • Mod

    const gibt dir keine Vorteile bei der Optimierung. Es ist ein Werkzeug, um besser entwickeln zu können und damit schon viele Fehler beim Compilieren zu entdecken, die man ansonsten später mühselig mit dem Debugger suchen müsste.

    Und das mit den const-Funktionsparametern: Du bringst damit Implementierungsdetails nach außen, die niemanden interessieren und eventuell sogar verwirren.



  • const_versprechen schrieb:

    ...
    Ich habe hier oft im Forum gelesen dass so etwas nicht gern gesehen wird:

    void func(const double, const double);
    

    ...

    Hi, const macht nur im Zusammenhang mit Pointern Sinn, da bei den normalen "primitiven" Datentypen sowieso eine Übergabe "by value" stattfindet.
    Beispiel:

    void foo(double pi, double val) {
        pi = 3.14;
        val = 1.1;
    }
    
    int main(...) {
        double mypi = 1.0;
        cout << mypi << endl;
        foo(mypi, 99.99);
        cout << mypi << endl;
    }
    

    Hier wird immer 1.0 ausgegeben, da der Wert mypi nur als Wert übergeben wurde und somit die Wertzuweisung in foo keine Auswirkungen auf mypi hat. Ist das jetzt versändlicher?



  • Wenn man const-Skalare definiert und diese mit zur Compilezeit berechenbaren Werten initialisiert, dann kann u.U. diese Variable wegoptimiert werden und überall dort, wo sie auftaucht das "constant folding" weitergeführt werden.

    Beispiel:

    const double pi = 3.14159265;
    
    double sinc(double x)
    {
      if (x==0) return 1;
      x *= pi;
      return std::sin(x)/x;
    }
    

    Da hier in sinc() nur der Wert von pi benötigt wird, dieser zur Compilezeit bekannt ist, und er sich ja nicht mehr ändern kann (ohne dass man undefiniertes Verhalten hervorruft), darf der Compiler einfach die 3.14159265 an die Stelle einsetzen, wo in der sinc-Funktion pi auftaucht. Es kann deswegen gut sein, dass in der Objektdatei von der Variablen pi nichts mehr zu sehen ist.

    Man beachte, dass wegen des const s auf oberster Ebene, pi eine interne Bindung statt einer externen hat (Unterschied zu C).



  • shadow schrieb:

    ...

    Hi, mir ging es eigentlich darum ob es nicht für den compiler besser ist wenn er während der Optimierphase weiß dass das argument const ist. Damit hätte er i.d.R. mehr Möglichkeiten zu optimieren. Dachte ich zumindest.



  • shadow schrieb:

    const macht nur im Zusammenhang mit Pointern Sinn, da bei den normalen "primitiven" Datentypen sowieso eine Übergabe "by value" stattfindet.

    Besser:
    const bei Parametern mach nur bei Referenzen und Zeigern sinn. Ansonsten wird der übergebene Wert sowieso kopiert.



  • Welche kluge Kopf sagte das nochmal? "Optimiere erst, wenn es wirklich erforderlich ist. Optimierungen während der Implementation machen wenig Sinn und verkomplizieren die eigentlich simple Lösung."

    Also meiner Meinung nach machst du dir da zu viele Gedanken. Außerdem ist das dann von Compiler zu Compiler unterschiedlich. Genieße einfach die Freude am hacken und mach dir erst einen dicken Kopp' wenn gar nüx geht 🕶

    @EOutOfResources_At_Work
    Ja, natürlich nur bei Parametern 😃


  • Mod

    const_versprechen schrieb:

    Ich habe hier oft im Forum gelesen dass so etwas nicht gern gesehen wird:

    void func(const double, const double);
    

    Das liegt in diesem Fall daran, dass das const an dieser Stelle keinerlei Bedeutung hat. In aller Regel ist es keine gute Idee bedeutungslose Sachen hinzuschreiben. Es ist z.B. auch möglich

    const volatile foo();
    

    zu benutzen. Dieses const und volatile bleibt sogar Teil des Funktionstyps (im Gegensatz zur Qualifizierung von Parametern). Allerdings hat ein Ausdruck, der diese Funktion aufruft, trotzdem nur den Typ void und nicht const volatile void.



  • camper schrieb:

    Das liegt in diesem Fall daran, dass das const an dieser Stelle keinerlei Bedeutung hat.

    Doch, es hat eine Bedeutung:

    void Func(const int P)
    {
      P = 5; //Geht nicht
    }
    void Func(int P)
    {
      P = 5; //Geht
    }
    

    Rein semantisch hat es also eine Bedeutung.


  • Mod

    EOutOfResources_At_Work schrieb:

    camper schrieb:

    Das liegt in diesem Fall daran, dass das const an dieser Stelle keinerlei Bedeutung hat.

    Doch, es hat eine Bedeutung:

    void Func(const int P)
    {
      P = 5; //Geht nicht
    }
    void Func(int P)
    {
      P = 5; //Geht
    }
    

    Rein semantisch hat es also eine Bedeutung.

    Das ist eine ganz anderere Frage. Der OP hat uns eine Funktionsdeklaration gezeigt.



  • SeppJ schrieb:

    const gibt dir keine Vorteile bei der Optimierung.

    Doch, klar ermöglicht const besseres Optimieren.
    Globales Zeugs (auch static in Klassen/Funktionen) mit integralem Typ kann mit const als Comiletime-Konstante behandelt werden, ohne const nicht.



  • hustbaer schrieb:

    Doch, klar ermöglicht const besseres Optimieren.
    Globales Zeugs (auch static in Klassen/Funktionen) mit integralem Typ kann mit const als Comiletime-Konstante behandelt werden, ohne const nicht.

    👍
    Struppi bezeichnet es doch "Konstante Ausdrücke auswerten". Deshalb geht die Sache mit TMP auch...


  • Mod

    hustbaer schrieb:

    Globales Zeugs (auch static in Klassen/Funktionen) mit integralem Typ kann mit const als Comiletime-Konstante behandelt werden, ohne const nicht.

    Was genau hat das mit Optimieren zu tun?



  • Ich finde das const-Thema auch sehr verwirrend, darum nochmal ein paar Fragen:

    SeppJ schrieb:

    Und das mit den const-Funktionsparametern: Du bringst damit Implementierungsdetails nach außen, die niemanden interessieren und eventuell sogar verwirren.

    Kannst du das nochmal etwas genauer erklären? Wieso sind hier nach außen Implementierungsdetails sichtbar?

    void func(const double, const double);
    

    shadow schrieb:

    Welche kluge Kopf sagte das nochmal? "Optimiere erst, wenn es wirklich erforderlich ist. Optimierungen während der Implementation machen wenig Sinn und verkomplizieren die eigentlich simple Lösung."

    Den Satz halte ich persönlich für unsinnig. Wieso sollte ich mir die Arbeit machen im Nachhinein den Code nochmal optimieren zu müssen. Man ist dann teilweise nicht mehr im Thema drin, muss sich neu einarbeiten. Und jeder, der schonmal in seinem Projekt nachträglich mit const hantiert hat, wird die Erfahrung gamacht haben, dass plötzlich an 100 anderen Stellen ebenfalls const nötig wurde. Ich will aus Prinzip immer die optimale Lösung auch wenn es keinen wirklichen Performancevorteil verspricht ;).

    EOutOfResources_At_Work schrieb:

    Besser:
    const bei Parametern mach nur bei Referenzen und Zeigern sinn. Ansonsten wird der übergebene Wert sowieso kopiert.

    Ich nehme mal an das gilt auch für die Rückgabetypen? Oder ist das const hier unsinnig.

    const Object* MyFunc();
    const Object& MyFunc();
    

    Und sollte man (wie z.B. bei Qt) Getter-funktionen immer als const definieren?

    QSize minimumSizeHint() const;
    int heightForWidth(int) const;
    


  • StellerFragen schrieb:

    SeppJ schrieb:

    Und das mit den const-Funktionsparametern: Du bringst damit Implementierungsdetails nach außen, die niemanden interessieren und eventuell sogar verwirren.

    Kannst du das nochmal etwas genauer erklären? Wieso sind hier nach außen Implementierungsdetails sichtbar?

    void func(const double, const double);
    

    Ich vermute, SeppJ meint, dass hier gezeigt wird, dass ich intern die Parameter nicht verändere. Genau weiß ich es aber auch nicht.

    StellerFragen schrieb:

    shadow schrieb:

    Welche kluge Kopf sagte das nochmal? "Optimiere erst, wenn es wirklich erforderlich ist. Optimierungen während der Implementation machen wenig Sinn und verkomplizieren die eigentlich simple Lösung."

    Den Satz halte ich persönlich für unsinnig. Wieso sollte ich mir die Arbeit machen im Nachhinein den Code nochmal optimieren zu müssen. Man ist dann teilweise nicht mehr im Thema drin, muss sich neu einarbeiten. Und jeder, der schonmal in seinem Projekt nachträglich mit const hantiert hat, wird die Erfahrung gamacht haben, dass plötzlich an 100 anderen Stellen ebenfalls const nötig wurde. Ich will aus Prinzip immer die optimale Lösung auch wenn es keinen wirklichen Performancevorteil verspricht ;).

    const ist ja auch nicht unbedingt eine Optimierung, sondern fällt eher unter "Korrektheit". Ich würde immer versuchen, so korrekt wie möglich zu programmieren, dazu gehört auch const.

    StellerFragen schrieb:

    EOutOfResources_At_Work schrieb:

    Besser:
    const bei Parametern mach nur bei Referenzen und Zeigern sinn. Ansonsten wird der übergebene Wert sowieso kopiert.

    Ich nehme mal an das gilt auch für die Rückgabetypen? Oder ist das const hier unsinnig.

    const Object* MyFunc();
    const Object& MyFunc();
    

    Und sollte man (wie z.B. bei Qt) Getter-funktionen immer als const definieren?

    QSize minimumSizeHint() const;
    int heightForWidth(int) const;
    

    Hier würde ich beide Methoden const machen. Die Entscheidung, ob eine Methode const ist, sollte nicht davon abhängen, ob die Methode etwas verändert, sondern ob es ein logisches const ausdrückt.


  • Mod

    StellerFragen schrieb:

    Kannst du das nochmal etwas genauer erklären? Wieso sind hier nach außen Implementierungsdetails sichtbar?

    void func(const double, const double);
    

    Weil es dem Aufrufer der Funktion völlig egal sein kann, was du mit deinen Kopien machst.

    shadow schrieb:

    Welche kluge Kopf sagte das nochmal? "Optimiere erst, wenn es wirklich erforderlich ist. Optimierungen während der Implementation machen wenig Sinn und verkomplizieren die eigentlich simple Lösung."

    Den Satz halte ich persönlich für unsinnig. Wieso sollte ich mir die Arbeit machen im Nachhinein den Code nochmal optimieren zu müssen. Man ist dann teilweise nicht mehr im Thema drin, muss sich neu einarbeiten. Und jeder, der schonmal in seinem Projekt nachträglich mit const hantiert hat, wird die Erfahrung gamacht haben, dass plötzlich an 100 anderen Stellen ebenfalls const nötig wurde. Ich will aus Prinzip immer die optimale Lösung auch wenn es keinen wirklichen Performancevorteil verspricht ;).

    So denken alle Programmierer am Anfang. Und später mit mehr Erfahrung sagen sie dann, dass die Warnungen gegen die frühzeitige Optimierung vollkommen berechtigt sind. Tja, irgendwie wirken diese Warnungen nicht, anscheinend muss jeder erst einmal selbst diese Erfahrungen machen.

    EOutOfResources_At_Work schrieb:

    Besser:
    const bei Parametern mach nur bei Referenzen und Zeigern sinn. Ansonsten wird der übergebene Wert sowieso kopiert.

    Ich nehme mal an das gilt auch für die Rückgabetypen? Oder ist das const hier unsinnig.

    const Object* MyFunc();
    const Object& MyFunc();
    

    Diese consts sind nicht unsinnig und machen einen echten Unterschied, wie man die Funktion benutzen kann.

    Und sollte man (wie z.B. bei Qt) Getter-funktionen immer als const definieren?

    QSize minimumSizeHint() const;
    int heightForWidth(int) const;
    

    Das nenn man const-correctness. Das fällt unter:

    SeppJ schrieb:

    [const] ist ein Werkzeug, um besser entwickeln zu können und damit schon viele Fehler beim Compilieren zu entdecken, die man ansonsten später mühselig mit dem Debugger suchen müsste.



  • Ok, so langsam wirds klarer.

    const Object* MyFunc();
    const Object& MyFunc();
    

    Diese consts sind nicht unsinnig und machen einen echten Unterschied, wie man die Funktion benutzen kann.

    Ah, mein Fehler. Ich dachte die const beziehen sich auf die Zeiger. Stattdessen aber auf das Object. Was ich eigentlich meinte war

    Object* const MyFunc();
    Object& const MyFunc();
    

    Das sollte nun aber unsinnig sein?


  • Mod

    StellerFragen schrieb:

    Object* const MyFunc();
    Object& const MyFunc();
    

    Das sollte nun aber unsinnig sein?

    Das erste ist unsinnig, das zweite ill-formed. Das const in

    Object* const MyFunc();
    

    wird vom Compiler nicht ignoriert, man muss es also in allen Deklarationen angeben. Der Typ eines Funktionsaufrufes ist trotzdem nur Object* (und somit hat const keinen Einfluss auf die Semantik des Aufrufs und ist folglich völlig überflüssig).
    rvalues (prvalues) sind niemals cv-qualifiziert es sei denn, es handelt sich um Klassenobjekte: und das ist auch der Grund; nur wenn es sich um eine Klasse handelt, ist ein rvalue überhaupt ein Objekt, so dass const/volatile eine Rolle spielen kann.


  • Mod

    StellerFragen schrieb:

    Ah, mein Fehler. Ich dachte die const beziehen sich auf die Zeiger. Stattdessen aber auf das Object. Was ich eigentlich meinte war

    Object* const MyFunc();
    Object& const MyFunc();
    

    Das sollte nun aber unsinnig sein?

    Ja. Dein Compiler sollte dich auch irgendwie davor warnen (zumindest mit ein paar strengeren Warnungsoptionen), weil davon auszugehen ist, dass du das anders meintest.

    edit: Und das zweite ist sogar illegal, siehe campers Beitrag.



  • SeppJ schrieb:

    shadow schrieb:

    Welche kluge Kopf sagte das nochmal? "Optimiere erst, wenn es wirklich erforderlich ist. Optimierungen während der Implementation machen wenig Sinn und verkomplizieren die eigentlich simple Lösung."

    Den Satz halte ich persönlich für unsinnig. Wieso sollte ich mir die Arbeit machen im Nachhinein den Code nochmal optimieren zu müssen. Man ist dann teilweise nicht mehr im Thema drin, muss sich neu einarbeiten. Und jeder, der schonmal in seinem Projekt nachträglich mit const hantiert hat, wird die Erfahrung gamacht haben, dass plötzlich an 100 anderen Stellen ebenfalls const nötig wurde. Ich will aus Prinzip immer die optimale Lösung auch wenn es keinen wirklichen Performancevorteil verspricht ;).

    So denken alle Programmierer am Anfang. Und später mit mehr Erfahrung sagen sie dann, dass die Warnungen gegen die frühzeitige Optimierung vollkommen berechtigt sind. Tja, irgendwie wirken diese Warnungen nicht, anscheinend muss jeder erst einmal selbst diese Erfahrungen machen.

    Naja, ich denke es ist wichtig dazuzusagen von was für einer Art Optimierung man da redet. Das gemeinte Zitat von Don Knuth ("premature optimization is the root of all evil") bezieht sich im Originalkontext auf Mikrooptimierungen. Also z.B. sowas wie eine in ASM handgecodete Wurzelfunktion. Und was das angeht stimmt das natürlich. Denn mein sqrt() kann ich später problemlos durch eine schnellere Funktion ersetzen, sofern das notwendig werden sollte. Allerdings fängt Optimierung schon im high-level Design einer Software an. Das berühmte Zitat stammt aus einer Zeit lange bevor man irgendwie über sowas wie Softwaredesign nachgedacht hat. Ich würde sogar soweit gehen und behaupten dass das ein sehr viel wesentlicherer performancebestimmender Faktor sein kann. Denn wenn das grundlegende Design ineffizient ist kann ich später evtl. gar nichtmehr wirklich optimieren. Hier und da ein paar Cycles rausquetschen macht nicht den Unterschied. Und daran später was zu ändern ist bestenfalls viel Arbeit. Ich denke vor allem erfahrenere Programmierer denken grundsätzlich sehr viel über Performance nach, wenn auch auf anderer Ebene...


Anmelden zum Antworten