memcpy schneller als Schleife?



  • memcpy, strlen, strcpy, memcmp,... das sind alles magische funktionen - die können alles.

    eine schleife wird nie schneller sein als ein memcpy - höchstens gleich schnell (es sei denn die library ist wirklich extremer mist)

    der compiler kann nämlich zaubern - der macht mal ein memcpy so rum und woanders macht ers andersrum, also der weiß schon wie er zu optimieren hat.

    und da memcpy ja nur plain data zum rumschaufeln hat, gibts auch keine speziellen tricks algorithmen tricks - alles auf assembler ebene -> je nachdem was die CPU an daten will, wird sie perfekt ausgelastet 🙂

    nunja, aber eine schleife kann genauso schnell sein, da der compiler ja erkennen kann was du willst und er die schleife einfach durch sein memcpy ersetzt.



  • /***
    *memcpy - Copy source buffer to destination buffer
    *
    *Purpose:
    *       memcpy() copies a source memory buffer to a destination memory buffer.
    *       This routine does NOT recognize overlapping buffers, and thus can lead
    *       to propogation.
    *
    *       For cases where propogation must be avoided, memmove() must be used.
    *
    *Entry:
    *       void *dst = pointer to destination buffer
    *       const void *src = pointer to source buffer
    *       size_t count = number of bytes to copy
    *
    *Exit:
    *       Returns a pointer to the destination buffer
    *
    *Exceptions:
    *******************************************************************************/
    
    void * __cdecl memcpy (
            void * dst,
            const void * src,
            size_t count
            )
    {
            void * ret = dst;
    
    #if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) || defined (_M_IA64)
            {
            extern void RtlMoveMemory( void *, const void *, size_t count );
    
            RtlMoveMemory( dst, src, count );
            }
    #else  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) || defined (_M_IA64) */
            /*
             * copy from lower addresses to higher addresses
             */
            while (count--) {
                    *(char *)dst = *(char *)src;
                    dst = (char *)dst + 1;
                    src = (char *)src + 1;
            }
    #endif  /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) || defined (_M_IA64) */
    
            return(ret);
    }
    


  • Original erstellt von ChrisM:
    Aber vielleicht optimieren manche guten Compiler die Schleife schon zu memcpy(), würd ich mich aber nicht drauf verlassen!

    Also ich würd mir dann nen neuen Compiler kaufen 😉 .

    Zum Thema: Ich glaube nicht, dass es einen großen Unterschied macht ob Schleife oder memcpy. Der Compiler kann nicht wirklich viel an der Stelle optimieren. Ich würde meinen Blick eher auf andere Codestellen lenken. Was mir so spontan zum Optimieren bei zeitkritischen Anwendungen einfällt:

    - PreIncrement verwenden wenn PostIncrement unnötig ist.
    - Initialisierungslisten von Klassen verwenden anstatt im Kontruktor drin zu initialisieren.
    - Objekt sofort nach dem erstellen initialisieren anstatt ne Zeile später.
    - Wenn möglich Parameter über Referenzen übergeben.
    - Variablen so spät wie möglich deklarieren und so lokal wie möglich halten.
    - Beim rechnen mit Ganzzahlen immer int anstatt short (auch wenn short reicht) bevorzugen. (Weil beim rechnen immer erst umwandlung in int geschieht falls eine Zahl short ist)
    - Inline-Funktionen kurz halten.
    - Rückgabewerte nur wenn sie gebraucht werden.
    - Lokale Variablen minimieren.
    - Shiften anstatt pow bei Zweierpotenzen.
    - Bei Strukturen in großen Arrays sollte die Strukturgröße eine Zweierpotenz sein, weil beim Berechnen des Indizes dann geshiftet werden kann.

    EDIT: Naja, der letzte Punkt ist nicht immer sinnvoll! 😃

    Vielleicht fällt ja noch jemandem was ein...?

    [ Dieser Beitrag wurde am 13.05.2003 um 18:21 Uhr von MaSTaH editiert. ]



  • Naja, der vorletzte Punkt ist nicht sinnvoll!



  • Original erstellt von <-1>:
    Naja, der vorletzte Punkt ist nicht sinnvoll!

    Natürlich.

    [ Dieser Beitrag wurde am 13.05.2003 um 18:45 Uhr von MaSTaH editiert. ]



  • Gibt auch noch die Möglichkeit von sogenannten Templates... schau mal unter http://www.oonumerics.org/blitz/ . Habe es zwar noch nicht so ganz verstanden, scheint aber schneller zu sein, als ne simple (!?) For Schleife.

    Gruß Winn



  • @Winn
    Hä, templates haben mit Schleifen doch nicht das geringste zu tun.



  • template metaprogrammierung



  • Original erstellt von MaSTaH:
    @Winn
    Hä, templates haben mit Schleifen doch nicht das geringste zu tun.

    doch, doch.
    man kann nen TinyVector<typename T,size_t SIZE>{T data[SIZE]... so implementieren, daß der Copy-Konstruktor ne normale for-Schleife nimmt, daß er memcpy nimmt, daß er duff's device nimmt, oder daß er per templates ne Schleife komplett unrolled. Und letzteres ist für Blitz++ typisch.



  • @Mastah

    Wenn ich das wußte, dann würd ich Dir mehr dazu posten, mich hatte ein Kumpel auf die Blitz++ Library gebracht, weil ich Performance brauchte, dann laß ich von den Templates und rekursiven Handlings... da gingen bei mir erstmal die Nackenhaare hoch, weil Rekusionen sind ja vielleicht fein, aber sau lahm, einfache Iterationen bieten sich dort besser an, aber diese Templates scheinen Dampf zumachen... der Kernpunkt der damit ausgestochen wird ist, daß der Compiler nur Pointer und Adressen sieht, also keine Ahnung vom eigentlichen Ablauf hat oder sagen wir von einem sinnigen Ablauf. Compiler die auf die Semantik achten und entsprechend der Programmlogik compilieren sind wohl gescheitert. Der Kai C++ Compiler wurde von Intel aufgekauft, er war das beste Stück in diesem Bereich. Mit den Templates läßt sich aber wohl diese Semantische Lücke schließen...

    Schau dir die Blitz++ Library mal an, sie ist Open Source und steht unter GNU. Wollte jetzt demnächst mal ein Test Programm schreiben, werde mich dann wieder zurückmelden...

    Winn



  • @volkard
    Rekursion mit Templates kann ich mir vorstellen. Oder meinst du was anderes? Werd mir die Blitz-Library mal angucken.



  • Original erstellt von MaSTaH:
    @volkard
    Rekursion mit Templates kann ich mir vorstellen. Oder meinst du was anderes? Werd mir die Blitz-Library mal angucken.

    doch, genau das meine ich.



  • Original erstellt von Shade Of Mine:
    **
    der compiler kann nämlich zaubern - der macht mal ein memcpy so rum und woanders macht ers andersrum, also der weiß schon wie er zu optimieren hat.
    **

    Das mit dem mal so und mal so rum hat nen andren Grund, je nachdem ob man:

    char array[500];
    char* dest = &array[110];
    char* src = &array[100];
    memcpy(dest, src, 100);
    // oder
    char* dest = &array[100];
    char* src = &array[110];
    memcpy(dest, src, 100);
    

    macht würde, wenn memcpy nur von links nach rechts bzw von rechts nach links kopieren könnte, ein falsches Ergebnis kommen, weil er das bereits kopierte nochmal kopiert wird.

    [ Dieser Beitrag wurde am 14.05.2003 um 11:42 Uhr von dreaddy editiert. ]



  • dafür gibts memmove.
    mit sorum&andersrum war wohl eher gemeint, daß er aus
    (ich nehme int*a an)

    memcpy(a,b,0) ein {} macht, aus
    memcpy(a,b,4} ein MOV, aus
    memcpy(a,b,40) ein REPNE MOV und aus
    memcpy(a,b,n) ein CALL _memcpy (oder doch nur ein REPNE MOV, wenn er weiß, daß n klein ist?) und aus
    {int* a=new int[100];memcpy(a,a,400);delete a;} ein {}.



  • Hey nochmal,

    also die Blitz Lib ist schon eine feine Sache, allerdings für meine Ansprüche war sie zu langsam. Leider 😞 Anbei der Quellcode für eine Template For Schleife und If Abfrage, gefunden unter [url] http://home.t-online.de/home/Ulrich.Eisenecker/meta.htm [/url]...

    Gruß Winn

    HEADER

    #ifndef _METACTRL
    #define _METACTRL
    
    namespace metactrl
    {       
    
    // IF<>
    
            namespace intimate
            {
                    struct SelectThen 
                    {       template<class Then, class Else>
                            struct Result
                            {       typedef Then RET;
                            };
                    }; // end SelectThen
    
                    struct SelectElse
                    {       template<class Then, class Else>
                            struct Result
                            {       typedef Else RET;
                            };
                    }; // end SelectElse
    
                    template<bool Condition>
                    struct Selector
                    {       typedef SelectThen RET;
                    }; // end Selector
    
                    template<>
                    struct Selector<false>
                    {       typedef SelectElse RET;
                    }; // end Selector<false>
            } // end namespace private
    
            template<bool Condition, class Then, class Else>
            struct IF
            {       typedef intimate::Selector<Condition>::RET select;
                    typedef select::Result<Then,Else>::RET RET;
            }; // IF
         
    // FOR<>, comparators // for convenience
    
            struct Equal
            {       template <int lval, int rval>
                    struct Test
                    {       enum {evaluate = lval == rval};
                    };
            }; // end Equal
    
            struct NotEqual
            {       template <int lval, int rval>
                    struct Test
                    {       enum {evaluate = lval != rval};
                    };
            }; // end NotEqual
    
            struct Less
            {       template <int lval, int rval>
                    struct Test
                    {       enum {evaluate = lval < rval};
                    };
            }; // end Less
    
            struct LessEqual
            {       template <int lval, int rval>
                    struct Test
                    {       enum {evaluate = lval <= rval};
                    };
            }; // end LessEqual
    
            struct Greater
            {       template <int lval, int rval>
                    struct Test
                    {       enum {evaluate = lval > rval};
                    };
            }; // end Greater
    
            struct GreaterEqual
            {       template <int lval, int rval>
                    struct Test
                    {       enum {evaluate = lval >= rval};
                    };
            }; // end GreaterEqual
    
            template <int From,class Compare,int To,int By,class Statement>
            struct FOR
            {       static void EXEC()
                    {       IF <Compare::Test<From,To>::evaluate,
                                    Statement::Code<From>,
                                    intimate::Stop>
                            ::RET::execute();
                            IF <Compare::Test<From,To>::evaluate,
                                    FOR<From+By,Compare,To,By,Statement>,
                                    intimate::Stop>
                            ::RET::EXEC();
                    };
            }; // end FOR
    
    } // end namespace metactrl
    #endif _METACTRL
    

    USING

    // METAXMPL.CPP - Copyright (C) 1998 by 
    // Ulrich W. Eisenecker and 
    // Krzysztof Czarnecki
    // This file demonstrates the usage of the template-metaprogramming
    // control-structures of METACTRL
    
    #include <iostream>
    #include "metactrl"
    
    using namespace std;
    using namespace metactrl; // all meta-controlstructures are in namespace "metactrl"
    
    // stuff for testing IF<>
    
    struct DemoThen
    {       DemoThen()
            {       cout << "DemoThen" << endl;
            }
    };
    
    struct DemoElse
    {       static void execute()
            {       cout << "DemoElse" << endl;
            }
    };
    
    // stuff for testing SWITCH<>
    
    struct A
    {       static void execute()
            {       cout << "A" << endl;
            }
    };
    
    struct B
    {       static void execute()
            {       cout << "B" << endl;
            }
    };
    
    struct C
    {       C()
            {       cout << "C" << endl;
            }
    };
    
    // stuff for testing FOR<>
    
    struct DemoFor
    {       template <int i> // "i" is the loop variable
            struct Code
            {       static void execute()
                    {       cout << i << endl;
                    }
            };
    };
    
    struct DemoNestedFor
    {       template <int i> // "i" is the first loop variable
            struct Code
            {       struct NestedStatement
                    {       template <int j> // "j" is the second loop variable
                            struct Code
                            {       static void execute()
                                    {       cout << i << '|' << j;
                                            if (i == j)
                                            cout << endl;
                                            else cout << '\t';
                                    }
                            };
                    };
                    static void execute()
                    {       FOR<i+1,Less,5,+1,NestedStatement>::EXEC();
                            cout << endl;
                    };
            };
    };
    
    int main()
    {
            // Testing IF<>
            cout << "Testing IF<>" << endl;
            IF<true,DemoThen,DemoElse>::RET if_result;      // declare variable
            IF<false,DemoThen,DemoElse>::RET::execute();    // call class function
    
            // Testing FOR<>
            cout << "Testing FOR<>" << endl;
            FOR<10,Greater,1,-2,DemoFor>::EXEC();             // define it and use it immediately
            // How to nest loops? Here is the answer:
            typedef FOR<0,Less,5,+1,DemoNestedFor> for_loop;  // define it
            for_loop::EXEC();                                 // and use it later
    
            return 0;
    }
    


  • Also ich kann mir nicht helfen. Irgendwie gefällt mir diese Art der Programmierung nicht. Bringt das rein performancetechnisch eigentlich viel?



  • Herkömmliche Methode Du startest das programm und etwas wird berechnet. Templatemetaprogrammierung: Wärend des Compilierens wird bereits einiges berechnet und beim ausführen nurnoch ausgegeben.



  • Herkömmliche Methode: Compilierzeit 2s, Berechnung 10 µs, Ausgabe 100µs
    Template-Meta-Methode: Compilierzeit 2min, Berechnung 0,1 µs, Ausgabe 100µs



  • Also bringts quasi nur was wenn man viel berechnet und wenig ausgibt. Dann dauert das kompilieren aber so lange wie ein Kind zu gebären. Frohes Debuggen 😉 😃



  • @Bashar
    Welche Art von Berechnung hast Du ausgeführt ? Bisher hab ich nur die Blitz++ Lib genutzt, die Template Technik hab ich noch nicht genutzt... zeig mal deinen Testrahmen ?

    Gruß Winn


Anmelden zum Antworten