Autor Beitrag
Arno-Wien
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 33

xp

BeitragVerfasst: 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 user profile iconChristian S.: Topic aus Delphi Language (Object-Pascal) / CLX verschoben am Mo 28.11.2005 um 22:28
BenBE
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: Di 29.11.05 23:23 
Gut, ich hab mir den Source heut einmal überschlagmäßig angeguckt und dabei sind mir folgende Stellen aufgefallen:

ausblenden volle Höhe Delphi-Quelltext
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.0OR (color = 62);
      if (color <= 18or (color = 62then
      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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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:
ausblenden Delphi-Quelltext
1:
2:
3:
a1 := b1+c1;
a2 := b2+c2;
d := a2+b2;


Das:
ausblenden 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:
ausblenden Delphi-Quelltext
1:
2:
y2wert:=xwert*ywert;  
ywert:=y2wert+y2wert-y_konst;


Das:
ausblenden Delphi-Quelltext
1:
ywert:=(xwert*ywert)*2-y_konst;					
Allesquarks
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: 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



BeitragVerfasst: 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:
ausblenden 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 // x := x * y;
    fld x // x in den Stack laden -> St(0)
    fmul r // St(0) * r -> St(0)
    fstp x // St(0) -> x Und Stapel leer Poppen
  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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 8721
Erhaltene Danke: 191

Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
BeitragVerfasst: 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
                                            //  ST0     ST1     ST2     ST3     ST4     ST5
    FLD     TBYTE PTR [B.TcomplexAbs.B]     //  B.B
    FLD     TBYTE PTR [B.TcomplexAbs.A]     //  B.A     B.B
    FLD     TBYTE PTR [A.TcomplexAbs.B]     //  A.B     B.A     B.B
    FLD     TBYTE PTR [A.TcomplexAbs.A]     //  A.A     A.B     B.A     B.B
    //Result.A := (A.A * B.A - A.B * B.B);
    FLD     ST(0)                           //  A.A     A.A     A.B     B.A     B.B
    FMUL    ST(0), ST(3)                    //  A.A*B.A A.A     A.B     B.A     B.B
    FLD     ST(2)                           //  A.B     A.A*B.A A.A     A.B     B.A     B.B
    FMUL    ST(0), ST(5)                    //  A.B*B.B A.A*B.A A.A     A.B     B.A     B.B
    FSUBP                                   //  ST1-ST0 A.A     A.B     B.A     B.B
    FSTP    TBYTE PTR [Result.TComplexAbs.A]//  A.A     A.B     B.A     B.B
    //Result.B := (A.B * B.A + A.A * B.B);
    FXCH    ST(2)                           //  B.A     A.B     A.A     B.B
    FMULP                                   //  B.A*A.B A.A     B.B
    FXCH    ST(2)                           //  B.B     A.A     B.A*A.B
    FMULP                                   //  A.A*B.B B.A*A.B
    FADDP                                   //  ST1+ST0
    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
                                            //  ST0     ST1     ST2     ST3     ST4     ST5     ST6
    FLD     TBYTE PTR [A.TcomplexAbs.A]     //  A.A
    FLD     TBYTE PTR [A.TcomplexAbs.B]     //  A.B     A.A
    FLD     TBYTE PTR [B.TcomplexAbs.A]     //  B.A     A.B     A.A
    FLD     TBYTE PTR [B.TcomplexAbs.B]     //  B.B     B.A     A.B     A.A
    //Tmp := B.A * B.A + B.B * B.B;
    FLD     ST(0)                           //  B.B     B.B     B.A     A.B     A.A
    FMUL    ST(0), ST(0)                    //  B.B^2   B.B     B.A     A.B     A.A
    FLD     ST(2)                           //  B.A     B.B^2   B.B     B.A     A.B     A.A
    FMUL    ST(0), ST(0)                    //  B.A^2   B.B^2   B.B     B.A     A.B     A.A
    FADDP                                   //  ST1+ST0 B.B     B.A     A.B     A.A
    //Result.A := (A.A * B.A + A.B * B.B) / Tmp;
    FLD     ST(2)                           //  B.A     Tmp     B.B     B.A     A.B     A.A
    FMUL    ST(0), ST(5)                    //  B.A*A.A Tmp     B.B     B.A     A.B     A.A
    FLD     ST(4)                           //  A.B     B.A*A.A Tmp     B.B     B.A     A.B     A.A
    FMUL    ST(0), ST(3)                    //  A.B*B.B B.A*A.A Tmp     B.B     B.A     A.B     A.A
    FADDP                                   //  ST1+ST0 Tmp     B.B     B.A     A.B     A.A
    FDIV    ST(0), ST(1)                    //  ST0/Tmp Tmp     B.B     B.A     A.B     A.A
    FSTP    TBYTE PTR [Result.TComplexAbs.A]//  Tmp     B.B     B.A     A.B     A.A
    //Result.B := (A.B * B.A - A.A * B.B) / Tmp;
    FXCH    ST(4)                           //  A.A     B.B     B.A     A.B     Tmp
    FMULP                                   //  A.A*B.B B.A     A.B     Tmp
    FXCH    ST(2)                           //  A.B     B.A     A.A*B.B Tmp
    FMULP                                   //  A.B*B.A A.A*B.B Tmp
    FSUBRP                                  //  ST0-ST1 Tmp
    FDIVP                                   //  ST0/Tmp
    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



BeitragVerfasst: Mo 12.12.05 21:13 
Ah, Ja. Ja, ich verstehe. Ich hätte das ganze wie folgt gemacht:

ausblenden 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
                                            //  ST0     ST1     ST2     ST3     ST4     ST5
    FLD     A                               //    B
    FMUL    B                               //  A*B
    FLD     C                               //    C     A*B
    FMUL    D                               //  C*D     A*B
    FADDP   ST(1), ST(0)                    //  F1
    FSTP    F1                              //
    //F1 := a*b + c*d;
    FLD     C                               //  C
    FMUL    B                               //  B*C
    FLD     A                               //    A     B*C
    FMUL    D                               //  A*D     B*C
    FADDP   ST(1), ST(0)                    //  F2
    FSTP    F2                              //
    //F2 := c*b + a*d
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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 510

Win XP Prof
Delphi 7 E
BeitragVerfasst: 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.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
asm
    mov eax, [myint64+4];
    mov edx, [myint64];
end;
SandStein
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Di 13.12.05 00:14 
... dennoch vielen Dank, auch an BenBE ...