speicher schreiben und der cache



  • halo leute

    mittels caching, loop unrolling usw. kann man das lesen aus dem speicher ja ganz schoen schnell machen. wie sieht das aber beim beschreiben des speichers aus ?

    caching bedeutet ja, das beim lesen einer speicherstelle nicht nur 1 oder 4 byte gelesen werden, sondern 32 oder 64. je nach cacheline groesse.

    wie sieht das nun beim schreiben in den speicher aus ?
    beim schreiben funktioniert dasirgendwie nicht so recht. nicht mal das loop unrolling bringt da was. ich schaffs grad mal auf 70% der memcpy funkton des bcb.
    ansich sollte mein system ca. 1800 MB/sek schreiben koennen. mit der memcpy gehts grad mal auf 650 MB. scheint mir recht wenig zu sein. gibts da ne moeglichkeit das zu beschleunigen ?

    vielleicht kennt sich da jemand aus und kann mir einen denkanstoss oder nen link geben

    cermy

    Meep Meep


  • Mod

    zeig doch mal, was du versucht hast - es ist zwar prinzipiell richtig, dass (sofern der speicher nicht als uncachebar gekennzeichnet ist) immer eine volle cacheline gelesen wird, das ist auf grund des hardwareprefetchings aber selten ein problem; zumal in zeiten von DDR. im zweifelsfalle kann man auch schon mal per hand nachhelfen - falls dein ergebnis allerdings tatsächlich schlechter als memcpy ist, dürfte sich das problem eher vor dem bildschirm befinden 🙂
    eine möglichkeit, das caching zu umgehen, sind non-temporal stores. dafür braucht man dann aber mindestens SSE.



  • hoi camper

    in meinem fall sitzt der fehler sicherlich vor dem bildschirm ;o)

    bis jetz hab ich einige sachen ausprobiert:

    int *target = int_buffer1;
    int *source = int_buffer2;
    
    for(int i = 0;i < x;i += 4)
    {
       target[i] = source[i];
       target[i + 1] = source[i + 1];
       target[i + 2] = source[i + 2];
       target[i + 3] = source[i + 3];
    }
    

    solche sachen in den verschiedensten variationen hab ich scho probiert.
    hab dann schon probiert target zu cachen und dann erst darauf zu schreiben. aber auch kein erfolg. im gegenteil, sogar etwas langsamer.

    das mit SSE waere interessant zu probieren, jedoch geht das mit dem bcb nicht.
    wenn es ne moeglichkeit gaebe, externen asm source mit nem c++ source zu verbinden, waere es ja sehr interessant. aber scheinbar funktioniert das nicht.

    Meep Meep


  • Mod

    aus dem C++ code könnte man bereits etwas machen:

    int i = - ( ( x + 3 ) & ~3 );
    int *target = int_buffer1 - i;
    const int *source = int_buffer2 - i;
    switch ( x & 3 )
    {
    case 0: do { target[ i ] = source[ i ];
    case 3: target[ i + 1 ] = source[ i + 1 ];
    case 2: target[ i + 2 ] = source[ i + 2 ];
    case 1: target[ i + 3 ] = source[ i + 3 ];
        } while ( i += 4 );
    }
    

    die optimale version hängt stark von einigen randbedingungen ab: dem prozessor typ, der pufferlänge und der relativen und absoluten lage von source und target (insbesondere ob abs(source-target) >= grösse der cacheline)

    das einbinden von externem assembler sollte eigentlich kein problem sein - es sei denn bcb benutzt ein inkompatibles linker format?

    die einfachste (und bei grossen puffern höchtens mit temporal-stores zu überbietende variante) dürfte die hier sein:

    void copy_mem(void* dest, const void* src, int len)
    {
        __asm{
        push    esi
        push    edi
        mov     edi, dest
        mov     esi, src
        mov     ecx, len
        mov     edx, ecx
        shr     ecx, 2
        and     edx, 3
        rep movsd
        mov     ecx, edx
        rep movsb
        pop     edi
        pop     esi
        }
    }
    

    das kann man noch etwas verfeinern, aber mir sind die callkonventionen von bcb nicht geläufig. schneller als memcpy ist das vermutlich aber nicht - wenn der compiler auch nur ein bisschen was taugt, ist das in etwa nähmlich dass, was bei memcpy passiert.



  • re

    hab deinen C code mal getestet. der laeuft am BCB auch ca. 30 % langsamer als das memcpy.

    wie es aussieht, werde ich von dem memcpy wohl nicht wegkommen, was ziemlich stoerend ist, weil ich nun nur wegen dieser einen funktion die mem.h bzw string.h in meinen source includieren muss. naja kann man wohl nix machen.
    trotzdem dank dir.

    bis denn

    Meep Meep



  • Meep Meep schrieb:

    hab deinen C code mal getestet. der laeuft am BCB auch ca. 30 % langsamer als das memcpy.

    Vermutlich, weil du deinen Code im Debug-Modus kompiliert hast, und die memcpy-Funktion der Bibliothek natürlich im Release-Modus kompiliert wurde.

    camper schrieb:

    das einbinden von externem assembler sollte eigentlich kein problem sein - es sei denn bcb benutzt ein inkompatibles linker format?

    Für den BCB nimmt man halt den (mitgelieferten) Turbo Assembler, dann klappt das auch mit dem Dateiformat (der BCB kommt aber AFAIK auch mit MSVC-LIBs zurecht; kann allerdings sein, daß man sie konvertieren muß).

    camper schrieb:

    wenn der compiler auch nur ein bisschen was taugt, ist das in etwa nähmlich dass, was bei memcpy passiert.

    Um zu sehen, was eine solche Funktion macht, kann man sie einfach mit dem im Debugger integrierten Disassembler verfolgen.

    Beim BCB werden esi und edi beim Funktionsaufruf automatisch auf den Stack geschoben, da können wir also noch etwas sparen:

    void* my_memcpy (void* dest, const void* src, unsigned int len)
    {
            __asm
            {
                    mov     eax, dest
                    mov     edi, eax
                    mov     esi, src
                    mov     ecx, len
                    mov     edx, ecx
                    shr     ecx, 2
                    cld
                    rep movsd
                    mov     ecx, edx
                    and     ecx, 0x00000003
                    rep movsb
            };
    }
    

    Btw: bringt es eventuell was, die Adressen zunächst an DWORD-Grenzen auszurichten, und wenn ja, dann src oder dest?

    Moritz



  • hoi

    audacia schrieb:

    Meep Meep schrieb:

    hab deinen C code mal getestet. der laeuft am BCB auch ca. 30 % langsamer als das memcpy.

    Vermutlich, weil du deinen Code im Debug-Modus kompiliert hast, und die memcpy-Funktion der Bibliothek natürlich im Release-Modus kompiliert wurde.
    Moritz

    hab im release-modus kompiliert.

    Meep Meep



  • Meep Meep schrieb:

    hab im release-modus kompiliert.

    Ich auch, und bei mir liefen beide Funktionen etwa gleich schnell (BCB 6, X 1.5). Vielleicht Meßungenauigkeit?


  • Mod

    das cld dürftest du dir sparen können, es sei denn, bcb hat hier völlig andere konventionen. jedenfalls für vc++, gcc bei allen funktionseintritten und für alle API calls ist zwingend vorgeschrieben, dass das direction flag nicht gesetzt ist.



  • Meep Meep schrieb:

    das mit SSE waere interessant zu probieren, jedoch geht das mit dem bcb nicht.

    Ab welchen Prozessoren wird SSE eigentlich unterstützt?



  • re

    audacia schrieb:

    Meep Meep schrieb:

    das mit SSE waere interessant zu probieren, jedoch geht das mit dem bcb nicht.

    Ab welchen Prozessoren wird SSE eigentlich unterstützt?

    bin mir nicht ganz sicher. beim intel ab dem Pentium III und beim AMD ab dem AMD64. wobei ich mir da bei dem Septeron oder wie der heisst nicht ganz sicher bin. AMD XP koennen SSE nur zum teil anwenden.

    Meep Meep


  • Mod

    Meep Meep schrieb:

    AMD XP koennen SSE nur zum teil anwenden.

    wieso zum teil?
    obige kopierroutine ist übrigens verbugt - immer dann, wenn die blockgrösse ein vielfaches von 4 ist, hast du ein problem 🙂



  • camper schrieb:

    Meep Meep schrieb:

    AMD XP koennen SSE nur zum teil anwenden.

    wieso zum teil?

    Zitat:
    AMD unterstützt erst mit seinen aktuellsten Prozessoren SSE vollständig. Davor wurden nur die Befehle unterstützt, die mit 64-Bit-Registern arbeiten.

    http://www.computerbase.de/lexikon/SSE <- da hatte ich das mal gelesen.

    Meep Meep



  • camper schrieb:

    obige kopierroutine ist übrigens verbugt - immer dann, wenn die blockgrösse ein vielfaches von 4 ist, hast du ein problem 🙂

    Meine? Wo?




  • Mod

    wenn wir schon beim link geben sind:
    http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25112.PDF
    enthält im abschnitt 5.12 ebenfalls eine ganze reihe kopierroutinen und diskutiert deren vor- und nachteile

    audacia schrieb:

    camper schrieb:

    obige kopierroutine ist übrigens verbugt - immer dann, wenn die blockgrösse ein vielfaches von 4 ist, hast du ein problem 🙂

    Meine? Wo?

    jetzt wo du fragst, bin ich mir auch nicht mehr sicher; aber war es nicht so, dass bei ecx == 0 der befehl 2^32 mal ausgeführt wird ? das ist bei mir seit dem Z80 (LDIR) so drin.


Log in to reply