Optimizer
- remi_meier
- Beiträge: 1078
- Registriert: 29.08.2004 20:11
- Wohnort: Schweiz
- remi_meier
- Beiträge: 1078
- Registriert: 29.08.2004 20:11
- Wohnort: Schweiz
http://mypage.bluewin.ch/remimeier/zip/Optimierer.zip
Es ist vollbracht, denk ich zumindest (wird wohl noch Bugs haben...)
Pseudomakros sind nun in PB möglich! Warum Pseudo? Weil es einige
Einschränkungen gibt die es zu beachten gilt:
- diese Makros sind etwas langsamer als 'wirkliche' Makros!
- es muss mindestens ein Parameter angegeben werden, auch wenn der
nicht benötigt wird (sry, bin unfähig)
- KEINE LOKALEN VARIABLEN
- siehe Help.txt -> Macros!
- die mitgelieferte Userlib wird dazu benötigt.
Beispiel:
Makros: 1268
PB-Proc: 2229
Deeem optimized proc: 1470 (great work!)
Vorsicht mit dem deklarieren vieler Makros! Die Codegrösse steigt immens!
Danke fürs Testen!
greetz
Remi
Es ist vollbracht, denk ich zumindest (wird wohl noch Bugs haben...)
Pseudomakros sind nun in PB möglich! Warum Pseudo? Weil es einige
Einschränkungen gibt die es zu beachten gilt:
- diese Makros sind etwas langsamer als 'wirkliche' Makros!
- es muss mindestens ein Parameter angegeben werden, auch wenn der
nicht benötigt wird (sry, bin unfähig)
- KEINE LOKALEN VARIABLEN
- siehe Help.txt -> Macros!
- die mitgelieferte Userlib wird dazu benötigt.
Beispiel:
Code: Alles auswählen
Structure VECTOR
x.f
y.f
EndStructure
OptimizerStartMacro()
Procedure.f Length(*a.VECTOR)
ProcedureReturn Sqr(*a\x * *a\x + *a\y * *a\y)
EndProcedure
OptimizerStopMacro()
Procedure.f Length2(*a.VECTOR)
ProcedureReturn Sqr(*a\x * *a\x + *a\y * *a\y)
EndProcedure
a.VECTOR
a\x = 2
a\y = 4
Delay(500)
#n = 99999999
time1 = timeGetTime_()
For z = 0 To #n
c.f = Length(@a)
Next
time1 = timeGetTime_() - time1
time2 = timeGetTime_()
For z = 0 To #n
c.f = Length2(@a)
Next
time2 = timeGetTime_() - time2
MessageRequester("",Str(time1) + " " + Str(time2))PB-Proc: 2229
Deeem optimized proc: 1470 (great work!)
Vorsicht mit dem deklarieren vieler Makros! Die Codegrösse steigt immens!
Danke fürs Testen!
greetz
Remi
- remi_meier
- Beiträge: 1078
- Registriert: 29.08.2004 20:11
- Wohnort: Schweiz
Da ich im Forum schon wegen der (zum Teil sehr) unterschiedlichen Programm-Ausführungszeiten der verschiedenen PB4-Betas rumgemosert habe hier nun ein (weiterer) Versuch, ungünstiges "im-Speicher-zum-Liegen-kommen" zu korrigieren.
Jaja, es geht um mein heissgeliebtes Thema "Align"
!
Die oben genannten Laufzeit-Unterschiede dürften allesamt mit dem Thema "Alignment" zu tun haben und nicht mit irgendeiner schlechten Programmierung seitens des PB-Teams oder was sonst noch vermutet wurde.
Also hier ein neuer Versuch von AliSet:
Der gravierende Unterschied zum Vorgänger ist, das für zu durchlaufenden Code nur noch 1 NOP zugelassen wird.
AliSet und die eigene Quelldatei ins Compilers-Verzeichnis von PB kopieren!
Ich hoffe, es testet mal jemand!
Gruss
Helle
Jaja, es geht um mein heissgeliebtes Thema "Align"
Die oben genannten Laufzeit-Unterschiede dürften allesamt mit dem Thema "Alignment" zu tun haben und nicht mit irgendeiner schlechten Programmierung seitens des PB-Teams oder was sonst noch vermutet wurde.
Also hier ein neuer Versuch von AliSet:
Code: Alles auswählen
;--------------------- Setzen Alignment, K.Helbing ("Helle"), PureBasic 4.00 Beta 7, 26.04.2005 -------------------
;- Alignment: Das Legen von CPU-Instruktionen (und auch Daten!) auf Speicheradressen, die ohne Rest durch 4 teilbar
;- sind (oder anderer Wert, der eine Zweier-Potenz sein muss; bei einem 32-Bit Betriebssystem aber sinnvollerweise
;- 4). Auf diese Speicheradressen kann effektiver zugegriffen werden als auf "ungerade", das Programm sollte also
;- schneller werden. Dies zeigt sich messbar natürlich nur bei etwas größeren Anwendungen (EXE!); ein Zehnzeiler in
;- einer (fast) Endlos-Schleife als Test ist Nonsens. Ein Schachprogramm lief ca.7-8% schneller.
;- Da es Unfug ist, jede CPU-Instruktion zu "alignmentieren", wurden in Programmen häufig vorkommende Instruktionen
;- ausgewählt, die 4-oder 8-Byte langen OP-Code haben.
;- Es besteht natürlich immer die "Gefahr", dass eigentlich gut liegende Befehle durch an anderer Stelle eingeschobene
;- NOP´s auf schlechtere "Plätze" verschoben werden. Dann ist eben der Gewinn geringer bzw. garnicht da. Probieren!
;- Gesetzt wird Alignment immer nach - JMP
;- - RET
;- wenn nur 1x NOP nötig dann vor - CMP WORD[MEM],WERT bei WERT <80h ist 8-Byte-Opcode
;- - CMP [16-Bit-REG],WERT ist 4-Byte-Opcode für Wert<80h
;- - MOV " " "
;- - ADD " " "
;- - SUB " " " für Wert<80h wenn nicht AX-Register
;- - ADC " " "
;- - SBB " " "
;- - AND " " "
;- - SHL " " " für Wert >1
;- - SHR " " " "
;- - LEA in Verbindung mit [ESP+...]" Alle ausser ESP+0
;- - PUSH " " "
;- - INC " " "
;- - DEC " " "
;- - FADD " " "
;- - FLD " " "
;- Würden 2 oder gar 3 NOP´s nötig sein, wird auf Alignment verzichtet (dies ist die grosse Änderung im Vergleich
;- zur Vorgänger-Version).
;- Erweiterungen sind jederzeit möglich.
;- Die EXE-Datei wird (natürlich) um die Anzahl der eingefügten NOP´s grösser.
;- Je nach Datei-Grösse kann der Durchlauf durchaus einige Minuten dauern.
;- Empfehlung: Das Ganze in einer RAM-Disk ablaufen lassen (auch wegen der vielen HD-Zugriffe, aber keine Angst!).
;- Als "Wiederfinder" in der EXE-Datei wird der Befehl UD2 verwendet, den ja wohl keiner in seinem Programm
;- verwendet ;).
;- WICHTIG! AliSet und die eigene Quelldatei ins Compilers-Verzeichnis von PB kopieren!
;- Quell- oder PB-Dateien werden nicht verändert; überschrieben wird nur die evtl. vorhandene EXE-Datei.
Global FILELAENGEA.l = $0 ;Filelänge ASM-Datei
Global FILELAENGEE.l = $0 ;Filelänge EXE-Datei
Global S1.l = $47494c41 ;ALIG
Global S2.l = $0a34204e ;N 4 mit LF
Global S3.l = $0a324455 ;UD2 mit LF
Global M1.l = $564f4d ;MOV Leerzeichen werden unten auf Null gesetzt!
Global M2.l = $44524f57 ;WORD
Global M3.l = $504d43 ;CMP
Global M4.l = $434e49 ;INC
Global M5.l = $434544 ;DEC
Global M6.l = $434441 ;ADC
Global M7.l = $424253 ;SBB
Global M8.l = $504d4a ;JMP
Global M9.l = $0a544552 ;RET
Global M10.l = $444441 ;ADD
Global M11.l = $4c4853 ;SHL
Global M12.l = $524853 ;SHR
Global M13.l = $425553 ;SUB
Global M14.l = $0b505345 ;ESP+
Global M15.l = $41454c ;LEA
Global M16.l = $48535550 ;PUSH
Global M17.l = $444e41 ;AND
Global M18.l = $44444146 ;FADD
Global M19.l = $444c46 ;FLD
Global INSTR.l = $0
Global WT.b = $0
Global WT1.b = $0
Global *MemoryID.l = $0
Global PROGID.l = $0
Global PROG$
;-------- Abfrage Name der Quell-Datei
PROG$ = InputRequester("PB-Quelldatei","Bitte Quelldatei ohne Erweiterung (.PB) eingeben!","")
;-------- Test, ob Dateiname gültig ist
If ReadFile(1,PROG$ + ".PB")
ElseIf MessageRequester("Status", "Datei nicht gefunden!")
End
EndIf
CloseFile(1)
;-------- erstmal die ASM-Datei zur Bearbeitung erstellen ("commented"), "exe" damit Programm nicht
;-------- startet sondern nur EXE-Datei erstellt wird. Datei kann gelöscht werden.
MessageRequester("Status", "Nach der OK-Bestätigung bitte warten bis Fertigmeldung erscheint!")
PROGID = RunProgram("PBCompiler.exe", PROG$ + ".pb /inlineasm /quiet /commented /exe " + PROG$ + "O.exe", "",#PB_Program_Hide | #PB_Program_Open)
WaitProgram(PROGID)
CloseProgram(PROGID)
CopyFile("PureBasic.asm","OldPureBasic.asm") ;für Testzwecke, kann entfallen
OpenFile(1,"PureBasic.asm")
FILELAENGEA=Lof(1)
CloseFile(1)
;----------------------- RAM-Speicher für direkten Zugriff reservieren ----------------------
*MemoryID = AllocateMemory($2000000) ;jeweils 16 MB für die ASM- und EXE-Datei
; eventuell anpassen an Source-Datei
If *MemoryID = $0
MessageRequester("Fehler!", "Konnte den angeforderten Speicher nicht reservieren!")
End
EndIf
ReadFile(1,"PureBasic.asm")
ReadData(1,*MemoryID,FILELAENGEA)
CloseFile(1)
MOV esi,*MemoryID ;ESI ist damit tabu!
XOr bp,bp ;Merker ob ALIGN "gut" ist
MOV ebx,1h ;evtl. Test auf "Main" o.ä. für grösseren Wert, ist aber nicht relevant
MOV edx,0dfdfdfdfh
SEARCH: MOV al,[esi+ebx-1h]
CMP al,3bh ;";"
JE l_nofound4
CMP al,41h ;wegen z.B. PAND, FIADD und ähnlichen möglichen Fehlinterpretationen
JAE l_nofound1
MOV eax,[esi+ebx]
And eax,edx ;Kleinbuchstaben-Bit löschen, Leerzeichen wird Null!
MOV edi,ebx ;ist die eventuelle Einfüge-Stelle
MOV INSTR,eax ;für z.B. Test ob WORD
CMP eax,M18 ;FADD
JE l_voradd3 ;Test auf esp+
CMP eax,M19 ;FLD
JE l_voradd3
CMP eax,M8 ;JMP
JE l_nachjmp
CMP eax,M9 ;RET
JE l_nachjmp
CMP eax,M3 ;CMP
JE l_vorcmp
CMP eax,M10 ;ADD
JE l_vorcmp
;JE l_voradd ;mögliche Variationen, um Ergebnis evtl. zu verbessern (auch komplettes
; Weglassen von Abfragen möglich)
CMP eax,M1 ;MOV
JE l_voradd
CMP eax,M11 ;SHL
JE l_vorshift
CMP eax,M12 ;SHR
JE l_vorshift
CMP eax,M13 ;SUB
JE l_vorcmp
;JE l_voradd
CMP eax,M6 ;ADC
;JE l_vorcmp
JE l_voradd
CMP eax,M7 ;SBB
;JE l_vorcmp
JE l_voradd
CMP eax,M15 ;LEA
JE l_voradd3
CMP eax,M16 ;PUSH
JE l_voradd3
CMP eax,M4 ;INC
JE l_voradd3
CMP eax,M5 ;DEC
JE l_voradd3
CMP eax,M17 ;AND
JE l_vorcmp
;JE l_voradd
JMP l_nofound1
;----------------------- Align NACH einem JMP oder RET einfügen ---------------------------
NACHJMP: INC ebx
CMP byte[esi+ebx],0ah ;Ende der Zeile?
JNE l_nachjmp ;nein
INC ebx
MOV edi,ebx ;Einfügestelle
INC bp ;Merker für "gutes" ALIGN
JMP l_found
;---------------- Align VOR div.Instruktionen bei bestimmten Konstellationen ---------------
VORCMP: CALL l_wordtest
XOr ecx,ecx ;für Werttest
CMP WT,cl ;Null?
JE l_voradd2 ;kein Word, nächster Test
;------------------ ist WORD ----------------------------
NOKLAMMER: INC ebx
MOV al,[esi+ebx]
CMP al,5bh ;[
JNE l_noklammer
NOKLAMMER1: INC ebx
MOV al,[esi+ebx]
CMP al,20h
JE l_noklammer1 ;Leerzeichen
CMP al,5fh ;_ als Kennung für Variable
JE l_werttest
CMP al,5dh ;]
JE l_nofound1
JMP l_noklammer1
WERTTEST: INC ebx
MOV al,[esi+ebx]
CMP al,2ch ;Komma
JNE l_werttest
WERTTEST1: INC ebx
MOV al,[esi+ebx]
WERTTEST3: CMP al,30h ;Leerzeichen oder führende Nullen
JBE l_werttest1
WERTTEST2: CMP al,0ah
JE l_auswertungd
CMP al,68h ;h
JE l_auswertungh
CMP al,48h ;H
JE l_auswertungh
CMP al,20h
JE l_werttest4 ;für dez bis Zeilenende
SHL ecx,8h
MOV cl,al
WERTTEST4: INC ebx
MOV al,[esi+ebx]
JMP l_werttest2
AUSWERTUNGH: TEST ecx,0ffff0000h
JNZ l_nofound1
CMP ch,38h
JAE l_nofound1
JB l_found
AUSWERTUNGD: TEST ecx,0ff000000h
JNZ l_nofound1
CMP ch,39h
JA l_nofound1 ;Register
MOV eax,ecx
SHR eax,8h
CMP ah,31h
JA l_nofound1
JB l_found
CMP ch,32h
JA l_nofound1
JB l_found
CMP cl,38h
JAE l_nofound1
JMP l_found
;------------------------ Ende Werttest ----------------------------------------------
VORADD: ADD ebx,3h
VORADD1: INC ebx
VORADD2: MOV al,[esi+ebx]
CMP al,5bh ; [
JE l_nofound3
And al,dl
JZ l_voradd1
MOV WT,al
INC ebx
MOV al,[esi+ebx] ;Test auf 2.Buchstaben
CMP al,5fh ;_ Variablen aussieben
JE l_nofound1
And al,dl
MOV WT1,al
CMP al,58h ;X? für AX.BX,CX,DX
JE l_isregister
CMP al,49h ;I? für DI,SI
JE l_isregister
CMP al,50h ;P? für BP
JE l_isregister
;-------------------------------------- Suche nach ESP+Wert, Wert>0 ------------
VORADD3: INC ebx
MOV eax,[esi+ebx]
CMP al,0ah
JE l_nofound1
And eax,edx
CMP eax,M14 ;ESP+?
JNE l_voradd3
ADD ebx,3h
VORADD4: INC ebx
MOV al,[esi+ebx]
CMP al,5dh ;]
JE l_nofound1
CMP al,31h
JAE l_found ;grösser Null
JMP l_voradd4
ISREGISTER: INC ebx
MOV al,[esi+ebx]
CMP al,30h ;Leerzeichen, Komma, Null
JB l_isregister
JE l_isregister1
CMP al,39h
JA l_nofound1
ISREGISTER1: MOV ecx,INSTR
CMP ecx,M1 ;MOV?
JE l_found ;kein Werttest
CMP WT,41h
JE l_found ;ist AX-Register DX???
;CMP WT,44h ;nochmal in Ruhe anschauen, ob lohnend
;JNE l_isregister3
;CMP WT1,58h
;JE l_found
ISREGISTER3: XOr ecx,ecx
JMP l_werttest3
VORSHIFT: CALL l_wordtest
CMP WT,1h
JNE l_vorshift2
ADD ebx,3h
JMP l_found
VORSHIFT1: INC ebx
VORSHIFT2: MOV al,[esi+ebx]
CMP al,5fh ;_ Variablen aussieben Klammer? Sollte hier egal sein
JE l_nofound1
And al,dl
JZ l_vorshift1
INC ebx
MOV al,[esi+ebx] ;Test auf 2.Buchstaben
And al,dl
CMP al,58h ;X? für AX.BX,CX,DX
JE l_isregister2
CMP al,49h ;I? für DI,SI
JE l_isregister2
CMP al,50h ;P? für BP
JNE l_nofound1
ISREGISTER2: INC ebx
MOV al,[esi+ebx]
CMP al,2ch ;Leerzeichen, Komma
JBE l_isregister2
INC ebx
CMP al,31h
JE l_nofound1 ;1-er Shifts haben eigenen Opcode!
CMP al,39h ;Test nicht für Wert, sondern auf Register!
JA l_nofound1
;-------------------------- was lohnenswertes (hoffentlich!) gefunden -----------------------------
FOUND: ADD FILELAENGEA,8h ;für 1x ALIGN wird die Datei 8 Byte länger
INC ebx
; ADD ebx,6h
;---------------------------- Restdatei nach "hinten" verschieben ---------------------------------
MOV ecx,FILELAENGEA
SUB ecx,4h
SCHIEBEN1: MOV eax,dword[esi-8h+ecx]
MOV dword[esi+ecx],eax
SUB ecx,4h
CMP ecx,edi
JAE l_schieben1
;----------------------------- "ALIGN 4" einfügen mit LF ------------------------------------------
MOV eax,S1
MOV [esi+edi],eax
MOV eax,S2
MOV dword[esi+4h+edi],eax
Or bp,bp
JNE l_genehm
PUSH edi ;Einfügestelle von ALIGN 4
;------------------------------ "UD2" einfügen mit LF -------------------------------------
NACHALI: INC ebx
CMP byte[esi+ebx],0ah ;Ende der Zeile?
JNE l_nachali ;nein
INC ebx
MOV edi,ebx ;Einfügestelle
;----------------------------- "UD2" einfügen mit LF -------------------------------------
ADD FILELAENGEA,4h ;für 1x UD2 wird die Datei 4 Byte länger
INC ebx
ADD ebx,6h ;bei Gelegenheit mal optimieren
;---------------------------- Restdatei nach "hinten" verschieben --------------------------
MOV ecx,FILELAENGEA
SUB ecx,4h
SCHIEBEN2: MOV eax,dword[esi-4h+ecx]
MOV dword[esi+ecx],eax
SUB ecx,4h
CMP ecx,edi
JAE l_schieben2
;----------------------------- "UD2" einfügen mit LF ---------------------------------------
MOV eax,S3
MOV [esi+edi],eax
PUSHAD
OpenFile(1,"PureBasic.asm")
WriteData(1,*MemoryID,FILELAENGEA)
CloseFile(1)
PROGID = RunProgram("PBCompiler.exe", PROG$ + ".pb /inlineasm /quiet /reasm /exe " + PROG$ + ".exe", "",#PB_Program_Hide | #PB_Program_Open)
WaitProgram(PROGID)
CloseProgram(PROGID)
OpenFile(2,PROG$ + ".exe")
FILELAENGEE=Lof(2)
ReadFile(2,PROG$ + ".exe")
ReadData(2,*MemoryID+$1000000,FILELAENGEE)
CloseFile(2)
POPAD
MOV ecx,1000000h
ADD ecx,FILELAENGEE
MOV ax,0b0fh ;in der EXE-Datei Suche nach Opcode für UD2 (0F0B)
SEARCH04: DEC ecx
CMP [esi+ecx],ax
JNE l_search04
MOV ax,0fh ;max. soviel Bytes zurück verfolgen auf der Suche nach NOP´s
; evtl. verfeinern wegen möglicher Kommentare!
FOUND05: DEC ecx
CMP Word[esi+ecx],9090h ;2xNOP und damit auch 3xNOP
JE l_found06
DEC ax
JNZ l_found05
INC bp ;"Gut" also nur, wenn 1xNOP
FOUND06:
;-------- Restdatei erstmal wieder nach "vorn" verschieben um 4 Bytes UD2 ---------
MOV ecx,FILELAENGEA
SUB ecx,4h
SCHIEBEN3: MOV eax,dword[esi+4h+edi]
MOV dword[esi+edi],eax
ADD edi,4h
CMP edi,ecx
JB l_schieben3
SUB FILELAENGEA,4h
POP edi
Or bp,bp
JNE l_genehm
;-------- Restdatei wieder nach "vorn" verschieben um 8 Bytes ALIGN 4 ---------
MOV ecx,FILELAENGEA
SUB ecx,4h
SCHIEBEN4: MOV eax,dword[esi+8h+edi]
MOV dword[esi+edi],eax
ADD edi,4h
CMP edi,ecx
JB l_schieben4
SUB FILELAENGEA,8h
GENEHM: XOr bp,bp
;ADD ebx,2h
;----------------------------------- Rest der Datei ----------------------------------------
NOFOUND1: INC ebx
CMP ebx,FILELAENGEA
JB l_search
JMP l_fertig
NOFOUND3: INC ebx
CMP ebx,FILELAENGEA
JAE l_fertig
CMP byte[esi+ebx],0ah ;LF?
JE l_search
JMP l_nofound3
NOFOUND4: INC ebx
CMP ebx,FILELAENGEA
JAE l_fertig
CMP byte[esi+ebx],0ah ;LF?
JNE l_nofound4
INC ebx
JMP l_search
;-------------------------------------- Unterprogramme ---------------------------------------------------
WORDTEST: MOV WT,0h
ADD ebx,3h
LEERW: INC ebx
MOV eax,[esi+ebx]
And eax,edx
Or al,al
JE l_leerw
CMP eax,M2 ;WORD
JNE l_nofound2
MOV WT,1h
NOFOUND2: RET
FERTIG:
OpenFile(1,"PureBasic.asm")
WriteData(1,*MemoryID,FILELAENGEA)
CloseFile(1)
RunProgram("PBCompiler.exe", PROG$ + ".pb /inlineasm /quiet /reasm /exe " + PROG$ + ".exe", "",2)
MessageRequester("Status", "Geschafft!")
End
AliSet und die eigene Quelldatei ins Compilers-Verzeichnis von PB kopieren!
Ich hoffe, es testet mal jemand!
Gruss
Helle
@MVXA: Ich versuche die gebotenen Möglichkeiten so gut wie möglich zu nutzen
! Assembler dort, wo es wirklich auf Speed ankommt und PB dort, wo ich mich sonst mit Win-API rumschlagen müsste (z.B. Bildschirmausgaben).
Übrigens: Auf [url]http://www.mdcc-fun.de/k.helbing gibts eine erweiterte Version mit gaaaanz vieeeeel PB-Anteil!
Gruss
Helle
Übrigens: Auf [url]http://www.mdcc-fun.de/k.helbing gibts eine erweiterte Version mit gaaaanz vieeeeel PB-Anteil!
Gruss
Helle
