Seite 1 von 2

ClearMemory(*MemoryBuffer, length)

Verfasst: 03.03.2008 18:07
von Leonhard
Eine Funktion, die den angegeben Speicherwert löscht.
Kann man das noch schneller machen? Ich hab da schon ziemlich optimert (alte Funktion: 250% langsamer).

Code: Alles auswählen

;# <description>
;#   Diese Funktion überschreibt den angegebenen Speicher mit nullen.
;# </description>
;# <param attr="(pointer)" name="MemoryBuffer" type="void">
;#   Pointer des Speicherwertes, der mit nullen überschrieben werden soll
;# </param>
;# <param name="length" type="int">
;#   Länge des Speichers.
;# </param>
;# <optimize>
;# If length <= 0
;#   ; lösche Funktion, da sinnlos
;# EndIf
;# </optimize>
Procedure ClearMemory(*MemoryBuffer, length.l)
  ;/ verwendete Register: eax, ecx, edx
  
  ;/ Variablen beschreiben:
  !MOV ecx, dword[p.v_length]        ;/ lege Länge in Register ab
  !DEC ecx                           ;/ length - 1
  !MOV edx, dword[p.p_MemoryBuffer]  ;/ lege Buffer in Register ab
  
  ;for(eax = 0; eax<=ecx; eax++)
  !MOV eax, 0                        ;/ Zählervariable für For-Schleife
!p_ClearMemory.l_for_start:
  !CMP eax, ecx                      ;/ überprüfe Bedingung:
  !JG p_ClearMemory.l_for_end        ;/ if(eax > ecx) for_break;
  
  !MOV byte[edx], 0                  ;/ schreibe 0 in Speicher
  !INC edx                           ;/ addiere den Buffer
  
  !INC eax                           ;/ addiere die Zählervariable
  !JMP p_ClearMemory.l_for_start     ;/ überprüfe For-Bedingung
!p_ClearMemory.l_for_end:
EndProcedure

Verfasst: 03.03.2008 19:12
von Franky
Jo, geht, hier mal ein Code von Deeem (?) aus meinem PGS.

Code: Alles auswählen

Procedure FillMemoryL(address.l,copylength.l,wert.l)
CLD 
MOV Eax,wert
MOV Edi,address
MOV Ecx,CopyLength 
SHR Ecx,2 
REP STOSD 
EndProcedure

Verfasst: 03.03.2008 20:00
von Leonhard
Kommt drauf an, wie groß der Buffer ist. Ich habs mal ausgetestet und hab festgestellt, das meine Procedure ab 150 total langsam wird. Wenn es jetzt kleine Buffers sind, so ist meine schneller.

Hier jetzt meine Funktion kombiniert mit der von Deeem (?):

Code: Alles auswählen

Procedure ClearMemory(*MemoryBuffer, length.l)
  ;/ verwendete Register: eax, ecx, edx
  
  !MOV ecx, dword[p.v_length]        ;/ lege Länge in Register ab
  
  ;/ Überprüfe, ob die länge größer als 150 ist.
  ;/ Dies ist wichtig, damit der schnellste
  ;/ Algoritmus ausgewählt werden kann.
  !CMP ecx, 96h ; 96h = 150
  !JG p_ClearMemory.l_algoritmus_hight ; if(ecx > 96h)

  ;/ Variablen beschreiben:
!p_ClearMemory.l_algoritmus_low: ;/ Algoritmus für kleine Speicherwerte (<150 lenge)
  !DEC ecx                           ;/ length - 1
  !MOV edx, dword[p.p_MemoryBuffer]  ;/ lege Buffer in Register ab
  
  ;for(eax = 0; eax<=ecx; eax++)
  !MOV eax, 0                        ;/ Zählervariable für For-Schleife
!p_ClearMemory.l_for_start:
  !CMP eax, ecx                      ;/ überprüfe Bedingung:
  !JG p_ClearMemory.l_for_end        ;/ if(eax > ecx) for_break;
  
  !MOV byte[edx], 0                  ;/ schreibe 0 in Speicher
  !INC edx                           ;/ addiere den Buffer
  
  !INC eax                           ;/ addiere die Zählervariable
  !JMP p_ClearMemory.l_for_start     ;/ überprüfe For-Bedingung
!p_ClearMemory.l_for_end:

  !JMP p_ClearMemory.l_endProcedure

!p_ClearMemory.l_algoritmus_hight: ;/ Algoritmus für große Speicherwerte (>150 lenge)
  ;// integriert von FillMemoryL(...)
  !CLD
  !MOV eax, 0
  !MOV edi, dword[p.p_MemoryBuffer]
  !REP STOSB

