movmem
-
Weiß jemand was der C-Compiler aus der Anweisung
movmem(a,b,10);
macht ? Könnte das vielleicht jemand, der einen Disassembler hat, rausfinden ?
-
Ich weiß nicht ob du das wirklich wissen willst aber nun gut
Ich nehme an du redest ovn der Funktion memmove und nicht movemem. Das genaue Verhalten hängt von der Runtime--Library ab, ich hab nur gerade den Source vonner M$-RTL da, viel spaß dabei...übrigens ist die Funktion im Source identisch zu memcpy...aber ich sag dir gelich: ich hab dich gewarnt
ifdef MEM_MOVE _MEM_ equ <memmove> else ; MEM_MOVE _MEM_ equ <memcpy> endif ; MEM_MOVE % public _MEM_ _MEM_ proc \ dst:ptr byte, \ src:ptr byte, \ count:IWORD ; destination pointer ; source pointer ; number of bytes to copy ; push ebp ;U - save old frame pointer ; mov ebp, esp ;V - set new frame pointer push edi ;U - save edi push esi ;V - save esi mov esi,[src] ;U - esi = source mov ecx,[count] ;V - ecx = number of bytes to move mov edi,[dst] ;U - edi = dest ; ; Check for overlapping buffers: ; If (dst <= src) Or (dst >= src + Count) Then ; Do normal (Upwards) Copy ; Else ; Do Downwards Copy to avoid propagation ; mov eax,ecx ;V - eax = byte count... mov edx,ecx ;U - edx = byte count... add eax,esi ;V - eax = point past source end cmp edi,esi ;U - dst <= src ? jbe short CopyUp ;V - yes, copy toward higher addresses cmp edi,eax ;U - dst < (src + count) ? jb CopyDown ;V - yes, copy toward lower addresses ; ; Copy toward higher addresses. ; ; ; The algorithm for forward moves is to align the destination to a dword ; boundary and so we can move dwords with an aligned destination. This ; occurs in 3 steps. ; ; - move x = ((4 - Dest & 3) & 3) bytes ; - move y = ((L-x) >> 2) dwords ; - move (L - x - y*4) bytes ; CopyUp: test edi,11b ;U - destination dword aligned? jnz short CopyLeadUp ;V - if we are not dword aligned already, align shr ecx,2 ;U - shift down to dword count and edx,11b ;V - trailing byte count cmp ecx,8 ;U - test if small enough for unwind copy jb short CopyUnwindUp ;V - if so, then jump rep movsd ;N - move all of our dwords jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes ; ; Code to do optimal memory copies for non-dword-aligned destinations. ; ; The following length check is done for two reasons: ; ; 1. to ensure that the actual move length is greater than any possiale ; alignment move, and ; ; 2. to skip the multiple move logic for small moves where it would ; be faster to move the bytes with one instruction. ; align @WordSize CopyLeadUp: mov eax,edi ;U - get destination offset mov edx,11b ;V - prepare for mask sub ecx,4 ;U - check for really short string - sub for adjust jb short ByteCopyUp ;V - branch to just copy bytes and eax,11b ;U - get offset within first dword add ecx,eax ;V - update size after leading bytes copied jmp dword ptr LeadUpVec[eax*4-4] ;N - process leading bytes align @WordSize ByteCopyUp: jmp dword ptr TrailUpVec[ecx*4+16] ;N - process just bytes align @WordSize CopyUnwindUp: jmp dword ptr UnwindUpVec[ecx*4] ;N - unwind dword copy align @WordSize LeadUpVec dd LeadUp1, LeadUp2, LeadUp3 align @WordSize LeadUp1: and edx,ecx ;U - trailing byte count mov al,[esi] ;V - get first byte from source mov [edi],al ;U - write second byte to destination mov al,[esi+1] ;V - get second byte from source mov [edi+1],al ;U - write second byte to destination mov al,[esi+2] ;V - get third byte from source shr ecx,2 ;U - shift down to dword count mov [edi+2],al ;V - write third byte to destination add esi,3 ;U - advance source pointer add edi,3 ;V - advance destination pointer cmp ecx,8 ;U - test if small enough for unwind copy jb short CopyUnwindUp ;V - if so, then jump rep movsd ;N - move all of our dwords jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes align @WordSize LeadUp2: and edx,ecx ;U - trailing byte count mov al,[esi] ;V - get first byte from source mov [edi],al ;U - write second byte to destination mov al,[esi+1] ;V - get second byte from source shr ecx,2 ;U - shift down to dword count mov [edi+1],al ;V - write second byte to destination add esi,2 ;U - advance source pointer add edi,2 ;V - advance destination pointer cmp ecx,8 ;U - test if small enough for unwind copy jb short CopyUnwindUp ;V - if so, then jump rep movsd ;N - move all of our dwords jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes align @WordSize LeadUp3: and edx,ecx ;U - trailing byte count mov al,[esi] ;V - get first byte from source mov [edi],al ;U - write second byte to destination inc esi ;V - advance source pointer shr ecx,2 ;U - shift down to dword count inc edi ;V - advance destination pointer cmp ecx,8 ;U - test if small enough for unwind copy jb short CopyUnwindUp ;V - if so, then jump rep movsd ;N - move all of our dwords jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes align @WordSize UnwindUpVec dd UnwindUp0, UnwindUp1, UnwindUp2, UnwindUp3 dd UnwindUp4, UnwindUp5, UnwindUp6, UnwindUp7 UnwindUp7: mov eax,[esi+ecx*4-28] ;U - get dword from source ;V - spare mov [edi+ecx*4-28],eax ;U - put dword into destination UnwindUp6: mov eax,[esi+ecx*4-24] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4-24],eax ;U - put dword into destination UnwindUp5: mov eax,[esi+ecx*4-20] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4-20],eax ;U - put dword into destination UnwindUp4: mov eax,[esi+ecx*4-16] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4-16],eax ;U - put dword into destination UnwindUp3: mov eax,[esi+ecx*4-12] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4-12],eax ;U - put dword into destination UnwindUp2: mov eax,[esi+ecx*4-8] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4-8],eax ;U - put dword into destination UnwindUp1: mov eax,[esi+ecx*4-4] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4-4],eax ;U - put dword into destination lea eax,[ecx*4] ;V - compute update for pointer add esi,eax ;U - update source pointer add edi,eax ;V - update destination pointer UnwindUp0: jmp dword ptr TrailUpVec[edx*4] ;N - process trailing bytes ;----------------------------------------------------------------------------- align @WordSize TrailUpVec dd TrailUp0, TrailUp1, TrailUp2, TrailUp3 align @WordSize TrailUp0: mov eax,[dst] ;U - return pointer to destination pop esi ;V - restore esi pop edi ;U - restore edi ;V - spare M_EXIT align @WordSize TrailUp1: mov al,[esi] ;U - get byte from source ;V - spare mov [edi],al ;U - put byte in destination mov eax,[dst] ;V - return pointer to destination pop esi ;U - restore esi pop edi ;V - restore edi M_EXIT align @WordSize TrailUp2: mov al,[esi] ;U - get first byte from source ;V - spare mov [edi],al ;U - put first byte into destination mov al,[esi+1] ;V - get second byte from source mov [edi+1],al ;U - put second byte into destination mov eax,[dst] ;V - return pointer to destination pop esi ;U - restore esi pop edi ;V - restore edi M_EXIT align @WordSize TrailUp3: mov al,[esi] ;U - get first byte from source ;V - spare mov [edi],al ;U - put first byte into destination mov al,[esi+1] ;V - get second byte from source mov [edi+1],al ;U - put second byte into destination mov al,[esi+2] ;V - get third byte from source mov [edi+2],al ;U - put third byte into destination mov eax,[dst] ;V - return pointer to destination pop esi ;U - restore esi pop edi ;V - restore edi M_EXIT ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ;----------------------------------------------------------------------------- ; ; Copy down to avoid propogation in overlapping buffers. ; align @WordSize CopyDown: lea esi,[esi+ecx-4] ;U - point to 4 bytes before src buffer end lea edi,[edi+ecx-4] ;V - point to 4 bytes before dest buffer end ; ; See if the destination start is dword aligned ; test edi,11b ;U - test if dword aligned jnz short CopyLeadDown ;V - if not, jump shr ecx,2 ;U - shift down to dword count and edx,11b ;V - trailing byte count cmp ecx,8 ;U - test if small enough for unwind copy jb short CopyUnwindDown ;V - if so, then jump std ;N - set direction flag rep movsd ;N - move all of our dwords cld ;N - clear direction flag back jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes align @WordSize CopyUnwindDown: neg ecx ;U - negate dword count for table merging ;V - spare jmp dword ptr UnwindDownVec[ecx*4+28] ;N - unwind copy align @WordSize CopyLeadDown: mov eax,edi ;U - get destination offset mov edx,11b ;V - prepare for mask cmp ecx,4 ;U - check for really short string jb short ByteCopyDown ;V - branch to just copy bytes and eax,11b ;U - get offset within first dword sub ecx,eax ;U - to update size after lead copied jmp dword ptr LeadDownVec[eax*4-4] ;N - process leading bytes align @WordSize ByteCopyDown: jmp dword ptr TrailDownVec[ecx*4] ;N - process just bytes align @WordSize LeadDownVec dd LeadDown1, LeadDown2, LeadDown3 align @WordSize LeadDown1: mov al,[esi+3] ;U - load first byte and edx,ecx ;V - trailing byte count mov [edi+3],al ;U - write out first byte dec esi ;V - point to last src dword shr ecx,2 ;U - shift down to dword count dec edi ;V - point to last dest dword cmp ecx,8 ;U - test if small enough for unwind copy jb short CopyUnwindDown ;V - if so, then jump std ;N - set direction flag rep movsd ;N - move all of our dwords cld ;N - clear direction flag jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes align @WordSize LeadDown2: mov al,[esi+3] ;U - load first byte and edx,ecx ;V - trailing byte count mov [edi+3],al ;U - write out first byte mov al,[esi+2] ;V - get second byte from source shr ecx,2 ;U - shift down to dword count mov [edi+2],al ;V - write second byte to destination sub esi,2 ;U - point to last src dword sub edi,2 ;V - point to last dest dword cmp ecx,8 ;U - test if small enough for unwind copy jb short CopyUnwindDown ;V - if so, then jump std ;N - set direction flag rep movsd ;N - move all of our dwords cld ;N - clear direction flag jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes align @WordSize LeadDown3: mov al,[esi+3] ;U - load first byte and edx,ecx ;V - trailing byte count mov [edi+3],al ;U - write out first byte mov al,[esi+2] ;V - get second byte from source mov [edi+2],al ;U - write second byte to destination mov al,[esi+1] ;V - get third byte from source shr ecx,2 ;U - shift down to dword count mov [edi+1],al ;V - write third byte to destination sub esi,3 ;U - point to last src dword sub edi,3 ;V - point to last dest dword cmp ecx,8 ;U - test if small enough for unwind copy jb CopyUnwindDown ;V - if so, then jump std ;N - set direction flag rep movsd ;N - move all of our dwords cld ;N - clear direction flag jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes ;------------------------------------------------------------------ align @WordSize UnwindDownVec dd UnwindDown7, UnwindDown6, UnwindDown5, UnwindDown4 dd UnwindDown3, UnwindDown2, UnwindDown1, UnwindDown0 UnwindDown7: mov eax,[esi+ecx*4+28] ;U - get dword from source ;V - spare mov [edi+ecx*4+28],eax ;U - put dword into destination UnwindDown6: mov eax,[esi+ecx*4+24] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4+24],eax ;U - put dword into destination UnwindDown5: mov eax,[esi+ecx*4+20] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4+20],eax ;U - put dword into destination UnwindDown4: mov eax,[esi+ecx*4+16] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4+16],eax ;U - put dword into destination UnwindDown3: mov eax,[esi+ecx*4+12] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4+12],eax ;U - put dword into destination UnwindDown2: mov eax,[esi+ecx*4+8] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4+8],eax ;U - put dword into destination UnwindDown1: mov eax,[esi+ecx*4+4] ;U(entry)/V(not) - get dword from source ;V(entry) - spare mov [edi+ecx*4+4],eax ;U - put dword into destination lea eax,[ecx*4] ;V - compute update for pointer add esi,eax ;U - update source pointer add edi,eax ;V - update destination pointer UnwindDown0: jmp dword ptr TrailDownVec[edx*4] ;N - process trailing bytes ;----------------------------------------------------------------------------- align @WordSize TrailDownVec dd TrailDown0, TrailDown1, TrailDown2, TrailDown3 align @WordSize TrailDown0: mov eax,[dst] ;U - return pointer to destination ;V - spare pop esi ;U - restore esi pop edi ;V - restore edi M_EXIT align @WordSize TrailDown1: mov al,[esi+3] ;U - get byte from source ;V - spare mov [edi+3],al ;U - put byte in destination mov eax,[dst] ;V - return pointer to destination pop esi ;U - restore esi pop edi ;V - restore edi M_EXIT align @WordSize TrailDown2: mov al,[esi+3] ;U - get first byte from source ;V - spare mov [edi+3],al ;U - put first byte into destination mov al,[esi+2] ;V - get second byte from source mov [edi+2],al ;U - put second byte into destination mov eax,[dst] ;V - return pointer to destination pop esi ;U - restore esi pop edi ;V - restore edi M_EXIT align @WordSize TrailDown3: mov al,[esi+3] ;U - get first byte from source ;V - spare mov [edi+3],al ;U - put first byte into destination mov al,[esi+2] ;V - get second byte from source mov [edi+2],al ;U - put second byte into destination mov al,[esi+1] ;V - get third byte from source mov [edi+1],al ;U - put third byte into destination mov eax,[dst] ;V - return pointer to destination pop esi ;U - restore esi pop edi ;V - restore edi M_EXIT _MEM_ endp end
-
So, und wer vernuenftig programmiert, dem macht es der Compiler u.U. mit drei vier Befehlen inline. Also nicht von dem Spaghetti-Code ablenken lassen und weiterhin memmove verwenden.
-
Wie meinst du das ? Wie soll ich was mit inline machen ? Was bedeutet inline ?
-
Schon klar...wenn man selber proggt wird man wohl kaum so eine Routine basteln Die Frage war nur was der assembler draus macht und ich habs ihm gezeigt Ist eben nur eine auf Intel 32 hochoptimierte Assemblerroutine...man kann das ganze auch in einfachen Schritten machen...
Im übrigens sollte man Funktionen aus der Runtimelibrary sowieso nie selbst neu erfinden...die in der RTL sind bewährt und jahrelang ausgereift..da lieber auf den eigenen Code achten
PS: "dem macht es der Compiler u.U. mit drei vier Befehlen inline"...nein, im allgemeinen wird er die runtime-library-Funktionen benutzen...dazu sind sie geschrieben worden
-
Mir geht es darum, dass mein C-Programm auf älteren Rechnern zu langsam ist. Und das liegt daran, daß bei jedem Schleifendurchgang ein 64000 Byte-Speicherblock kopiert wird. Ich mache das mit dem Befehl movmem(ist äquivalent zu memmove). Und ich dachte, man könnte das Kopieren etwas optimieren. Denn die Befehle aus C machen ja noch mehrere Tests, ob sich die Blöcke überschneiden und solche Test brauche ich eigentlich nicht. Hat jemand eine Idee, wie ich diesen Teil meines Programms beschleunigen könnte ?
-
@TriPhoenix: Mit inline meinte ich, dasz der Compiler - zumindest war es mit C fuer WindowsCE so - nicht direkt eine Funktion aufruft sondern den Code analysiert und zum Beispiel lieber vier MOV in Folge schreibt als das ganze mit einem REP MOVSW. Das ist den Architekturen teilweise lieber.
Wenn du im Quellcode a=b+c schreibst, dann ruft der Compiler fuer die Zuweisung und Addition auch keine Funktion auf, oder? Ach ja, a,b,c Element aus Z.
-
Original erstellt von Steffen Vogel:
Mir geht es darum, dass mein C-Programm auf älteren Rechnern zu langsam ist. Und das liegt daran, daß bei jedem Schleifendurchgang ein 64000 Byte-Speicherblock kopiert wird. Ich mache das mit dem Befehl movmem(ist äquivalent zu memmove). Und ich dachte, man könnte das Kopieren etwas optimieren. Denn die Befehle aus C machen ja noch mehrere Tests, ob sich die Blöcke überschneiden und solche Test brauche ich eigentlich nicht. Hat jemand eine Idee, wie ich diesen Teil meines Programms beschleunigen könnte ?Was sind denn "aeltere Rechner"?
Die RTLs so wie die oben sind natuerlich auf Pentium und hoeher optimiert...bei niedrigeren Rechnern greift das ganze natuerlich nicht...und wenn du auf alle checks etc. verzichtest sollte ausreichen:void mymemmove(void *source, void *destination, int count) { int i; for (i = 0; i < count; i++) *(destination + i) = *(source + i); }
Achtung, das kuemmert sich nicht die Bohne um ueberlappungen usw Kannst ja mal gucken obs was an Speed bringt
Wenn das ganze in asm sein soll, waere eine angabe des Zielrechners guenstig..soll ja schnell sein...abe rim Allgemeinen optimiert der C-Compiler die routine wohl auch so
-
Korrektur...mich hat gerade wer darauf aufmerksam gemacht dass das ganze noch netter geht
void mymemmove(char *src, char *dest, int count) { while (count--) *(dest++) = *(src++); }
-
Original erstellt von <TriPhoenix_ohne_login>:
**Korrektur...mich hat gerade wer darauf aufmerksam gemacht dass das ganze noch netter geht
**Derjenige hat Dich verkohlt.
Der neue Code mit dem while ist genau 0% schneller als der mit dem for. Schließlich kann der optimierende Compiler diese Kleinigkeiten selber.
Was aber signifikant beschleunigen würde, wäre dafür int* zu nehmen und nur 1/4 der Schleifendurchläufe zu brauchen. Dazu sollte der Speicher natürlich auch fein aligned sein. Dafür soll mal Steffen Vogel selber sorgen.
Und REPNE MOV ah für sowas doch immer fein aus, oder?
-
Aber mal was andereres. Steffen Vogel meint doch, er will einen Speicherbereich kopieren, also nicht verschieben! Wieso sollte er dann memmove() verwenden? Ist doch eigentlich unsinnig!
Besonders weil diese 64kByte verdammt nach DoubleBuffering in Modus 0x13 klingen! Und da wird es immer noch schneller sein, wenn er das normale memcopy() verwendet, als wenn er sich jetzt eine neue Routine bastelt! Weil ein Hardware "REP MOVSW" wird immer noch schneller sein als ein wiederholtes "MOV" - sogar wenn vorher irgendwelche Grenzueberschreitungen getestet werden sollten!
Hier ein Stueck Code, das ab 80286 funktioniert. Es kopiert aus einem Buffer auf den Bildschirm!cld lds SI, Poi mov AX, $A000 mov ES, AX xor DI, DI mov CX, 32000 rep movsw
-
Also bisher hab ich den Unterschied zwischen memmove und memcpy hier noch net gesehen (In den Runtime-Librarys sind das auch dieselben routinen soweit ichs gefunden habe :))
Aber ich schätze du hast genau den Verwendungszweck getroffen
-
memcopy: Stures Kopieren ohne Grenzen zu ueberwachen.
memmove: Kopieren mit Ueberwachung von Grenzen.
-
@RainerSP: Könntest du den Code noch kommentieren ? Ich blick da nämlich nicht ganz durch.
-
@RainerSP: Auch wenn ich nicht alles verstehe, es funktioniert prächtig, und ich kann tatsächlich einen Geschwindigkeitsvorteil erzielen. Ist es möglich, das kopieren über mov abzuwickeln und somit ein paar Clocks zu sparen ?
-
Original erstellt von RainerSp:
**Aber mal was andereres. Steffen Vogel meint doch, er will einen Speicherbereich kopieren, also nicht verschieben! Wieso sollte er dann memmove() verwenden? Ist doch eigentlich unsinnig!
Besonders weil diese 64kByte verdammt nach DoubleBuffering in Modus 0x13 klingen! Und da wird es immer noch schneller sein, wenn er das normale memcopy() verwendet, als wenn er sich jetzt eine neue Routine bastelt! Weil ein Hardware "REP MOVSW" wird immer noch schneller sein als ein wiederholtes "MOV" - sogar wenn vorher irgendwelche Grenzueberschreitungen getestet werden sollten!
Hier ein Stueck Code, das ab 80286 funktioniert. Es kopiert aus einem Buffer auf den Bildschirm!cld ; aufsteigende Richtung bei StringBefehlen (REP) erzwingen! lds SI, Poi ; ds:[si] beinhaltet den Zeiger auf den Puffer mov AX, $A000 ; Segment der Graphikkarte mov ES, AX ; nach ES kopieren. Geht nicht direkt! xor DI, DI ; Offset auf Null setzen. Aequiv. zu mov DI, 0 mov CX, 32000 ; 32000 mal rep movsw ; 2 Byte kopieren (w) mov cx, 16000 ; oder auch 16000 mal 4 Byte kopieren (d) rep movsd ; erst ab 80386
**
Zum Thema per MOV umsetzen: Willst du wirklich 16000 mal einen MOV Befehl hinschreiben? Wuerde auf jeden Fall nicht so toll sein!
[ Dieser Beitrag wurde am 04.07.2002 um 22:44 Uhr von RainerSp editiert. ]
[ Dieser Beitrag wurde am 04.07.2002 um 22:46 Uhr von RainerSp editiert. ]
-
@RainerSP:
Mit dem mov dachte ich eher, dass man das in einer Schleife abwickelt, oder wäre das am Ende doch langsamer.Wie kann man mit möglich wenigen Clocks einen 64k-Bereich gleich 0 setzen ? Ich nehme dafür setmem, aber ich habe über den Debugger herrausgefunden, dass setmem auf memcpy zurückgreift. Wie würdet ihr so einen Bereich löschen.
-
@RainerSP: Das mit dem $A000 funktioniert nur unter Borland C++ 2.0. Unter 3.1 liefert er den Fehler, das er das $A000 nicht versteht. Gibt es da noch einen anderen Weg, die Bildschirmadresse hinzuschreiben ?
-
Ich rate mal mit
Versuch mal 0xA000
-
Geht auch nicht, hab ich schon probiert.