Autor |
Beitrag |
Arno-Wien
      
Beiträge: 33
xp
|
Verfasst: Mo 28.11.05 23:25
Ich brauche für Delphi6 Assembler-Routinen für Multiplikation, Addition und Subtraktion
zur Beschleunigung meines Fraktale-Programms. Ich brauche keine Abfrage auf Fehler, da nur "gute" Zahlen zugewiesen werden.
Das Programm in der derzeitigen Fassung, auch schon schnell, sende ich gerne jedem
Interessiertem zu. Moderiert von Christian S.: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Mo 28.11.2005 um 22:28
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 28.11.05 23:35
Welche Multiplikation brauchst Du?
Ganzzahl, Gleitkomma (Single, Double, Extended)
Liegen Daten als Array vor
Ist an anderer Stelle wirklich nix mehr zu holen?
Naja, sende mal einen kurzen Ausschnitt ausm Source, für den Überblick ...
Ansonsten per PN an mich... Vielleicht hätt ich dann morgen sogar mal was in Info zu tun ...
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Di 29.11.05 23:23
Gut, ich hab mir den Source heut einmal überschlagmäßig angeguckt und dabei sind mir folgende Stellen aufgefallen:
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43:
| PROCEDURE apf_bild; VAR m,n,i:longint; p:pbytearray; xwert,ywert,x_konst,x1_konst,y_konst, y2wert,xqu,yqu,summe,deltax,deltay:extended; BEGIN deltax:=3.25/639; deltay:=2.50/479; y_konst:=-1.25-deltay; m:=0; REPEAT p:=a_bild.ScanLine[m]; y_konst:=y_konst+deltay; x_konst:=-0.9-deltax; FOR n:=0 TO 639 DO BEGIN x1_konst:=x_konst+deltax; x_konst:=x1_konst; xwert:=0.0; ywert:=0.0; xqu:=0.0; yqu:=0.0; color:=0; REPEAT y2wert:=xwert*ywert; ywert:=y2wert+y2wert-y_konst; xwert:=xqu-yqu-x_konst; xqu:=xwert*xwert; yqu:=ywert*ywert; summe:=xqu+yqu; color:=succ(color) UNTIL (summe > 100.0) OR (color = 62); if (color <= 18) or (color = 62) then begin i:=3*n; p[i]:=farbe_blau; p[i+1]:=farbe_gruen; p[i+2]:=farbe_rot end END; m:=succ(m) UNTIL m = 480 END; |
Eins vorweg: Ich lege Wert auf gesetzte Semikolons in Pascal-Sources.
Gut, zum Source selber:
Zeile 5: Für schnelle BErechnungen sollte man auf Extended verzichten und stattdessen Double oder besser Single verwenden. Extended ist 10 Byte groß und erzeugt damit IMMER unaligned-Zugriffe, wodurch der Speicherzugriff stark gebremst wird, auch wenn er von der COPU, wenn er einmal geladen ist, gut verwendet werden kann. Single und Double können im Gegensatz zu Extended In-Place nachgeladen werden, was nicht nur eine FPU-Anweisung spart, sondern auch (trotz Konvertierung des Zahlenformats) schneller geht.
Zeilen 7f: Diese Initialisierung sollte am Besten als Konstante erfolgen. Der Kompiler kann dann bei gegebenen Koeffizienten besser auf Vereinfachungen reagieren und unnötige Operationen einsparen. Das gilt auch für andere Konstanten im Source.
Weiterhin gilt, dass Anweisungen wenn möglich immer Numerische Konst, Deklarierte Konst, Variablen (in der Reihenfolge) in Berechnungen auftreten sollten, da der Compiler dann einfacher eine Optimierung durchführen kann.
Zeilen 31 u. 41: Statt der Funktion Succ solltest Du die Inplace-Anweisung Dec\Inc verwenden. Das wird direkt vom Compiler geinlined und ist außerdem übersichlicher \ gebräuchlicher.
Zeilen 35-38: Statt dem Index I solltest Du die statischen Indizes 0-2 verwenden und nach getaner Arbeit einfach Inc(P, 3) ausführen. Evtl. dazu die Deklaration auf [delphi]PRGBColor = ^TRGBColor; TRGBColor = packed array[0..2] of Byte; ändern. Inc erkennt automatisch die korrekte Größe (und brauch daher keinen zweiten Parameter in diesem Fall)...
HTH vorerst.
Eine ASM-Optimierung ist bei suboptimaler Pascal-Implementierung vertane Zeit.
MfG,
BenBE.
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: So 04.12.05 02:23
Eventuell könntest du mal einen Versuch starten, das ganze zu parallelisieren (z.B. mit SSE2).
Ich bin zwar nicht extrem auf dem neusten Stand was Intel Prozessoren anbelangt, aber vielleicht macht es ja einen Unterschied, wenn du Variablen, die geschrieben wurden, nicht grad wieder liest sondern andere Operationen vorziehst (wenn du sowieso noch unabhängige Operationen rumliegen hast):
Statt:
Delphi-Quelltext 1: 2: 3:
| a1 := b1+c1; a2 := b2+c2; d := a2+b2; |
Das:
Delphi-Quelltext 1: 2: 3:
| a2 := b2+c2; a1 := b1+c1; d := a2+b2; |
So kann der Prozessor automatisch mehr parallelisieren, weil er sich "mehr Zeit fürs Schreiben" nehmen kann. Statt zu warten, bis der Speicher geschrieben ist, kann er andere, unabhängige Operationen ausführen.
Ich weiss allerdings nicht, ob der Prozessor das nicht schon automatisch macht, oder ob bei Intel-Prozessoren überhaupt solche automatischen Parallelisierungen statt finden.
//Edit: Was ich auch sehe: Wahrscheinlich ist es besser, keine Zwischenresultate in Variablen zu speichern, wenn die später nicht mehr gebraucht werden.
Statt:
Delphi-Quelltext 1: 2:
| y2wert:=xwert*ywert; ywert:=y2wert+y2wert-y_konst; |
Das:
Delphi-Quelltext 1:
| ywert:=(xwert*ywert)*2-y_konst; |
|
|
Allesquarks
      
