Optimieren, need help



  • Au weia, au weia, au weia. Ohne dir zu nahe treten zu wollen, aber der Code ist wirklich dreckig. Zur Performance komm ich nachher noch, aber erst mal Programmierstil allgemein - kapsel deinen verdammten Code. Du hast allein in dieser kleinen Funktion zig Stellen, an denen du den selben Code immer und immer wieder stehen hast. Du könntest die Code-Größe deutlich verringen (und auch ein bisschen speed rausholen), wenn du zum Beispiel erst auf clipping und danach auf transparente Pixel prüfst, denn so wie der Code im Moment aussieht, ist es dir, wenn clipping an ist, eh egal, ob du transparente Pixel hast. Jedenfalls ist der Code haargenau gleich. Und wenn ich das richtig sehe, ist der Code für nicht clipping und keine transparenten Pixel eigentlich auch genau der gleiche, nur dass du m_height und m_width fest gecodet hast, obwohl in diesem Fall srcBottom, srcTop usw. genau die entsprechenden Werte haben.

    Dann hast du einige völlig unnötige Zuweisungen - srcLeft und srcTop sind schon 0, das musst du ihnen nicht mehr zuweisen. Eigentlich kannst du srcRight und srcBottom auch gleich entsprechend initialisieren.

    Ansonsten - memcpy benutzen.

    Ohne das jetzt groß getestet zu haben (Ich hab im Moment keinen Compiler zur Hand, und den Rest deines Codes kenn ich ja auch nicht...), ich denke, das hier sollte das selbe tun wie deine Methode, und wenn nicht, sollte zumindest die Idee klar werden, so dass du was damit anfangen kannst.

    void CBitmap::drawNormal (int x, int y, unsigned short *dest)
    {
        assert (m_buffer);
    
        unsigned short *source = m_hasTransparentPixels ? m_rleBuffer : (m_buffer + srcLeft + srcTop * m_width);
    
        int srcLeft   = x < 0 ? -x : 0;
        int srcTop    = y < 0 ? -y : 0;
    
        dest += x + srcLeft + (y + srcTop) * g_screenWidth;
    
        if (m_hasTransparentPixels)
        {
            int realCount = 0;
            int index     = 0;
    
            for(int i = 0; i < m_height;) {
                if (!source [index]) {
                    ++index;
                    dest      += source[index];
                    realCount += source[index];
                    ++index;
                } else {
                    memcpy(dest, &source[index + 1], source[index] * sizeof(unsigned short));
    
                    realCount += source[index];
                    dest      += source[index];
                    index     += source[index] + 1;
                }
    
                if(realCount == m_width) {
                    realCount = 0;
                    dest += g_screenWidth - m_width;
                    ++i;
                }
            }
        } else {
            int srcRight  = x + m_width  > g_screenWidth  ? g_screenWidth  - x : m_width ;
            int srcBottom = y + m_height > g_screenHeight ? g_screenHeight - y : m_height;
    
            for (int i=srcTop; i<srcBottom; ++i) {
                memcpy(dest, src, (srcRight - srcLeft) * sizeof(unsigned short));
    
                source += m_width;
                dest   += g_screenWidth;
            }
        }
    }
    


  • Whoopsie, kleiner Fehler unterlaufen. also so:

    void CBitmap::drawNormal (int x, int y, unsigned short *dest)
    {
        assert (m_buffer);
    
        unsigned short *source = m_hasTransparentPixels ? m_rleBuffer : (m_buffer + srcLeft + srcTop * m_width);
    
        int srcLeft   = x < 0 ? -x : 0;
        int srcTop    = y < 0 ? -y : 0;
        int srcRight  = x + m_width  > g_screenWidth  ? g_screenWidth  - x : m_width ;
        int srcBottom = y + m_height > g_screenHeight ? g_screenHeight - y : m_height;
    
        int no_clipping = srcLeft | srcTop | srcRight | srcBottom;
    
        dest += x + srcLeft + (y + srcTop) * g_screenWidth;
    
        if (m_hasTransparentPixels && no_clipping)
        {
            int realCount = 0;
            int index     = 0;
    
            for(int i = 0; i < m_height;) {
                if (!source [index]) {
                    ++index;
                    dest      += source[index];
                    realCount += source[index];
                    ++index;
                } else {
                    memcpy(dest, &source[index + 1], source[index] * sizeof(unsigned short));
    
                    realCount += source[index];
                    dest      += source[index];
                    index     += source[index] + 1;
                }
    
                if(realCount == m_width) {
                    realCount = 0;
                    dest += g_screenWidth - m_width;
                    ++i;
                }
            }
        } else {
            for (int i=srcTop; i<srcBottom; ++i) {
                memcpy(dest, src, (srcRight - srcLeft) * sizeof(unsigned short));
    
                source += m_width;
                dest   += g_screenWidth;
            }
        }
    }
    


  • Wieso ich keinen namespace benutze und assert neu definiere und kein memcpy benutzen kann ist einfach: ich kann keine Standardlib benutzen? Wie denn auch wenn ich sie für meine Zwecke nicht mitlinken darf?

    @0xdeadbeef
    danke.

    @otze
    nur weil ich m_ und g_ benutze heißt das das ich die ganze Ungarische Notation benutze? Nö. nur 2 sachen die sich für mich persönlich durchgesetzt haben! Merk dir: es gibt nicht immer nur schwarz und weis sondern auch graustufen.



  • achja zum assert:
    CBitmap.cpp(244) : warning C4800: 'unsigned short *' : forcing value to bool 'true' or 'false' (performance warning)



  • ------------ schrieb:

    achja zum assert:
    CBitmap.cpp(244) : warning C4800: 'unsigned short *' : forcing value to bool 'true' or 'false' (performance warning)

    ja, die warnung stört. ich hab mir für den msvc ein eigenes idiom ausgedacht, was nur diese warnung auszumachen hat.

    assert(!!m_buffer);
    

    bei m_buffer?true:false muss ich erst genau lesen, was passert. das !! ist als ein symbol erkennbar. und assert(bool(m_buffer)) macht die warnung nicht aus.



  • Man könnte natürlich auch die Zeiger einfach auf 0 testen... dürfte den selben Code generieren wie assert(pointer), IMHO. Bedeutet also das selbe und ist auch noch klarer, wie ich finde.



  • Optimizer schrieb:

    Man könnte natürlich auch die Zeiger einfach auf 0 testen... dürfte den selben Code generieren wie assert(pointer), IMHO. Bedeutet also das selbe und ist auch noch klarer, wie ich finde.

    nee.
    ich frage mit assert(pdata), ob der zeiger gültig ist. mit assert(pdata!=0) frage ich bloß, ob er gleich 0 ist (und nach sekundenlanger überlegung komme ich bestimmt drauf, daß das aussagt, ob er gültig ist). das ist für mich ein großer unterschied, auch wenn hume gleich andeuten wird, daß ich immer aus ner mücke nen elefanten mache.



  • Ich bin von Java verdorben und bevorzuge daher, explizit den Vergleich mit 0 hinzuschreiben. Naja auch net wild, solang mans lesen kann...
    Mach nicht immer aus ner Mücke nen Elefanten. 😉 🤡

    Willkommen Newbie

    Auf diese Seite zu kommen ist wahrscheinlich die erste kluge Entscheidung von dir. Hier lernst
    du alles was du brauchst um dir selbstständig Information mit Google zu besorgen. In diesem
    Tutorial wird beschrieben, wie du einen C++ Kurs mit Google finden kannst.

    Ich krieg mich nicht mehr. 😃



  • ------------- schrieb:

    Wieso ich keinen namespace benutze und assert neu definiere und kein memcpy benutzen kann ist einfach: ich kann keine Standardlib benutzen? Wie denn auch wenn ich sie für meine Zwecke nicht mitlinken darf?

    also ich musste noch nie etwas mitlinken, um namespaces benutzen zu können...hmmm liegts vielleicht daran, dass "namespace" ein normales c++ schlüsselwort wie "int" oder "class" ist? :p

    und wieso zum geier willst du die stl nicht benutzen? die ist doch eigentlich das, was c++ ausmacht

    @otze
    nur weil ich m_ und g_ benutze heißt das das ich die ganze Ungarische Notation benutze? Nö. nur 2 sachen die sich für mich persönlich durchgesetzt haben! Merk dir: es gibt nicht immer nur schwarz und weis sondern auch graustufen.

    und alle diese graustufen sind absolut häßlich 🙄. schon allein die unterstriche sind ne sünde und urhäßlich, aber sie dann auch noch als präfix trenner zu mißbrauchen....tzz 👎



  • otze schrieb:

    und wieso zum geier willst du die stl nicht benutzen? die ist doch eigentlich das, was c++ ausmacht

    falsch.

    und assert muß man umdefinieren, weil das alte assert keine destruktoren aufruft.



  • hmm gehört die stl nicht mehr zum standard?
    und beschreibt der standard nicht mehr, was für inhalte die sprache haben muss?



  • Die STL gehört zum Standard, aber was die Sprache ausmacht, sind eher ihre Paradigmen - im Fall von C++ vor allem das generische und das objektorientierte Paradigma.



  • dann formuliere ich es um "ohne die STL verliert man den großteil der funktionalität von C++"

    🙄



  • ------------- schrieb:

    nur weil ich m_ und g_ benutze heißt das das ich die ganze Ungarische Notation benutze? Nö. nur 2 sachen die sich für mich persönlich durchgesetzt haben! Merk dir: es gibt nicht immer nur schwarz und weis sondern auch graustufen.

    g für globale variablen? erklärt uns bitte noch mehr über guten stil. das müssen alle lernen.



  • otze schrieb:

    dann formuliere ich es um "ohne die STL verliert man den großteil der funktionalität von C++"

    ohne die sl verliert man den großteil der funktionalität der stl.
    ich schreibe oft saenstrukturen selber.
    wie lange braucht es, ne string-klasse zu schreiben? bloß minuten doch.
    und nen doppelt vberketteten ring kaum läner.
    die verketten datenstrukturen werden gerne intrusiv, naja, man will ja top speed.
    wie lange dauert's nen heap zu bauen? steht ja beim sedgewck deutlich genug da.
    und dann noch nen stack und ne queue.
    ein array und einm vector.
    das war's doch auch schon in sachen datenstrukturen.
    bei den algos ist nch sort manchmal nett. rest ist trivial.
    also wirklich absolut nix, auf das man ganz dringend angewiesen wäre.
    also wenn man keinen bock hat, sich was selber zu schreiben und vor allem, wenn man zu doff dazu ist, lebt c++ von der stl. wenn einem so einfache sachen aber endlich auch einfach von der hand gehen, empfindet man große teile der stl eher als ballast, der alle nubes von der echten welt abschneidet. den spaß, eine genau angemessene datenstruktur zu haben, werden sie nie erleben. und bei diesem hype um die stl scheint es, als sei ich der letze, der das je erlebt hat.



  • Ich für meinen Teil möchte möglichst schnell möglichst sauberen Code bauen, und wenn ich mich jedesmal hinsetze und eine neue Listenklasse hinschlure, wird das nichts. Wenn die einfache Liste für meine Zwecke mal wirklich nicht ausreichen sollte, kann ich immer noch davon ableiten und ein paar Methoden drancoden, was den riesigen Vorteil hat, dass das Ding nachher noch kompatibel zu den Standard-Algorithmen ist - und zur Hälfte meines sonstigen Codes,

    Mal ganz ehrlich - wenn du selbst eben mal ne String-Klasse hinschlurst, wird die weder bugfreier noch stabiler noch schneller als die gängigen STL-Implementierungen sein, und wenn du das in ein paar Minuten schaffst, kümmerst du dich höchstvermutlich nicht um die Konzepte kümmern, die für eine generische Sprache wie C++ so unglaublich wichtig und praktisch sind.

    Ganz abgesehen davon vergisst du einige der komplizierteren Dinge der STL - set, map, bit_vector - ich möchte sehen, wie du nen RB-Baum in 5 Minuten zusammenschraubst. Dazu kommen Iteratoren, Streams und so weiter und so weiter. Zugegeben, die STL stellt nur recht grundsätzliche Funktionalität zur Verfügung, aber dafür ist sie auch da. Wenn du gerne irgendwelche Anfängerübungen wieder und wieder neu machst, meinetwegen, aber ich für meinen Teil will in der Zeit was gebacken kriegen.



  • ich möchte sehen, wie du nen RB-Baum in 5 Minuten zusammenschraubst

    Das traue ich volkard durchaus zu ;).

    mfg
    v R



  • 0xdeadbeef schrieb:

    Ich für meinen Teil möchte möglichst schnell möglichst sauberen Code bauen, und wenn ich mich jedesmal hinsetze und eine neue Listenklasse hinschlure, wird das nichts. Wenn die einfache Liste für meine Zwecke mal wirklich nicht ausreichen sollte, kann ich immer noch davon ableiten und ein paar Methoden drancoden, was den riesigen Vorteil hat, dass das Ding nachher noch kompatibel zu den Standard-Algorithmen ist - und zur Hälfte meines sonstigen Codes,

    ableiten und drancoden? hört, hört!
    so kriegste nir die genau angemessenen container zusatnde. bevor du sowas machst, beschränke dich lieber auf die stl.

    Mal ganz ehrlich - wenn du selbst eben mal ne String-Klasse hinschlurst, wird die weder bugfreier noch stabiler noch schneller als die gängigen STL-Implementierungen sein, und wenn du das in ein paar Minuten schaffst, kümmerst du dich höchstvermutlich nicht um die Konzepte kümmern, die für eine generische Sprache wie C++ so unglaublich wichtig und praktisch sind.

    handlichkeit ist mir oft wichtiger als generizität.

    Ganz abgesehen davon vergisst du einige der komplizierteren Dinge der STL - set, map, bit_vector - ich möchte sehen, wie du nen RB-Baum in 5 Minuten zusammenschraubst.

    ne BitField hab ich schon, weil ich top speed für mein promzahlensieb haben sollte.
    und ich brauche keine rb-bäume. beobachte mal, wo bäume wirklich verwendet werden!
    - in fällen, wo ich von einer zufälligen verteilung ausgehen darf. erfolg: ein simpler 5-minuten-baum ist schneller. buh!
    - in fällen, wo es auf keine sortierung ankommt, sondern nur auf die methoden der map. also einfügen, löschen und finden. erfolg: eine hash-table ist schneller. buh!
    - wenn es mal auf die sortierung ankommt, dann nur, damit man in seinem algo das kleinste element abholen kann. erfolg: ein heap ist schneller.

    heaps und hashtables benutze ich hin und wieder. aber bäume? nee. das passiert nicht.

    Dazu kommen Iteratoren, Streams und so weiter und so weiter.

    die streams, die so scheiße sind?
    wer sagte, daß man keine iteratoren für ringe, heaps und hashtables bauen kann? ok, für ringe und hashtables macht es sinn. für heaps macht es keinen sinn (ist aber zu leicht, um sich gedanken drüber zu machen.).

    Zugegeben, die STL stellt nur recht grundsätzliche Funktionalität zur Verfügung, aber dafür ist sie auch da. Wenn du gerne irgendwelche Anfängerübungen wieder und wieder neu machst, meinetwegen, aber ich für meinen Teil will in der Zeit was gebacken kriegen.

    das darfst du.

    ich muss mir halt nicht sagen lassen, ich habe c++ oder das, was c++ ausmacht, nicht verstanden, weil ich die stl nicht mag. ich halte die stl für einen durchaus notwendig gewesenen schritt zu guten libs hin.



  • An der STL stören mich nur 3 Punkte:
    1.) Manches ist zu umständlich realisiert.
    2.) In der 98er-Version fehlen mir noch einige Sachen.
    3.) Der optische Eindruck bei STL-haltigem Code ist nicht unbedingt schön.



  • Whow, whow. Ich hab nie Code von dir gesehen, von daher liegt es mir fern, zu bewerten, wie gut du C++ geblickt hast.

    Alles, was ich sagen wollte, war, dass neben der Laufzeit auch die Entwicklungszeit eine Rolle spielt. Und wenn man nicht grad der krasse Übergott ist, baut man in komplexen 5-Minuten-Code auch den ein oder anderen Bug ein, deswegen macht es in meinen Augen mehr Sinn, schon vorhandenen, generischen Code wieder zu verwenden. Natürlich mag es für bestimmte Dinge handlicher oder schneller sein, wenn man maßgeschneiderte Datentypen benutzt, aber im Vergleich zum Aufwand ist der Gewinn in aller Regel eher mäßig.

    Wo ich dir widersprechen muss ist aber das ableiten und drancoden. Zugegeben, meistens ist es sinnvoller, nen STL-Datentyp zu wrappen, und eigentlich immer ist es sinnvoller, ne Erweiterungstemplate zu schreiben, so in der Art

    template <typename container_t> class container_wrapper {
    public:
      typedef container_t container_type;
    
    protected:
      container_type my_containing_member;
    
      ...
    };
    

    bzw.

    template <typename container_t> class container_extension : public container_t {
    public:
      void my_new_and_wonderful_method() { ... }
    };
    

    ...einfach für ne schönere Kapselung. Auf jeden Fall gibt es durchaus Situationen, in denen das Sinn macht.

    Was die Verwendung von Bäumen angeht, so macht das vor allem dann Sinn, wenn man hierarchische Strukturen zu verwalten hat, wie etwa Dateisysteme, wenn es keine wirklich sinnvolle Möglichkeit gibt, den Schlüssel-Datentyp zu hashen, oder wenn man überhaupt nicht einschätzen kann, wie viele Werte der ADT nachher enthalten soll.

    Und noch eine kleine Sache: Iteratoren gibt es in der STL nicht nur für streams, sondern auch für alle Container.


Anmelden zum Antworten