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.
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.
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
Der gravierende Unterschied zum Vorgänger ist, das für zu durchlaufenden Code nur noch 1 NOP zugelassen wird.