byte schnell von a nach b kopieren



  • Hallo

    Ich will ein Byte schnell von A nach B kopieren. A und B sind Speicheradressen, die in esi und edx gespeicher sind

    Im Moment schaffe ich es nur über den Umweg des lowwords von cx

    mov cl, [esi]// Src Ptr in low byte von cx Register speichern
    mov [edx], cl // low byte von cl in Tgt Ptr speichern (1 Byte Operation)

    Ich würde jetzt gerne das auf eine Zeile verkürzen, also direkt Speicher->Speicher, aber alle Versuche scheitern

    z.B.
    mov byte ptr[edx], byte ptr[esi]

    geht nicht

    Danke für jede Hilfe

    Ich will übrigens damit einen schnellen 1-Byte-Matrix Zeilen-Spaltentausch realisieren. Weiss jemand ob es dafür noch schnellere Methoden gibt als dumpfbacken ein Byte zu vertauschen ?

    Stefan



  • mov byte ptr[edx], byte ptr[esi]

    Das geht einfach nicht. Es gibt schon einen Befehl, der von einer Speicheradresse liest und dann in eine andere schreibt (movsb), aber das ist nicht gerade sehr empfehlenswert.

    Weiss jemand ob es dafür noch schnellere Methoden gibt als dumpfbacken ein Byte zu vertauschen ?

    Naja, du könntest die shuffle-Funktionen von MMX verwenden, wenn du das zur Verfügung hast.



  • Hmm bringt shuffle was? Das Problem ist ja dass ich aus der Quelle zeilenweise lesen kann, aber in das Ziel Spaltenweise schreiben muss. Und das sind Matrizen mit Zeilenlängen über 500. D.h. ich muss den Offset für das Ziel für jede Operation um +500 oder mehr erhöhen im Moment.

    Mit movsb gehts wenn ich esi u. edi mit den Pointern belege. Danach darf ich edi nur um hdec = h-1 erhöhen da es durch den Befehl schon inkrementiert wird. Ist aber auch nicht schneller als meine erste Variante

    movsb
    add edi, hdec // Tgt Ptr um eine Zeile erhoehen ( = Hoehe von Src)

    Stefan



  • Wenn das Zeug so groß ist, dann ist das einzige, was zählt, das Speicherzugriffsmuster. Ich hoffe, du schreibst es nicht deshalb in Assembler, weil du davon mehr Geschwindigkeit erhoffst.


  • Mod

    ist die quelle ein stream, oder ist sie einfach nur zeilenweise organisiert?
    eine möglichkeit, das problem anzugehen, wäre sie rekursiv aufzufassen:
    spiegeln einer matrix an der hauptdiagonalen kann man auch dadurch erreichen, dass man die matrix in kleine handliche matrizen zerlegt, diese einzeln spiegelt und ihre position (anschliessend oder gleichzeitig) an der hauptdiagonalen spiegelt. das würde man dann am besten mit 8*8Matrizen machen, die man mittels aligned read lädt, dort vertauscht und anschliessend mittels non-temporal-store zurückschreibt. zum schluss das sfence nicht vergessen. einfach geht dass allerdings nur mit SSE2, da es nicht ganz trivial (aber möglich) ist, 8*8Matrizen zu spiegeln, wenn man bloss 8 mmx-register hat.



  • Wieso braucht man dann ein sfence?



  • Ja es hat was mit Speicherzugriff zu tun. Um genau zu sein: Ich kippe ein Bild um 90°.
    Sicherlich ist momentan die Transferrate zum Speicher der Flaschenhals, aber vielleicht ändert sich das irgendwann.

    Stefan



  • Nein, nicht in absehbarer Zeit. Im Gegenteil, die Kluft zwischen CPU- und RAM-Frequenz wird immer größer.


  • Mod

    Ringding schrieb:

    Wieso braucht man dann ein sfence?

    wir wollen doch sicherstellen, dass die daten auch tatsächlich im speicher gelandet sind, bevor sie verwendet werden. andernfalls kann es zu hässlichen und schwer debugbaren problemen kommen, spätestens in multiprozessorumgebungen, aber das ist gar nicht unbedingt nötig.
    die l/s/mfence instruktionen sind generell zu verwenden, wenn man irgendwelche temporal-move befehlen arbeitet. die serialisierung ist dabei keine performance bremse, denn der schreib/lesevorgang, den man serialsiert, muss ja in jedem falle stattfinden, im übrigen benutzt man sie ja meist nur im zusammenhang mit grösseren datenblöcken.



  • Bei multithread-Systemen macht man soundso irgendeine Art von lock, wenn mehrere Threads auf den gleichen Speicher zugreifen. Das ist genauso gut. Also wäre nur der singlethread-Fall interessant. Wenn man einen non-temporal-store macht und danach im gleichen Thread die gleiche Speicheradresse ausliest, kann dann der Wert geliefert werden, der vorher dringestanden ist? Was physikalisch im Speicher steht, interessiert mich nicht wirklich, sondern nur das, was ein Lesebefehl mir liefert.


  • Mod

    ich nehme an, in diesem falle ist es irrelevant, abgesehen davon, dass es ohne mfence (ja mfence, sfence war ein fehler von mir) möglicherweise langsamer ablaufen könnte. ein normales read lädt ja den speicherbereich in den cache, jedes non-temporal-store invalidiert diese cache zeile aber wieder. wenn also die abfolge so aussähe:

    STORE - LOAD - STORE - LOAD (jeweils auf denselben speicherbereich) dann muss bei jedem LOAD der speicher neu in den cache geladen werden. ein rechtzeitiges mfence nach den stores würde dagegen dazu führen:

    STORE - STORE - LOAD - LOAD hier kann also das zweite LOAD den cache benutzen, das führt also zu geringerer busaktivität und daher potentiell höherem durchsatz gerade bei operationen, die durch den speicherzugriff gebremst werden. wenn man kein mfence will, sollte man besser auf non-temporal store verzichten, andererseits ist dieses ja sehr wertvoll, wenn man es eben mit solchen operationen wie obigem matrixtransponieren zu tun hat, wenn diese matrix selbst sehr viel platz braucht und daher der platz im cache knapp wird.



  • Das ist mir nicht ganz geheuer, was du da schreibst. Die Zugriffsreihenfolge kann ja gerade über eine mfence-Grenze hinweg auf gar keinen Fall umgestellt werden.


  • Mod

    Ringding schrieb:

    Das ist mir nicht ganz geheuer, was du da schreibst. Die Zugriffsreihenfolge kann ja gerade über eine mfence-Grenze hinweg auf gar keinen Fall umgestellt werden.

    das meinte ich doch:

    was ist denn mit

    movnti [ ecx ], eax
    movnti [ ecx + 4 ], ebx
    mov eax, [ ecx ]
    mov ebx, [ ecx + 4 ]
    

    es gibt keine garantie, dass das store auf [ecx+4] vor dem load von [ecx] passiert, anders mit mfence:

    movnti [ ecx ], eax
    movnti [ ecx + 4 ], ebx
    mfence
    mov eax, [ ecx ]
    mov ebx, [ ecx + 4 ]
    

    EDIT: man denke sich ein hinreichend komplexes beispiel, bei dem store-load-forwarding nicht greifen würde; obiges beispiel ist evtl. zu einfach.



  • So schaut's vernünftig aus. Ich werd trotzdem mal ins Intel Manual schauen :). Man sollte ja mit mfence nicht gerade um sich werfen, weil es einfach eeeeewig dauert. Dagegen ist ein normaler Speicherzugriff ja geradezu so flink, dass er überhaupt nicht ins Gewicht fällt.


Anmelden zum Antworten