Interpreter beschleunigen

Anfängerfragen zum Programmieren mit PureBasic.
Kevin
Beiträge: 236
Registriert: 11.06.2007 12:55

Interpreter beschleunigen

Beitrag von Kevin »

Hi,

ich möchte meinen Interpreter beschleunigen und hab dazu ein paar fragen.

So läuft es aktuell: (Codes sind nur zur Veranschaulichung funktionieren nicht)

Code: Alles auswählen

Repeat

Select ReadWord(File)
Case 1;+
;...
Case 2;-
....
Case 3;*
...
Case ...
...
...
case 200;extrem langsam da erst die anderen case' durchgegangen werden
...
EndSelect

Until ...
nur das Problem dabei ist um so mehr case' es gibt um so langsamer es wird.


deshalb hab ich das hier getestet:

Code: Alles auswählen

Procedure P01_Add()
; + ...
EndProcedure
Procedure P02_Sub()
; - ...
EndProcedure
Procedure P04_Div()
; * ...
EndProcedure
Procedure P200_Ka()
; * ...
EndProcedure
;...

Prototype Beispiel()

Structure ProzedurListe
   Beispiel.Beispiel[4]
EndStructure

Define ProzedurListe.ProzedurListe
ProzedurListe\Beispiel[1] = @P01_Add()
ProzedurListe\Beispiel[2] = @P02_Sub()
ProzedurListe\Beispiel[2] = @P04_Div()
ProzedurListe\Beispiel[200] = @P200_Ka()


Repeat

ProzedurListe\Beispiel[ReadWord(File)]()

Until ...
hier ist zwar jeder Aufruf gleich schnell, aber alles insgesamt langsamer da jetzt immer eine procedure aufgerufen werden muss...


Meine frage jetzt gibt es noch eine Möglichkeit das ganze schneller zu machen ohne proceduren dabei zu verwenden?
Benutzeravatar
TomS
Beiträge: 1508
Registriert: 23.12.2005 12:41
Wohnort: München

Re: Interpreter beschleunigen

Beitrag von TomS »

Was hast du gegen Proceduren?
Also das Lesen geht sicherlich sehr viel schneller, wenn du den ganzen Dateiinhalt mit ReadData() einliest und dann mit
Select PeekW(*pointer)
und *pointer + SizeOf(Word)
arbeitest.
Kevin
Beiträge: 236
Registriert: 11.06.2007 12:55

Re: Interpreter beschleunigen

Beitrag von Kevin »

TomS hat geschrieben:Was hast du gegen Proceduren?
ich habe nichts gegen Proceduren aber schon allein der Aufruf einer Procedure braucht Rechenleistung und ich suche eine noch schnellere Möglichkeit das ganze umzusetzen.
TomS hat geschrieben:Also das Lesen geht sicherlich sehr viel schneller, wenn du den ganzen Dateiinhalt mit ReadData() einliest und dann mit
Select PeekW(*pointer)
und *pointer + SizeOf(Word)
arbeitest.
so wird es aktuell schon gemacht der Code war nur da damit ihr euch grob vorstellen könnt wie das ganze abläuft.
Benutzeravatar
TomS
Beiträge: 1508
Registriert: 23.12.2005 12:41
Wohnort: München

Re: Interpreter beschleunigen

Beitrag von TomS »

Schneller geht's nicht. Außer du benutzt von Haus aus Integer, was aber die Scriptgröße vervierfacht.

Was du noch machen kannst (was du wahrscheinlich schon gemacht hast, nur nicht für nötig empfunden hast und mitzuteilen), ist das aufteilen deiner Befehle in Gruppen (Libraries).

Anstatt:

Code: Alles auswählen

Case 1:
Case 2:
;...
Case 9999999:

Code: Alles auswählen

Case 1 ;LibA
  Select next
    Case 1
        ;...
    Case 255
  EndSelect

Case 1 ;LibB
  Select next
    Case 1
        ;...
    Case 255
  EndSelect

Mal angenommen du hast 10 Libraries mit jeweils 10 Befehlen.
Dann musst du mit der linearen Methode 100 mal Case aufrufen.

