Wie heißt der Nachfolger ...



  • Es muss hier doch jemand wissen, ob das Anlegen eines Objektes in Abhängigkeit vom belegten Speicher in C++ in konstanter Zeit, in logarithmischer Zeit oder in welcher Zeit auch immer geschieht

    Nö wie soll er auch. Das dynamische Anlegen eines Objekts geschieht in C++ in zwei Schritten.
    1. Aufruf des passenden operator new der den erforderlichen Speicher besorgt
    2. Aufruf eines passenden Konstruktor.

    Wie im ersten Schritt der Speicher bereitgestellt wird ist nicht definiert und frei varierbar (op new kann schließlich global bzw. pro Klasse überladen werden.) Ein "new Objekt" kann auch ein Objekt auf dem Stack anlegen. Es ist so ziemlich alles möglich.

    Viele Implementationen rufen standardmäßig eine C *alloc Funktion auf. Diese Funktionen wiederum verwenden Betriebssystemspezifische Aufrufe und Allokatoren. Unter Linux wird meines Wissens nach hauptsächlich mit einem Slab-Allokator gearbeitet. Wer ganz genau wissen will, was da auf seiner Platform abgeht muss auf jeden Fall ganz schön tief graben oder alles selbst implementieren.

    Früher konnte man zumindest relativ gefahrlos behaupten, dass das dynamische Anlegen vieler *kleiner* Objekte ein Performancekiller war, da die Allokatoren für große Speicherblöcke optmiert waren (so wie man sie in der Regel in C braucht). Das gilt so aber nicht mehr unbedingt.

    Da es also in C++ nicht *eine* Strategie gibt, kann man auch nicht sagen ob diese in konstanter Zeit, in logarithmischer Zeit oder in welcher Zeit auch immer geschieht.



  • Original erstellt von HumeSikkins:
    **
    Da es also in C++ nicht *eine* Strategie gibt, kann man auch nicht sagen ob diese in konstanter Zeit, in logarithmischer Zeit oder in welcher Zeit auch immer geschieht.**

    Mit anderen Worten :

    Es gibt Dinge, bei denen man nicht weiß, ob sie schnell oder langsam sind. Entsprechend kann man da wegen fehlendem Wissen auch keine vernünftige Optimierung betreiben. Wenn man etwas für einen Compiler gut optimiert hat, dann ist es dadurch vielleicht auf einem anderen Compiler langsamer geworden.

    Somit spricht also nichts dagegen, an gewissen Punkten etwas Kompetenz an den Compiler abzutreten. Dadurch kriegt dieser auch weitere Möglichkeiten, zu optimieren.



  • Es gibt Dinge, bei denen man nicht weiß, ob sie schnell oder langsam sind

    Genau. Deshalb heißt es ja auch: nicht raten sondern messen!

    . Entsprechend kann man da wegen fehlendem Wissen auch keine vernünftige Optimierung betreiben.

    Dem Schluss kann ich nicht folgen. Man kann natürlich niemals eine genrell global gültige Optimierung erzielen. Jedes Verfahren hat seine Vor- und Nachteile. Das heißt aber doch nicht, dass ich nicht optimieren kann. Jede Optimierung ist aber immer nur in einem bestimmten Kontext gültig.

    Wenn man etwas für einen Compiler gut optimiert hat, dann ist es dadurch vielleicht auf einem anderen Compiler langsamer geworden.

    Natürlich. Das gilt aber doch immer. Wenn ich meinen Code für ein Pentium X optimiere, ist er auf einem Athlon wahrscheinlich langsamer.

    Somit spricht also nichts dagegen, an gewissen Punkten etwas Kompetenz an den Compiler abzutreten.

    Nö. Natürlich nicht. Das macht man ja auch in extrem vielen Situationen. Ich kümmere mich z.B. nicht um die optimale Registerbelegung.



  • Und trotzdem möchte ich vielleicht einige Objekte auf dem Stack ablegen können! Basta!



  • @gregor

    Du vergleichst hier auch Äpfel mit Birnen.

    Klar, weißt Du bei Java, wie Du am besten optimierst, weil Du weißt, wie Java den Speicher reserviert. Aber eigentlich weißt Du nicht, wie Java das macht sondern nur, wie der Java-Compiler von Sun das macht. In C gibts nun mal Compiler von vielen verschiedenen Herstellern. Und ob die Optimierung, die Du durchführst für ne VM vom MS (gibts die noch?) genausogut ist, wie sie auf der von Sun war, is ne andere Frage. Oder auch, ob eine VM-Implementierung auf System X mit Deiner Optimierung genausoviel anfangen kann wie die VM auf System Y kannst Du auch nicht sagen. Könnte ja sein, dass System Y schon besonders gute Systemfunktionen mitbringt, damit der Speicher nicht fragmentiert und die VM auf System Y diese Eigenschaft nutzt. Hochoptimierten Code wirst Du also auch in Java nur für ein System schreiben können.



  • Du kannst mal davon ausgehen, dass ich in Java garnicht in dem Maße optimiere. Ich lasse z.B. diverse Dinge von Klassen aus der Standardbibliothek erledigen, obwohl ich es schneller könnte.

    ...zurück zu anderen Sprachfeatures :

    Was habt ihr gegen Garbage-Collection?

    [ Dieser Beitrag wurde am 26.11.2002 um 11:02 Uhr von Gregor editiert. ]



  • Original erstellt von Gregor:
    Was habt ihr gegen Garbage-Collection?

    Ich mag nix für was bezahlen, was ich nicht brauche.



  • Original erstellt von volkard:
    **
    Ich mag nix für was bezahlen, was ich nicht brauche.**

    OK! ...was kostet Garbage-Collection? Performance? Ich denke, dass zumindest das Erzeugen von Objekten mit Garbage-Collection schneller sein sollte, als ohne. Der Grund steht weiter oben. Wann braucht man meistens Performance?

    • Nach dem Erzeugen eines Objekts?
    • Wenn ein Objekt nicht mehr gebraucht wird?
    • Wenn man den Garbage-Collector per Hand aufruft?

    Ich tendiere stark zur ersten Möglichkeit! 🙂



  • Hallo,
    ich hätte überhaupt nichts gegen einen GC in C++ solange selbiger optional ist.



  • Original erstellt von HumeSikkins:
    solange selbiger optional ist.

    Warum das? An welchen Stellen hat man denn ohne GC einen Vorteil? Es gibt doch optionale GCs für C++, oder? Ich habe gehört, dass die alle Müll sind. ...zumindest vom Performance-Standpunkt aus. Ist halt etwas Aufgesetztes, also kein Wunder. Die bringen nur etwas gegen Speicherlecks, was natürlich auch sehr gut ist. Viele Programme haben schließlich Speicherlecks. Warum braucht mein Opera z.B. gerade über 82 MB Speicher? Der Grund ist, dass ich längere Zeit gesurft habe, aber nicht, dass ich gerade viele innere Fenster oder so auf habe.



  • Original erstellt von Gregor:
    OK! ...was kostet Garbage-Collection? Performance? Ich denke, dass zumindest das Erzeugen von Objekten mit Garbage-Collection schneller sein sollte, als ohne.

    Wenn man Performance jagt, tut Freispeicher weh. Wenigstens das allokieren unzähliger kleiner Objekte. Oft wollen die aber aus der Anwendung heraus schon, daß man sie in Datenstrukturen wie Stack oder Queue stoft, und da ist es wieder leicht (und in den Standardklassen sogar schon drin), Seitenweise Speicher zu holen, den Speicher defragmentiert zu halten und so. Und wirklich billig.

    Das einzige mal, daß ich ernsthaft überlegt habe, nen eigenen Allokator zu bauen, war folgender Fall:
    Um in nem Simulator, wo ich Hunderte von Objekten simulieren wollte, ein wenig Parallelität zu bekommen, wollte ich die Methodenaufrufe Asynchon machen. Da ein Methodenaufruf ja dem Senden einer Nachricht entspricht, hab ich das wörtlich genommen, und echt Nachrichtenobjekte verschickt. Und die Nachrichtenobjekte haben ne Weckzeit drin, wann sie aktiviert werden sollen.
    Weil die Nachrichten in einer gemeinsamen PriorityQueue verwaltet werden sollen, aber unterschiedliche Größen haben, kann ich nur Zeiger auf die Nachrichten verwalten. Also lege ich alle Nachrichten im Freispeicher an. Ich hatte zwar nen Durchsatz von 100000 Nachrichten/Sekunde, aber naja, ich hatte auch das Gefühl, mit nem eigenen Nachrichten-Allokator könnte man da einiges mehr rausholen.

    Mal abwarten, wann sowas als nächstes vorkommt.
    Ansonsten meide ich einfach new für die kleinen Objekte und für die großen isse erstmal nicht so schlimm. Wenns irgend geht, auf den Heap mit dem Kram.

    Auf den neuen Rechnern ist es immer schlimmer mit schlechten Cache-Treffern. Ich überlege auch schon ne ganze weile dran, häufig benutze Objekte im Speicher nebeneinanderzulegen, aber da sträubt sich C++ ein wenig dagegen, was mich nicht perfekt glücklich macht.

    zur veranschaulichung das mit den vielen kleinen messages:

    class MessageBase
    {
    private:
        Time timeToWakeUp;
    public:
        MessageBase(Time ttwu)
            :timeToWakeUp(ttwu)
        {
        };
        MessageBase(const MessageBase &m);//forbidden
        MessageBase &operator=(const MessageBase &m);//forbidden
        virtual ~MessageBase(void)
        {
    //      cout<<"test\n";
        };
        Time getTime(void)
        {
            return timeToWakeUp;
        };
        virtual void shoot(void)=0;
        virtual void deactivate(void)=0;
        virtual SimObject *getTarget(void)=0;
        bool operator<(const MessageBase &m)
        {
            return timeToWakeUp<m.timeToWakeUp;
        };
    };
    template<class TARGET1,class TARGET2>
    class Message0:public MessageBase
    {
    private:
        TARGET1 *target;
        void(TARGET2::*function)();
    public:
        Message0(TARGET1 *t,Time ttwu,void(TARGET2::*f)())
            :target(t),
            MessageBase(ttwu),
            function(f)
        {
            target->_incCountOfMessages();
        };
        ~Message0(void)
        {
            deactivate();
        };
        void shoot(void)
        {
            if(target!=NULL)
                (target->*function)();
        };
        void deactivate(void)
        {
            if(target!=NULL)
            {
                target->_decCountOfMessages();
                target=NULL;
            };
        };
        SimObject *getTarget(void)
        {
            return target;
        };
    };
    template<class TARGET1,class TARGET2,class O1>
    class Message1:public MessageBase
    {
    private:
        TARGET1 *target;
        void(TARGET2::*function)(O1);
        O1 object1;
    public:
        Message1(TARGET1 *t,Time ttwu,void(TARGET2::*f)(O1),O1 o1)
            :target(t),
            MessageBase(ttwu),
            function(f),
            object1(o1)
        {
            target->_incCountOfMessages();
        };
        ~Message1(void)
        {
            deactivate();
        };
        void shoot(void)
        {
            if(target!=NULL)
                (target->*function)(object1);
        };
        void deactivate(void)
        {
            if(target!=NULL)
            {
                target->_decCountOfMessages();
                target=NULL;
            };
        };
        SimObject *getTarget(void)
        {
            return target;
        };
    };
    class Simulator
    {
    private:
        Time time;
        PtrHeap<MessageBase> messages;
        int count;
    public:
        Simulator(void)
        {
            count=0;
        };
        ~Simulator(void)
        {
            messages.flush();
        };
        Time getTime()
        {
            return time;
        };
        void addMessage(MessageBase *m)
        {
            messages.push(m);
    //      cout<<"+"<<++count<<'\n';
        };
        void deactivate(SimObject *o);
        void step(TimeDiff i);
    };
    void Simulator::deactivate(SimObject *o)
    {
        for(int i=0;i<messages.getSize();i++)
        {
            if(messages[i]->getTarget()==o)
                messages[i]->deactivate();
        };
    };
    void Simulator::step(TimeDiff i)
    {
        Time end=time+i;
        for(;;)
        {
            if(messages.isEmpty()) break;
            MessageBase *m=messages.peek();
            if(end<m->getTime()) break;
            messages.pop();
    //      cout<<"-"<<--count<<'\n';
            time=m->getTime();
    //      cout<<time<<'\r'<<flush;
            m->shoot();
            delete m;
        };
        time=end;
        cout<<time<<'\r'<<flush;
    };
    class SimObject  
    {
    private:
        Simulator *simulator;
        SimObject &operator=(const SimObject &o);
        int countOfMessages;
    protected:
        bool trace;
    public:
        SimObject(Simulator *sim)
            :simulator(sim)
            ,trace(false)
        {
            countOfMessages=0;
        };
        ~SimObject(void)
        {
            simulator->deactivate(this);
        };
        void _incCountOfMessages(void)
        {
            countOfMessages++;
            if(countOfMessages>100)
            {
                cout<<"countOfMessages=="<<countOfMessages<<"\n";
            };
        };
        void _decCountOfMessages(void)
        {
            countOfMessages--;
        };
        void setTrace(bool t)
        {
            trace=t;
        };
        template<class TARGET1,class TARGET2>
        void doLater(TARGET1 *t,TimeDiff dauer,void(TARGET2::*f)())
        {
            Message0<TARGET1,TARGET2> *m=
                new Message0<TARGET1,TARGET2>(t,simulator->getTime()+dauer,f);
            simulator->addMessage(m);
        };
        template<class TARGET1,class TARGET2,class O1>
        void doLater(TARGET1 *t,TimeDiff dauer,void(TARGET2::*f)(O1),O1 o1)
        {
            Message1<TARGET1,TARGET2,O1> *m=
                new Message1<TARGET1,TARGET2,O1>(t,simulator->getTime()+dauer,f,o1);
            simulator->addMessage(m);
        };
        template<class TARGET1,class TARGET2,class O1,class O2>
        void doLater(TARGET1 *t,TimeDiff dauer,void(TARGET2::*f)(O1,O2),O1 o1,O2 o2)
        {
            Message2<TARGET1,TARGET2,O1,O2> *m=
                new Message2<TARGET1,TARGET2,O1,O2>(t,simulator->getTime()+dauer,f,o1,o2);
            simulator->addMessage(m);
        };
        Time getTime(void)
        {
            return simulator->getTime();
        };
    };
    


  • Original erstellt von Gregor:
    Viele Programme haben schließlich Speicherlecks. Warum braucht mein Opera z.B. gerade über 82 MB Speicher? Der Grund ist, dass ich längere Zeit gesurft habe, aber nicht, dass ich gerade viele innere Fenster oder so auf habe.[/QB]

    Ist kein Argument für nen obligatorischen GC. Es gibt in C++ keinen Grund mehr, Speicherlöcher zu basteln.



  • Original erstellt von Gregor:
    Warum das? An welchen Stellen hat man denn ohne GC einen Vorteil?[/QB]

    Wenn ich nur kurz Speicher anlege, was damit mache, und dann sofort wegwerfe, dann schreib vorne ein new und hinten ein delete und keine Architektur mit Zwangs-GC kann genausso billig sein.
    Macht zwar nur ein paar Takte aus, aber in C++ gehörts zu den Regeln, daß man nix zahlen muß für was, was man nicht braucht.



  • Hallo,
    ganz interessant zum Thema ist auch das Topic "Will C++0X have a gc?" das im Oktober in der Newsgroup comp.std.c++ diskutiert wurde.



  • Original erstellt von kartoffelsack:
    **Templates (habt ja gesagt, die solls jetzt doch geben),
    **

    gibt es auch, aber sie müssen explizit instanziiert werden.
    Klassen und Interfaces

    [ Dieser Beitrag wurde am 26.11.2002 um 12:54 Uhr von Noesis editiert. ]



  • Noch mal zum Thema GC. Ist ein GC wirklich besser, als Smartpointer.

    Der GC muss bei jedem Objekt überprüfen, ob er es löschen darf. dazu muss er doch semtliche Referenzen überprüfen, ob diese nicht auf dieses Objekt verweisen.

    Smartpointer wissen, wann es keine Referenz mehr auf das Objekt gibt und müssen nicht danach suchen.

    Vielleicht sollte man aber auch eine mischung verwenden (oder wird es in Java schon so gelößt?): Die Referenzen sind eine Art Smartpointer, die das Objekt aber nicht selbstständig löschen, sondern nur einer Art GC mitteilen, wenn sie zerstört werden. So muss der GC nichts suchen, sondern wiß immer, wann er ein Objekt zerstören darf.



  • Original erstellt von volkard:
    **
    Wenn ich nur kurz Speicher anlege, was damit mache, und dann sofort wegwerfe, dann schreib vorne ein new und hinten ein delete und keine Architektur mit Zwangs-GC kann genausso billig sein.
    Macht zwar nur ein paar Takte aus, aber in C++ gehörts zu den Regeln, daß man nix zahlen muß für was, was man nicht braucht.**

    Verstehe ich nicht. Es ist IMHO schneller, sich mit einem vernünftigen GC Speicher für ein Objekt zu holen, als ohne. Das Löschen dauert mit GC natürlich länger, es wird aber nicht gleich gelöscht, wenn das Objekt nicht mehr benötigt wird. In Java gibt es 2 Stellen, an denen der GC aktiv wird und Speicher wieder freigibt :

    1. Wenn der Speicher voll ist.
    2. Wenn man den GC per Hand aufruft. Natürlich macht man das in der Regel an Punkten, an denen man ansonsten keine Performance benötigt.

    Natürlich ist ein GC langsamer, wenn man in einer Schleife, die oft durchlaufen wird, immer ein kleines Objekt erzeugt und kurz danach wieder zerstört. ...aber ich denke, solche Objekte sollten eh auf den Stack, oder?! 🙂 ...da hat der GC doch eher nichts verloren.



  • Was sagt ihr denn zu den "neuen Features"?

    Ich denke, folgendes gibt es in C++ so nicht, ich habe es zumindest noch nicht gesehen :

    1. Vorbedingungen und Nachbedingungen für Methoden
    2. Der Bit-Datentyp
    3. Threads als Teil der Standardbibliothek
    ...



  • 1. Vorbedingungen und Nachbedingungen für Methoden

    Gibt's meines Wissens nach nur in Eiffel. In C++ kann man dies aber im Gegensatz zu Java noch relativ einfach einbauen. Einfach Template-Method anwenden:

    class Base
    {
    public:
         void Func()
         {
              // Vorbedingungen prüfen
              do_Func();
              // Nachbedingungen prüfen
         }
    private:
         virtual void do_Func()
         {
             // Algorithmus hier
         }
    };
    
    class Derived : public Base
    {
    public:
    // öffentliches Interface in Ruhe lassen
    private:
         void do_Func()
         {
             // spezialisierter Algo hier
         }
    };
    

    Ist sicher nicht perfekt, aber auch nicht ganz schlecht.

    2. Der Bit-Datentyp

    Es gibt bitsets. Bit als Datentyp wird ist meiner Meinung nach aber wohl niemals geben. Die kleinste adressierbare Einheit in C und C++ war und ist Byte.

    3. Threads als Teil der Standardbibliothek

    Irgendeine Form von Threads wird's im C++0X wohl geben. Und wenn es nur eine Spezifikation (so wie bei den STL-Containern und Algorithmen )ist.



  • versioning?
    also z.b. debug/release spez. code ohne makros realisieren zu können.
    (zb ein compiler assert)


Anmelden zum Antworten