Bereich möglichst schnell mit Werten füllen
-
Ich hab eine Grafikengine in Assembler und C geschrieben
und überlege nun wie ich am schnellsten den Bildschirm leere,
also den Buffer komplett mit 0en überschreibe
(das muss leider in jedem Frame wieder gemacht werden, kostet also ziemlich Zeit)im Moment hab ich eine umständliche loop Funktion genommen,
aber ich denke mit rep geht das noch irgendwie schnellerals nächstes bräuchte ich dann auch noch eine Funkion die einen Bereich möglichst schnell an eine andere Stelle im Speicher kopiert
(hab ich im Moment auch mit einer loop Funktion gelöst),
ich hab gehört, dass das aufjedenfall mit rep gehen soll, wie weis ich allerdings nichtvielleicht kann mir jemand von euch auf die Sprünge helfen
EDIT:
okay, hat sich erledigt, ging mit stosw und movsw ganz einfach
EDIT2:
na toll, hab jetzt die Zeit gemesen, meine loop-Konstruktion ist fast doppelt so schnell
und ich dacht immer rep wär performant
-
haben deine rep-befehle nur 16-bitter geschrieben und deine eigene loop gleich 32-bitter?
-
Ich hoffe mal das geht klar mit Copyrightvermerk ^^
Vielleicht findest du etwas, was dir hilft. _VEC_memzero habe ich allerdings nicht gefunden.page ,132 title memset - set sections of memory all to one byte ;*** ;memset.asm - set a section of memory to all one byte ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; contains the memset() routine ; ;******************************************************************************* .xlist include cruntime.inc .list page ;*** ;char *memset(dst, value, count) - sets "count" bytes at "dst" to "value" ; ;Purpose: ; Sets the first "count" bytes of the memory starting ; at "dst" to the character value "value". ; ; Algorithm: ; char * ; memset (dst, value, count) ; char *dst; ; char value; ; unsigned int count; ; { ; char *start = dst; ; ; while (count--) ; *dst++ = value; ; return(start); ; } ; ;Entry: ; char *dst - pointer to memory to fill with value ; char value - value to put in dst bytes ; int count - number of bytes of dst to fill ; ;Exit: ; returns dst, with filled bytes ; ;Uses: ; ;Exceptions: ; ;******************************************************************************* CODESEG extrn _VEC_memzero:near extrn __sse2_available:dword public memset memset proc \ dst:ptr byte, \ value:byte, \ count:dword OPTION PROLOGUE:NONE, EPILOGUE:NONE .FPO ( 0, 3, 0, 0, 0, 0 ) mov edx,[esp + 0ch] ; edx = "count" mov ecx,[esp + 4] ; ecx points to "dst" test edx,edx ; 0? jz short toend ; if so, nothing to do xor eax,eax mov al,[esp + 8] ; the byte "value" to be stored ; Special case large block zeroing using SSE2 support test al,al ; memset using zero initializer? jne dword_align cmp edx,0100h ; block size exceeds size threshold? jb dword_align cmp DWORD PTR __sse2_available,0 ; SSE2 supported? je dword_align jmp _VEC_memzero ; use fast zero SSE2 implementation ; no return ; Align address on dword boundary dword_align: push edi ; preserve edi mov edi,ecx ; edi = dest pointer cmp edx,4 ; if it's less then 4 bytes jb tail ; tail needs edi and edx to be initialized neg ecx and ecx,3 ; ecx = # bytes before dword boundary jz short dwords ; jump if address already aligned sub edx,ecx ; edx = adjusted count (for later) adjust_loop: mov [edi],al add edi,1 sub ecx,1 jnz adjust_loop dwords: ; set all 4 bytes of eax to [value] mov ecx,eax ; ecx=0/0/0/value shl eax,8 ; eax=0/0/value/0 add eax,ecx ; eax=0/0val/val mov ecx,eax ; ecx=0/0/val/val shl eax,10h ; eax=val/val/0/0 add eax,ecx ; eax = all 4 bytes = [value] ; Set dword-sized blocks mov ecx,edx ; move original count to ecx and edx,3 ; prepare in edx byte count (for tail loop) shr ecx,2 ; adjust ecx to be dword count jz tail ; jump if it was less then 4 bytes rep stosd main_loop_tail: test edx,edx ; if there is no tail bytes, jz finish ; we finish, and it's time to leave ; Set remaining bytes tail: mov [edi],al ; set remaining bytes add edi,1 sub edx,1 ; if there is some more bytes jnz tail ; continue to fill them ; Done finish: mov eax,[esp + 8] ; return dest pointer pop edi ; restore edi ret toend: mov eax,[esp + 4] ; return dest pointer ret memset endp end
-
haben deine rep-befehle nur 16-bitter geschrieben und deine eigene loop gleich 32-bitter?
das ganze läuft im 16-Bit Modus deswegen benutze ich ja auch stosw und nicht stosd und mein loop ist auch 16-Bit
ich hab das Ganz aber ungefähr so wie in Nekus Beispiel gemacht,
vielleicht ist die rep Möglichkeit einfach langsamer
-
scales of justice schrieb:
haben deine rep-befehle nur 16-bitter geschrieben und deine eigene loop gleich 32-bitter?
das ganze läuft im 16-Bit Modus deswegen benutze ich ja auch stosw und nicht stosd und mein loop ist auch 16-Bit
Im "16Bit-Modus" bist du nicht gezwungen, nur 16Bit-Befehle zu verwenden.
Deine Begruendung fuer die Verwendung von stosw anstelle von stosd ist so nicht stichhaltig.
Was meinst du mit "mein loop ist auch 16-Bit"?scales of justice schrieb:
ich hab das Ganz aber ungefähr so wie in Nekus Beispiel gemacht,
vielleicht ist die rep Möglichkeit einfach langsamerNicht ganz nachvollziehbar. Der zentrale Punkt in Nekus Code ist naemlich genau ein "rep stosd".
-
Im "16Bit-Modus" bist du nicht gezwungen, nur 16Bit-Befehle zu verwenden
natürlich nicht, aber ich benutze nur 16-Bit, um die Abwärtskompatibilität zu wahren
Deine Begruendung fuer die Verwendung von stosw anstelle von stosd ist so nicht stichhaltig
stosd gibt es auf alten Prozessoren so weit ich weis auch noch nicht
und wenn doch müsste es eigentlich langsamer sein, als zweimal stosw zu verwendenWas meinst du mit "mein loop ist auch 16-Bit"?
einfach dass ich für den auch 16-Bit Code und keinen 32-Bit Code verwendet habe,
dann wäre es klar warum loop schneller ist
-
am schnellsten geht's wenn man fpu und cpu parallel schreiben laesst:
fillloop: mov dword ptr [edi+ecx],eax mov dword ptr [edi+ecx+4],edx fst qword ptr [edi+ecx+8] add ecx,16 jnz fillloop
mit:
eax = 0 (oder womit auch immer man fuellen will)
edx = 0
st(0) = 0.0
ecx = -blocksize (muss durch 16 teilbar sein)
edi = bufferstart + blocksize (sollte 16-byte alligned sein)
-
mit mmx geht das am schnellsten, dazu gibt es paper bei amd. fix ist ebenfalls mit doubles und der fpu zu arbeiten, die kann dann auch 64bit am stück füllen.
fpu und "cpu" gleichzeitig arbeiten zu lassen dürfte nicht sooo viel bringen, das bottleneck ist nicht die cpu, sondern das ram... aber alles nötige steht im paper irgendwo auf der amd seite.
-
"am schnellsten" ist sowieso relativ
jedenfalls wird die antwort immer zumindest vom zielprozessor abhängen. und wenn das programm am ende auf einem 286er laufen soll und dort schnell genug ist, ist es doch sowieso egal, ob die gewählte variante auf einem athlon o.ä. suboptimal ist. rep stosx ist für pentium+ traditionell die schnellste variante große blöcke (mehrere cachezeilen) zu füllen - der befehl ist nachhaltig in dieser hinsichtig optimiert worden. für kleine blöcke gibt es verschiedene andere möglichkeiten. im übrigen sollte stets die größtmögliche wortbreite benutzt werden, selbst wenn der ergebende zugriff dann unausgerichtet wäre, ist das günstiger. und für alles <=486 ist die frage ohnehin absolut zu beantworten - sieht man von bestimmten zusätzlichen parametern ab, ist die ausführungszeit für diese prozessoren einfach und exakt im voraus zu bestimmen. im übrigen sei auf die software optimierungshandbücher von amd und intel verwiesen:
http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/25112.PDF
http://www.intel.com/design/pentium4/manuals/248966.htm