Mit der neuen Methode nur maximal 20 mal.
Das ist also 5 mal schneller (~4.9 wegen bissal Overhead wegen erneutem Select)

Wenn du Byte nimmst anstatt Word bleibt dein Code gleich groß.
Falls du eine "Library" haben solltest die mehr als 254 Befehle hat, kannst du die ja aufteilen.
LibA1
LibA2
LibB
etc...
miscalculated
Beiträge: 4
Registriert: 11.04.2011 18:17

Re: Interpreter beschleunigen

Beitrag von miscalculated »

Soetwas war bei mir schneller als ein Select:

Code: Alles auswählen

; Springt zur nächsten Anweisung, muss hinter jeden Befehl
Macro NextInstruction()
	TARGET = JumpTable(ReadWord(Dateibla))
; Weiß nicht ob das auf 64bit Systemen genauso funktionieren würde:
	!JMP dword [p.v_TARGET]
EndMacro

Procedure Execute()
; Sprungtabelle mit allen Befehlen
Dim JumpTable(255)
JumpTable(0) = ?Add
JumpTable(0) = ?Sub
...

; alles ins rollen bringen:
NextInstruction()

?Add:
; addieren
NextInstruction()

?Sub:
; subtrahieren
NextInstruction()
...
EndProcedure
Eventuell sind bei dir auch noch Optimierungen in den Befehlen möglich, aber das musst du selbst wissen, wenn dass alles nicht hilft bleibt noch die Möglichkeit das Skript zu kompilieren.

PS: kannst auch das Konzept der Sprungtabelle nutzen und die Schleifenstruktur beibehalten:

Code: Alles auswählen

Procedure Execute()
	; Sprungtabelle mit allen Befehlen
	Dim JumpTable(255)
	JumpTable(0) = ?Add
	JumpTable(0) = ?Sub
	...

	; alles ins rollen bringen:
	Repeat
		; Zum passenden Befehl springen
		TARGET = JumpTable(ReadWord(Dateibla))
		; Weiß nicht ob das auf 64bit Systemen genauso funktionieren würde:
		!JMP dword [p.v_TARGET]
		?Add:
		; addieren
		Continue

		?Sub:
		; subtrahieren
		Continue
		...
	ForEver
EndProcedure
Kevin
Beiträge: 236
Registriert: 11.06.2007 12:55

Re: Interpreter beschleunigen

Beitrag von Kevin »

@TomS
genau das hätte ich auch noch einbauen können

@miscalculated
Super genau so was habe ich gesucht :allright:
Eventuell sind bei dir auch noch Optimierungen in den Befehlen möglich, aber das musst du selbst wissen, wenn dass alles nicht hilft bleibt noch die Möglichkeit das Skript zu kompilieren.

PS: kannst auch das Konzept der Sprungtabelle nutzen und die Schleifenstruktur beibehalten:
werde ich machen.



Vielen Dank euch beiden ihr habt mir sehr weitergeholfen.
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 7028
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Interpreter beschleunigen

Beitrag von STARGÅTE »

Bei der Lösung von miscalculated hast du halt keine möglichkeit Parameter oder Rückgabewerte zu erhalten, weil du die ganze Zeit nur "springst".

Wenn du also selber eine Art Stack/Register schreibst, in dem die Sachen verarbeitet werden, ist das sicher schneller.

Allerdings benutze ich selber in meinem aktuellen Projekten, ausschließlich FunktionsBäume:

Code: Alles auswählen

Prototype.i Term_FunctionI(*Term)

Structure Term
	FunctionI.Term_FunctionI
	StructureUnion
		Integer.i
		*Term.Term[2]
	EndStructureUnion
EndStructure

Procedure.i Term_GetI(*Term.Term)
	ProcedureReturn *Term\Integer
EndProcedure

Procedure.i Term_Function_PlusI(*Term.Term)
	ProcedureReturn *Term\Term[0]\FunctionI(*Term\Term[0]) + *Term\Term[1]\FunctionI(*Term\Term[1])
EndProcedure

Term1.Term
Term1\FunctionI = @Term_GetI()
Term1\Integer = 123

Term2.Term
Term2\FunctionI = @Term_GetI()
Term2\Integer = 456

