Container loopen



  • Heiho,

    Oft kommt es vor das ich durch ein container loopen muss, zb nem vector
    ich benutz zwei varianten, je nach bedarf

    for(std::size_t i = 0; i < vec.size(); ++i)
        vec[i]
    

    oder die laengere variante wenn mir der index egal ist

    for(std::vector<DATA>::iterator iter = vec.begin(); iter != vec.end(); ++iter)
        *iter;
    

    Sofern es moeglich ist nehm ich algos, wie sort, for_each usw,

    was ich mir aber zZt frag
    wenn ich den index i nicht brauch, ist es dann performanter den iterator zu nehmen statt [i] oder at(i) ?

    wenn ich den index brauch ist es dann besser std::size_t,
    oder int (dann size() casten auf int)
    oder vector::size_type zu nehmen ?



  • Theoretisch ist die Iteratorvariante schneller, weil sie bei Vector auf Zeigerarithmetik hinauslaufen kann, sprich pro Durchlauf wird ein Zeiger um sizeof(Element) Bytes weitergeführt. Bei der Indexvariante muss in jedem Durchlauf Startoffset + Anzahl * sizeof(Element) berechnet werden, also kommt noch eine Multiplikation hinzu. Bei mehreren Zugriffen auf das Element summiert sich das natürlich auch noch, während der Iterator nur einmal berechnet werden muss.



  • Hi,

    Mr Evil schrieb:

    wenn ich den index i nicht brauch, ist es dann performanter den iterator zu nehmen statt [i] oder at(i) ?

    Ich glaub das schenkts sich nicht viel, hatten wir schon einige mal hier im Forum. Wobei at() durch die Index-Überprüfung ehe rausfällt. Also bleibt nur iter und [i] übrig. Da iterator bei vector als einfacher Zeiger implementiert sein sollte, dürfte es auf selebe hinauslaufen. Ein kleiner Test würde aber natürlich hier Gewissheit schaffen.

    Mr Evil schrieb:

    wenn ich den index brauch ist es dann besser std::size_t,
    oder int (dann size() casten auf int)
    oder vector::size_type zu nehmen ?

    Casten brauchst du hier IMHO nicht. Würde dennoch vector::size_type, da dieser Typ ja genau dafür gedacht ist.



  • Mr Evil schrieb:

    ...
    wenn ich den index i nicht brauch, ist es dann performanter den iterator zu nehmen statt [i] oder at(i) ?
    ...

    siehe:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-179278-and-highlight-is-.html

    at(i) ist per se die langsamste variante, weil IMMER eine index-überprüfung stattfindet



  • - at(i) ist nicht so performant wie [i], da at() immer noch die Gueltigkeit ueberpruefen muss, op[] hingegen nicht.
    - bei vector sollten die index-methode und die iterator-methode in etwa gleich performant sein, da vector-iteratoren oft im Sinne von pointern implementiert werden
    - wenn du schon auf Performanz achten willst, dann lass nicht vor jedem Schleifendurchlauf vec.end() aufrufen (vorausgesetzt du fuegst dem vector keine Elemente hinzu oder loeschst sie):

    std::vector<DATA>::iterator vec_end = vec.end();
    for(std::vector<DATA>::iterator iter = vec.begin(); iter != vec_end; ++iter)
        *iter;
    

    - welchen integer-typ du benutzt duerfte relativ egal sein, performancetechnisch

    - BEVOR du dir irgendwelche gedanken ueber Performance machst, schau auf Korrektheit, Leserlichkeit und Wartbarkeit deines Codes. Wenn das alles gegeben ist, kuemmer dich nicht von Hand um die performance, sondern lass dir von einem Profiler sagen, wo du was verbessern kannst/musst. Der kann das tausendmal besser als deine Intuition, und er sieht auch die Optimierungen die der Compiler macht und von denen du nichtmal ansatzweise traeumen kannst 😉

    Fazit: machs nicht absichtlich schlechter, aber halte dich nicht mit Performance-Feinheiten auf, bevor du nicht 100%ig sicher bist, dass du es gerade an der Stelle musst.



  • Performancegewinn hat man meist eh bei Algorithmen. Zum Beispiel durch den Verzicht auf sqrt indem man mit den noch quadrierten Werten rechnet. Oder die richtige Reihenfolge bei verschachtelten Schleifen entsprechend der Speicherstruktur etc.



  • std::vector<DATA>::iterator vec_end = vec.end();
    for(std::vector<DATA>::iterator iter = vec.begin(); iter != vec_end; ++iter)
        *iter;
    

    Nur als Zusatz, wegen Schreibarbeit und so:

    for(std::vector<DATA>::iterator iter=vec.begin(), end=vec.end(); iter != end; ++iter )
        *iter;
    


  • http://boost.org/doc/html/foreach.html 🙂 Sollte gleich schnell sein wie die iterator-Variante, ist aber wesentlich weniger Schreibaufwand (und sieht IMO besser aus 🙂 )



  • Warum nicht std::for_each?



  • Also Iterator ist wie hier schon angesprochen so gut wie immer schneller als direkter Zugriff per Index-Operator.

    Dann noch mit Standardalgorithmen kombinieren, und es rockt das haus 😉

    #include <functional>
    #include <iostream>
    #include <vector>
    #include <algorithm>
    
    struct bar
    {
        bar()
            : data(0)
        {}
    
        void foo(const std::size_t value) { std::cout << "data = " << value << " (old: " << data << ");" << std::endl; data = value; }
    
    private:
        std::size_t data;
    };
    
    int main()
    {
        std::vector<bar> data(10);
    
        /* operator[] => Indexoperator */
        for (std::size_t i(0); i < data.size(); ++i)
            data.foo(2);
    
        /* begin(), end() => Iterator */
        const std::vector<bar>::iterator it_end(data.end());
        for (std::vector<bar>::iterator it(data.begin()); it != it_end; ++it)
            it->foo(3);
    
        /* for_each => Algorithm | Iterator */
        std::for_each(data.begin(), data.end(), std::bind2nd(std::mem_fun_ref(&bar::foo), 2));
    }
    

    ... Letztes gewinnt! ^^ Naja und in manchen Fällen ist es sogar nur per Iterator anständig möglich (std::list) oder einfach viel einfacher (std::ifstream_iterator usw.) ...



  • danke fuer die meinungen

    - wegen dem casten, das wurde hier von jemanden missverstanden, wenn ich
    for(int i...
    mache, muss ich vec.size() nach int casten, darum lass ich das meistens

    - das for_each usw schneller sind, also generell algos zu bevorzugen sind weiss ich, hab cih im start post auch schon geschrieben, mir gehts nur um die faellt wo ein also nicht moeglich ist



  • Na, damit du bei der for-Schleife nicht casten mußt, solltest du dann halt den richtigen Typ nehmen:

    for(std::size_t i=0; i<vec.size(); i++)
    


  • ich weiss, das mach ich ja auch, nur manchmal brauch ich den index i auch fuer andere zwecke, da ist es dann einfacher size nach int zu casten als das size_t jedesmal nach int casten zu muessen

    ich vermeide das aber so gut wie geht {also casts ueberhaupt}



  • man braucht selten wirklich +/- -Zahlenbereiche ... meist reicht + oder -.



  • Na, damit du bei der for-Schleife nicht casten mußt, solltest du dann halt den richtigen Typ nehmen

    Ähm der richtige wäre:

    typedef std::vector<int> IntArray;
    IntArray vec;
    for(IntArray::size_type i=0; i<vec.size(); i++)
    


  • mal so ne Frage nebenbei:
    1. funzt std::mem_fun_ref auch virtuellen methoden?
    2. funzt das mit dem for_each auch bei Pointern?

    class bar{
        bar() {}
        virtual void foo1() = 0;
        virtual void foo2() { std::cout << "bar::foo2"  << std::endl;  }
    };
    
    class bar2 : public bar{
        bar2() {}
        void foo1() { std::cout << "bar2::foo1"  << std::endl;  }
        void foo2() { std::cout << "bar2::foo2"  << std::endl;  }
    };
    
    class bar3 : public bar{
        bar3() {}
        void foo1() { std::cout << "bar3::foo1"  << std::endl;  }
        void foo2() { std::cout << "bar3::foo2"  << std::endl;  }
    };
    
    int main()
    {
        std::vector<bar*> data(4);
        data[0] = new bar2;
        data[1] = new bar2;
        data[2] = new bar3;
        data[3] = new bar3;
    
        /* for_each => Algorithm | Iterator */
       //wie sähe hier der for_each Befehl aus? einmal für foo1 und einmal für foo2    
       std::for_each( ??? )
    }
    

Anmelden zum Antworten