!p_ClearMemory.l_endProcedure:
EndProcedure
Und nocht was: die Funktion, die du mir gegeben hast geht nur auf Längen, die durch 4 teilbar sind (halt long und so).
Hier ist deine Funktion für Bytes:

Code: Alles auswählen

Procedure FillMemoryB(*MemoryBuffer, length.l, Value.b) 
  !CLD
  !MOV eax, byte[p.v_Value]
  !MOV edi, dword[p.p_MemoryBuffer]
  !MOV ecx, dword[p.v_length]
  !REP STOSB
EndProcedure

Verfasst: 03.03.2008 21:20
von Thorium
Deine Prozedur ist so langsam, weil du in 1 Byte schritten schreibst. Es geht erheblich schneller, wenn du in 4Byte (32bit) Schritten schreibst.

Hier mal mein ASM-Code zum schnellen kopieren von Speicherblöcken. Da siehst du was ich meine.

Code: Alles auswählen

!push ecx
!shr ecx,2
!rep movsd
!pop ecx
!and ecx,3
!rep movsb
Da wird in 4 Byte Schritten kopiert und am Ende die übrigen Restbyte ermittelt und einzeln kopiert.

Verfasst: 03.03.2008 22:14
von Thorium
Ohne es getestet zu haben.
Das sollte den Job sehr schnell erledigen:

Code: Alles auswählen

Procedure ClearMemory(*MemoryBuffer, length.l)
  !xor eax,eax
  !mov edi,dword[p.p_MemoryBuffer]
  !mov ecx,dword[p.v_length]
  !mov ebx,ecx
  !shr ecx,2
  !rep stosd
  !mov ecx,ebx
  !and ecx,3
  !rep stosb
EndProcedure
Wenn du das oft hintereinander benötigst, bekommst mit nem Makro nochmal etwas Speed, da der Call weggespart wird.

Das CLD brauchst net. PureBasic sorgt dafür das das Direction-Flag sowieso glöscht ist. Das muss es auch, da viele Win-API-Funktionen ein gelöschtes Direction-Flag voraussetzen. Das heisst für dich, wenn du mal STD verwendest, nicht vergessen später wieder mit CLD zu löschen. Sonst kracht es später.

Edit: Habs nochmal geändert, zum sichern von ECX wird jetzt ein weiteres Register verwendet, anstatt der Stack.

Edit2:
Leonhard hat geschrieben:Kommt drauf an, wie groß der Buffer ist. Ich habs mal ausgetestet und hab festgestellt, das meine Procedure ab 150 total langsam wird. Wenn es jetzt kleine Buffers sind, so ist meine schneller.
Das kann ich nicht bestätigen. Habs jetzt mal bei mir getestet. Deine Prozedur ist in jedem Fall langsamer. Deeems ist die schnellste, dafür nur Längen, die durch 4 teilbar sind. Meine ist unwesentlich (desdo größer die Länge, desdo unwesentlicher) langsamer als Deems, dafür werden auf Längen die durch 1 teilbar sind korrekt abgearbeitet.

Verfasst: 09.04.2008 18:47
von LCD
Habe ein Benchmark durchgeführt, und es gibt so gut wie keinen Unterschied zu der WinAPI Funktion ZeroMemory_(*Memory,Length), was mich etwas irritiert. Normalerweise müsste Assembler etwas schneller sein.

Verfasst: 09.04.2008 19:29
von edel
Woher willst du wissen, wie diese Funktion aussieht und wie sehr sie optimiert
wurde ?

Verfasst: 09.04.2008 19:34
von Andreas_S
Die genaue Definition:

Code: Alles auswählen

void ZeroMemory(
  [in]  PVOID Destination,
  [in]  SIZE_T Length
);
Wenn ich mich nicht irre ist die ganze Win-API in C++, und das ist so stark optimiert das man das per assembler als Mensch eh nicht mehr wirklich nachschreiben kann... vor allem bei größeren Befehlen...

Zwischenfrage: Warum sollte man den Speicher löschen? Da kann man ihn doch gleich überschreiben, oder freigeben...

Verfasst: 09.04.2008 19:36
von edel
Weil jeder Speicher erst einmal geleert werden sollte, bevor man ihn ueberhaupt benutzt.

Verfasst: 09.04.2008 19:40
von Andreas_S
Ok, ja stimmt... kann ja noch was drauf sein...