Term3.Term
Term3\FunctionI = @Term_Function_PlusI()
Term3\Term[0] = @Term1
Term3\Term[1] = @Term2

Debug Term3\FunctionI(@Term3)
Bei mir verbraucht dieser Baum ca 33 Tackzyklen
Klar an 2 Tackzyklen die eine normale Addition verbraucht mit zwei Variablen kommt es natürlcih nicht ran.
Aber zum Vergleich: Str(1) benötig alleine nur weil es um Strings geht 50 Tackzyklen.

Von daher sind die "Bremser" woanders zu suchen.

@miscalculated
Wäre nett wenn du mal ein funktionierendes Beispiel posten würdest.

@ALL:

Hier im übrigen so messe ich das: Anzahl der Tacktzyklen von Befehlen ermitteln
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Kevin
Beiträge: 236
Registriert: 11.06.2007 12:55

Re: Interpreter beschleunigen

Beitrag von Kevin »

STARGÅTE hat geschrieben:Bei der Lösung von miscalculated hast du halt keine möglichkeit Parameter oder Rückgabewerte zu erhalten, weil du die ganze Zeit nur "springst".

Wenn du also selber eine Art Stack/Register schreibst, in dem die Sachen verarbeitet werden, ist das sicher schneller.

Allerdings benutze ich selber in meinem aktuellen Projekten, ausschließlich FunktionsBäume:

Code: Alles auswählen

Prototype.i Term_FunctionI(*Term)

Structure Term
	FunctionI.Term_FunctionI
	StructureUnion
		Integer.i
		*Term.Term[2]
	EndStructureUnion
EndStructure

Procedure.i Term_GetI(*Term.Term)
	ProcedureReturn *Term\Integer
EndProcedure

Procedure.i Term_Function_PlusI(*Term.Term)
	ProcedureReturn *Term\Term[0]\FunctionI(*Term\Term[0]) + *Term\Term[1]\FunctionI(*Term\Term[1])
EndProcedure

Term1.Term
Term1\FunctionI = @Term_GetI()
Term1\Integer = 123

Term2.Term
Term2\FunctionI = @Term_GetI()
Term2\Integer = 456

Term3.Term
Term3\FunctionI = @Term_Function_PlusI()
Term3\Term[0] = @Term1
Term3\Term[1] = @Term2

Debug Term3\FunctionI(@Term3)
Bei mir verbraucht dieser Baum ca 33 Tackzyklen
Klar an 2 Tackzyklen die eine normale Addition verbraucht mit zwei Variablen kommt es natürlcih nicht ran.
Aber zum Vergleich: Str(1) benötig alleine nur weil es um Strings geht 50 Tackzyklen.

Von daher sind die "Bremser" woanders zu suchen.
ich glaube du hast meinen erste post nicht gelesen oder falsch verstanden
mein Interpreter liest eine kette von Anweisungen die im Speicher stehen ungefähr so wie in meinem ersten Beispiel und das kann ich jetzt durch miscalculated' Beispiel optimieren.
mit miscalculated' Beispiel brauch ich keinen Rückgabewerte da ich es sofort berechnen kann
STARGÅTE hat geschrieben: @miscalculated
Wäre nett wenn du mal ein funktionierendes Beispiel posten würdest.
Hier ein funktionierender code:

Code: Alles auswählen

Define TARGET.l

Macro NextInstruction(too)
TARGET = JumpTable(too)
!JMP [v_TARGET]
EndMacro

; Sprungtabelle mit allen Befehlen
Dim JumpTable(255)
JumpTable(1) = ?l1
JumpTable(2) = ?l2
JumpTable(3) = ?l3


NextInstruction(1);sprung zu l1

Debug "wird übersprungen"

l1:
Debug "start"

NextInstruction(3);sprung zu l3

l2:
Debug "wird übersprungen"

l3:
Debug "ende"
STARGÅTE hat geschrieben:@ALL:

Hier im übrigen so messe ich das: Anzahl der Tacktzyklen von Befehlen ermitteln
hab ich schon gesehen und werde es auch benutzen um meinen Interpreter zu optimieren. :allright:
Antworten