Beiträge: 510
Win XP Prof
Delphi 7 E
|
Verfasst: So 04.12.05 03:01
SSE2 wird vom Delphi Compiler nicht unterstützt. Man muss also alles in Assembler programmieren. Da er zuvor mit extended gerechnet hat, nehme ich mal an dass er auf gar keinen Fall auf singles (SSE) zurückfallen möchte. Wenn er dann AMD Nutzer ist und "noch" einen Athlon XP besitzt kommt SSE2 (doubles) nicht in Betracht.
Meines Wissens sollte der Compiler aber solche Umstrukturierungen zur Parallelisierung unterstützen.
Kenn mich damit nicht aus aber könnte man nicht einen anderen Compiler für diese Funktion benutzen, der SSE und Co. unterstützt und diese dann irgendwie inlinen?
|
|
SandStein
Hält's aus hier
Beiträge: 5
|
Verfasst: Mo 12.12.05 18:35
Servus!
Um Multiplikationen und andere nette berechnungen durchzufüren gibts ja auch die Floating-Point-Unit (FPU)
Dieses Teil rechnet mit einem Internen Stack und ist dadurch am Anfang etwas kompliziert.
Eine kleine Referenz gibts hier:
hera.dvz.fh-giessen....orlesung/fpu-ref.pdf
Ein kleines Beispiel:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
| procedure TForm1.Button1Click(Sender: TObject); var r,x : real; begin x := 3.3; r := 4.4;
asm fld x fmul r fstp x end; label1.Caption := floattostr(x); end; |
Wenn mehrere Variablen in den Stack kopiert werden, sollte beachtet werden, den Stack wieder richtig zu entpoppen, aber meisst reicht der st(0) aus.
Alles klar?
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mo 12.12.05 18:58
Und du bist da echt dieser Meinung??? Gut, mal ein paar Beispiele (von vielen):
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70:
| Function ComplexMul(Var A: TComplexAbs; Var B: TComplexAbs): TComplexAbs; Overload; {$IFDEF OMORPHIA_FEATURES_USEASM} Assembler; Asm FLD TBYTE PTR [B.TcomplexAbs.B] FLD TBYTE PTR [B.TcomplexAbs.A] FLD TBYTE PTR [A.TcomplexAbs.B] FLD TBYTE PTR [A.TcomplexAbs.A] FLD ST(0) FMUL ST(0), ST(3) FLD ST(2) FMUL ST(0), ST(5) FSUBP FSTP TBYTE PTR [Result.TComplexAbs.A] FXCH ST(2) FMULP FXCH ST(2) FMULP FADDP FSTP TBYTE PTR [Result.TComplexAbs.B]End; {$ELSE} Begin Result.A := A.A * B.A - A.B * B.B; Result.B := A.B * B.A + A.A * B.B; End; {$ENDIF}
Function ComplexDiv(Var A: TComplexAbs; Var B: TComplexAbs): TComplexAbs; Overload; {$IFDEF OMORPHIA_FEATURES_USEASM} Assembler; Asm FLD TBYTE PTR [A.TcomplexAbs.A] FLD TBYTE PTR [A.TcomplexAbs.B] FLD TBYTE PTR [B.TcomplexAbs.A] FLD TBYTE PTR [B.TcomplexAbs.B] FLD ST(0) FMUL ST(0), ST(0) FLD ST(2) FMUL ST(0), ST(0) FADDP FLD ST(2) FMUL ST(0), ST(5) FLD ST(4) FMUL ST(0), ST(3) FADDP FDIV ST(0), ST(1) FSTP TBYTE PTR [Result.TComplexAbs.A] FXCH ST(4) FMULP FXCH ST(2) FMULP FSUBRP FDIVP FSTP TBYTE PTR [Result.TComplexAbs.B]End; {$ELSE} Var Tmp: Extended; Begin Tmp := B.A * B.A + B.B * B.B; Result.A := (A.A * B.A + A.B * B.B) / Tmp; Result.B := (A.B * B.A - A.A * B.B) / Tmp; End; {$ENDIF} |
Da komm ich mit ST0 lange nicht aus ... Ich hab sogar schon Probleme gehabt, dass es fast zu wenig Register gibt 
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
SandStein
Hält's aus hier
Beiträge: 5
|
Verfasst: Mo 12.12.05 21:13
Ah, Ja. Ja, ich verstehe. Ich hätte das ganze wie folgt gemacht:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| var A,B,C,D,F1,F2: real; begin
asm FLD A FMUL B FLD C FMUL D FADDP ST(1), ST(0) FSTP F1 FLD C FMUL B FLD A FMUL D FADDP ST(1), ST(0) FSTP F2 End; |
Da benötige ich zwar nur 2 Register, aber ich denke Deine Lösung gefällt mir besser, da sie vermutlich schneller ist - ätzende HP-Zugriffe...
ByTheWay: Weißt Du wie ich ein Int64 mit asm in register laden kann?
|
|
Allesquarks
      
Beiträge: 510
Win XP Prof
Delphi 7 E
|
Verfasst: Mo 12.12.05 22:54
Ja!!
Sorry aber eigentlich nicht Thema!
unter 32 Bit Prozis brauchst du zwei Register eax und edx. edx = most significant Bits, eax = least significant Bits. Delphi-Quelltext 1: 2: 3: 4:
| asm mov eax, [myint64+4]; mov edx, [myint64]; end; |
|
|
SandStein
Hält's aus hier
Beiträge: 5
|
Verfasst: Di 13.12.05 00:14
... dennoch vielen Dank, auch an BenBE ...
|
|
|