Vorteile von unterschiedlichen for-Schleifen: for(int a : vec), BOOST_FOREACH(a,vec), for(auto a=vec.begin();a!=vec.end(



  • Hallo,

    gibt es irgendeinen Vorteil von:

    for(int a : vec)
    {
    
    }
    

    gegenueber:

    for(auto a=vec.cbegin();a!=vec.cend();a++)
    {
    }
    

    Die erste Schleife ist weniger Schreibarbeit fuer mich, aber gibt es einen unterschied fuer den Compiler?

    🙂



  • Ja, die erste Schleife kann optimierr sein.
    Nd sie ist auf jeden Fall besser als deine, die in jedem Durchlauf cend() aufruft.



  • In der Praxis gibt es wohl nur selten einen Unterschied, da der end()-Aufruf geinlined werden kann.
    Dennoch ist range based for einfacher zu schreiben, einfacher zu lesen und somit auch einfacher zu warten.
    BOOST_FOREACH ist fuer Compiler, die kein range based for koennen.
    std::for_each ist manchmal auch eine gute Loesung.
    Rohe for-schleifen Schleifen braucht man verhaeltnismaesig selten und sollte die anderen bevorzugen.



  • Marthog schrieb:

    In der Praxis gibt es wohl nur selten einen Unterschied, da der end()-Aufruf geinlined werden kann.

    Also im Going Native 2013 Beitrag wo über Range-Based-For gesprochen wurde, wurde behauptet dass es in der Praxis eben doch einen Unterschied macht. Weil die end() Aufrufe eben oft nicht aus der Schleife rausgezogen werden können.

    (Inlining geht wohl meistens, ja. Ohne dass der Compiler erkennt dass das Ergebnis von end() Konstant ist, und die Berechnung aus der Schleife rauszieht, wird die "manuelle" Schleife aber trotzdem langsamer sein.)



  • Für die Diskussion zur Performance von Range Based For siehe hier.



  • hustbaer schrieb:

    Marthog schrieb:

    In der Praxis gibt es wohl nur selten einen Unterschied, da der end()-Aufruf geinlined werden kann.

    Also im Going Native 2013 Beitrag wo über Range-Based-For gesprochen wurde, wurde behauptet dass es in der Praxis eben doch einen Unterschied macht. Weil die end() Aufrufe eben oft nicht aus der Schleife rausgezogen werden können.

    (Inlining geht wohl meistens, ja. Ohne dass der Compiler erkennt dass das Ergebnis von end() Konstant ist, und die Berechnung aus der Schleife rauszieht, wird die "manuelle" Schleife aber trotzdem langsamer sein.)

    Wenn das so ist, dann geh da nicht mehr hin, würde ich sagen.



  • Weiß nicht, das wurde von den Typen behauptet die die Compiler schreiben, man sollte meinen die wissen sowas.



  • @volkard
    Wo hin?
    Ich hab mir den Beitrag online angesehen.

    Davon abgesehen weiss ich selbst dass es Fälle gibt wo der Compiler nicht erkennen kann dass bei end() immer das selbe rauskommt. Wenn ich deinen Beitrag richtig deute, also wenn du behaupten willst dass die "kann nicht optimiert werden" Behauptung Blödsinn ist, dann muss ich da ganz klar widersprechen.

    Beispiel:

    void Foo::Bar()
    {
        std::vector<Baz> batzen;
        for (auto it = m_baz.begin(); it != m_baz.end(); ++it)
            if (very_selective_condition(*it))
                batzen.push_back(*it);
    }
    

    Ein Compiler kann hier nur sinnvoll optimieren, wenn er beweisen kann dass m_baz nicht über den " new Handler" ( _set_new_handler() ) modifiziert werden kann. Und das wird i.A. ziemlich schwer zu beweisen sein.
    Dazu müsste er entweder beweisen dass die Adresse des Foo-Objekts mit dem gearbeitet wird nur lokal bekannt ist, oder dass im ganzen Programm kein new Handler gesetzt wird. Oder dass alle new Handler "brav" sind.

    Die erste Option (Adresse des Objekts nur lokal Bekannt) halte ich dabei noch für halbwegs realistisch. Die fällt aber auch flach sobald Foo::Bar nicht selbst inline erweitert wird, und das wird durchaus kein seltener Fall sein. Wenn man DLLs verwendet hat man den sogar andauernd.
    Die anderen Varianten halte ich für völlig utopisch.

    (OK, theoretisch wäre noch möglich dass der Compiler Code erzeugt der m_baz.end() nur neu berechnet wenn mal push_back aufgerufen wurden. Das halte ich aber auch für reichlich weit hergeholt.)



  • Ich meinte eher im Sinne von "Zeitverschwendung, zugehört zu haben", vor allem wegen

    a) Wer bei

    for(auto a=vec.cbegin();a!=vec.cend();a++)
    

    unsicher ist, schreibt schon lange

    for(auto a=vec.cbegin(),e=vec.cend();a!=e;++a)
    

    und auch ein wenig wegen

    b) Man kann meistens wohl erwarten, daß end() inline ist und höchstens einen Attribut-Zugriff macht, und das im Cache liegt, ich sehe keinen Bedarf, hier Performanceerwägungen ins Feld zu führen. So, wie ich oft kein lokales e aushebe.



  • ForWas schrieb:

    gibt es irgendeinen Vorteil von:

    for(int a : vec)
    {
    
    }
    

    gegenueber:

    for(auto a=vec.cbegin();a!=vec.cend();a++)
    {
    }
    

    ?

    Nimm einfach das, von dem du glaubst, dass das für andere sehr gut lesbar wird. Bei dem längeren for kann ein Leser nicht auf einen Blick erkennen, was da passiert. Er muss alle drei Teile überprüfen. Und dann kommt raus, dass es doch nur ein Loop über alle Elemente ist (sofern da kein break drin steht). Deswegen fand ich std::for_each auch nicht so schlecht von der Idee her. Da steht ja schon im Namen drin, wie und worüber iteriert werden soll. Nur ist das mit dem Iterator-Paar, was anzugeben ist, etwas lästig und ohne polymorphe Lambdas muss man da auch schon wieder zu viel schreiben.

    Manches an Schreibarbeit kann man sich aber mit'nem fähigen Editor sparen. Zum Beispiel bietet ein sehr einfacher Editor (gedit für *nix) ein "Snippets"-Feature an, mit dem man sich Tipparbeit sparen kann. Das Ding ist mit Python script-bar. So generiere ich auch inzwischen Include-Guards (einfach ein paar Bytes von /dev/random nehmen und die auf INCLUDE_GUARD_XXXXXXXXXXXXXXXXXXXXXXXXXX mappen, wobei X jeweils ein zufälliges Zeichen aus [A-Z,0-9] ist. In der Hinsicht unterscheiden sich deine zwei Varianten nicht besonders. Mit entsprechender Editor-Konfiguration lässt sich beides ähnlich schnell tippen.



  • volkard schrieb:

    Ich meinte eher im Sinne von "Zeitverschwendung, zugehört zu haben"

    War es ganz sicher nicht.


Anmelden zum Antworten