Autor Beitrag
Spaceguide
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 552


(D3/D7/D8) Prof.
BeitragVerfasst: Do 11.05.06 10:44 
Hi,
ich versuche gerade eine Routine zu optimieren, welche einen FloatPoint (x,y) in einen einen ganzzahligen Teil und die Nachkommastellen zerlegt.

Mein Problem ist die schlechte Performanz der SSE-Implementierung FracTruncXY_SSE(). Sie ist kaum schneller (manchmal auf langsamer) als der FPU-Murks FracTruncXY_FPU. Kann man da noch was herausholen?

Timings zum Vergleich auf Pentium-M:

FracTruncXY() : 4386ms
FracTruncXY_FPU() : 2043ms
FracTruncXY_SSE() : 2023ms


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:
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:
71:
type
 float = single;
 TPoint = packed record x,y : integer; end;
 TFloatPoint = packed record x,y : float; end;


procedure FracTruncXY(const aPoint : TFloatPoint;
                      var aIntOut : TPoint;
                      var aFraction : TFloatPoint);
begin
 aIntOut.x := Round(aPoint.x-0.5);
 aFraction.x := aPoint.x-aIntOut.x;
 aIntOut.y := Round(aPoint.y-0.5);
 aFraction.y := aPoint.y-aIntOut.y;
end;

procedure FracTruncXY_FPU(const aPoint : TFloatPoint;
                           var aIntOut : TPoint;
                           var aFraction : TFloatPoint);

 const round_toward_m_i: float = -0.5;
 var i: integer;
begin

 asm
      fld      dword ptr [eax]
      fadd     round_toward_m_i
      fistp    i
 end;


 aIntOut.x := i;
 aFraction.x := aPoint.x - i;

 asm
      fld      dword ptr [eax+4]
      fadd     round_toward_m_i
      fistp    i
 end;

 aIntOut.y := i;
 aFraction.y := aPoint.y - i;
end;





procedure FracTruncXY_SSE(const aPoint : TFloatPoint;
                          var aIntOut : TPoint;
                          var aFraction : TFloatPoint);
 const RoundTowards : array [0..3of float = (-0.5,-0.5,0,0);
ASM
 MOVLPS xmm0,[aPoint] //xmm0 = (x)(y)(0)(0)

 MOVUPS xmm1,[RoundTowards] //xmm1 = (-0.5)(-0.5)(0)(0)

 MOVAPS xmm2,xmm0 //xmm2 = (x)(y)(0)(0)
 ADDPS xmm0,xmm1 //xmm0 = (x-0.5)(y-0.5)(0)(0)

 CVTPS2PI mm0, xmm0 //mm0 = (int x)(int y)
 MOVQ [aIntOut],mm0 //aIntOut = (int x)(int y)

 CVTPI2PS xmm0,mm0 //xmm0 = (trunc x)(trunc y)(0)(0)

 SUBPS xmm2,xmm0 //xmm2 = (x-trunc x)(y-trunc y)(0)(0)

 MOVLPS [aFraction],xmm2 //aFraction = (x-trunc x)(y-trunc y)

 EMMS //retore fpu state
end;
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6395
Erhaltene Danke: 149

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Do 11.05.06 10:57 
Warum benutzt du Round? Delphi kennt auch die Funktion Trunc. Round dürfte theoretisch langsamer sein. Und die Funktion Frac gibts doch auch.
Spaceguide Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 552


(D3/D7/D8) Prof.
BeitragVerfasst: Do 11.05.06 11:04 
Ich habe gewusst, dass das jemand fragt, der's nicht ausprobiert hat.
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6395
Erhaltene Danke: 149

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Do 11.05.06 11:15 
Dann hätte auch ein einfache Hinweis in deinem ersten Beitrag genügt, dass du das schon getestet hast. Oder ist es unsere Aufgabe, deine Versuche zu wiederholen?
Spaceguide Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 552


(D3/D7/D8) Prof.
BeitragVerfasst: Do 11.05.06 11:19 
Hae? Die erste Routine steht doch garnicht zur Diskussion, die ist nur dabei, damit man sofort sieht, was die unten stehenden Assembler-Routinen eigentlich machen sollen. Das Problem ist, wie im Beitrag steht, die SSE-Routine, die mir ziemlich langsam vorkommt.
jasocul
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 6395
Erhaltene Danke: 149

Windows 7 + Windows 10
Sydney Prof + CE
BeitragVerfasst: Do 11.05.06 11:23 
user profile iconSpaceguide hat folgendes geschrieben:
Hae? Die erste Routine steht doch garnicht zur Diskussion, die ist nur dabei, damit man sofort sieht, was die unten stehenden Assembler-Routinen eigentlich machen sollen.

Dann war ich wohl zu blöd, das so zu interpertieren.
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Do 11.05.06 11:37 
Hallo,

auf einem Athlon64 (1800 Mhz)sieht es so mit Delphi7 aus
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:
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:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils,windows;
type
 float = single;
 TPoint = packed record x,y : integer; end;
 TFloatPoint = packed record x,y : float; end;


var
  fPunkt,Fout :TFloatPoint;
  iPunkt: TPoint;
  i,max : integer;
  T1,T0,Tf,Td : int64;

procedure FracTruncXY(const aPoint : TFloatPoint;
                      var aIntOut : TPoint;
                      var aFraction : TFloatPoint);
begin
 aIntOut.x := Round(aPoint.x-0.5);
 aFraction.x := aPoint.x-aIntOut.x;
 aIntOut.y := Round(aPoint.y-0.5);
 aFraction.y := aPoint.y-aIntOut.y;
end;

procedure FracTruncXY_FPU(const aPoint : TFloatPoint;
                           var aIntOut : TPoint;
                           var aFraction : TFloatPoint);

 const round_toward_m_i: float = -0.5;
 var i: integer;
begin

 asm
      fld      dword ptr [eax]
      fadd     round_toward_m_i
      fistp    i
 end;


 aIntOut.x := i;
 aFraction.x := aPoint.x - i;

 asm
      fld      dword ptr [eax+4]
      fadd     round_toward_m_i
      fistp    i
 end;

 aIntOut.y := i;
 aFraction.y := aPoint.y - i;
end;


procedure FracTruncXY_SSE(const aPoint : TFloatPoint;
                          var aIntOut : TPoint;
                          var aFraction : TFloatPoint);
 const RoundTowards : array [0..3of float = (-0.5,-0.5,0,0);
ASM
 MOVLPS xmm0,[aPoint] //xmm0 = (x)(y)(0)(0)
 MOVUPS xmm1,[RoundTowards] //xmm1 = (-0.5)(-0.5)(0)(0)
 MOVAPS xmm2,xmm0 //xmm2 = (x)(y)(0)(0)
 ADDPS xmm0,xmm1 //xmm0 = (x-0.5)(y-0.5)(0)(0)
 CVTPS2PI mm0, xmm0 //mm0 = (int x)(int y)
 MOVQ [aIntOut],mm0 //aIntOut = (int x)(int y)
 CVTPI2PS xmm0,mm0 //xmm0 = (trunc x)(trunc y)(0)(0)
 SUBPS xmm2,xmm0 //xmm2 = (x-trunc x)(y-trunc y)(0)(0)
 MOVLPS [aFraction],xmm2 //aFraction = (x-trunc x)(y-trunc y)
 EMMS //retore fpu state
end;

begin
  queryperformancefrequency(Tf);
  fPunkt.x := pi;
  fPunkt.y := pi;
  max := 1000*1000*100;
  writeln(Format('Durchlaeufe  %e',[max*1.0]));
  writeln;
  queryperformanceCounter(T0);
  for i := max downto 1 do;
  queryperformanceCounter(T1);
  writeln(Format('Nichts  %10.5f ms',[(T1-T0)/Tf*1000]));
  Td := t1-t0;

  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('Normal  %10.5f ms',[(T1-T0-Td)/Tf*1000]));

  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY_FPU(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('FPU     %10.5f ms',[(T1-T0-Td)/Tf*1000]));

  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY_SSE(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('SSE     %10.5f ms',[(T1-T0-Td)/Tf*1000]));

  readln;
end.


mit dem Ergebnis

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
Durchlaeufe  1,00000000000000E+008

Nichts   112,82160 ms
Normal  3048,05080 ms
FPU     1428,75058 ms
SSE      729,30247 ms


Also dort ist es schneller mit SSE

Gruss Horst

EDIT

Auf meine Notebook (Pentium M 1,7 Ghz)
sind es
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
Durchlaeufe  1,00000000000000E+008

Nichts   197,01470 ms
Normal  2614,99157 ms
FPU     1286,62917 ms
SSE      891,20740 ms


Also auch dort ist es schneller mit SSE

Da ich immer den selben Speicherbereich anspreche, koennte es daran liegen, dass die Daten nur immer im 1.st level cache sind.
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: Do 11.05.06 14:29 
Wenn ich mich nicht vertan hab (ACHTUNG: UNGETESTET), sollte es so noch etwas optimaler sein.
ausblenden 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:
type
 float = single;
 TPoint = packed record x,y : integer; end;
 TFloatPoint = packed record x,y : float; end;

procedure FracTruncXY_FPU(
    var aPoint : TFloatPoint;       //EAX
    var aIntOut : TPoint;           //EDX
    var aFraction : TFloatPoint);   //ECX
const
    round_toward_m_i: float = -0.5;
var
    i: integer;
asm
    fld     DWORD PTR [eax].TFloatPoint.x
    fld     st(0)
    fadd    round_toward_m_i
    fld     st(0)
    fistp   DWORD PTR [edi].TPoint.X
    fsubrp
    fstp    DWORD PTR [ECX].TFloatPoint.x

    fld     DWORD PTR [eax].TFloatPoint.y
    fld     st(0)
    fadd    round_toward_m_i
    fld     st(0)
    fistp   DWORD PTR [edi].TPoint.y
    fsubrp
    fstp    DWORD PTR [ECX].TFloatPoint.y
end;


Wenn's Vorzeichenfehler gibt, bei fsubrp das R entfernen ;-)

_________________
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.
Spaceguide Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 552


(D3/D7/D8) Prof.
BeitragVerfasst: Do 11.05.06 15:05 
Wenn ich jetzt mal EDI durch EDX ersetze funktioniert es noch nicht. Die Fraction ist konstant 0.5,0.5
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: Do 11.05.06 15:23 
Hi, hatte noch nen kleinen Fehler bei der Rundung drin. Jetzt funzt's ... Hab keine Zeitmessung gemacht, sollte aber zumindest den Algo von Delphi um Längen schlagen ...
ausblenden 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:
type
 float = single;
 TPoint = packed record x,y : integer; end;
 TFloatPoint = packed record x,y : float; end;

procedure FracTruncXY_FPU(
    var aPoint : TFloatPoint;       //EAX
    var aIntOut : TPoint;           //EDX
    var aFraction : TFloatPoint);   //ECX
const
    round_toward_m_i: float = -0.5;
var
    i: integer;
asm
    fld     DWORD PTR [eax].TFloatPoint.x
    fld     st(0)
    fadd    round_toward_m_i
    fistp   DWORD PTR [edx].TPoint.X
    fild    DWORD PTR [edx].TPoint.X
    fsubrp
    fstp    DWORD PTR [ECX].TFloatPoint.x

    fld     DWORD PTR [eax].TFloatPoint.y
    fld     st(0)
    fadd    round_toward_m_i
    fistp   DWORD PTR [edx].TPoint.y
    fild    DWORD PTR [edx].TPoint.y
    fsubp
    fstp    DWORD PTR [ECX].TFloatPoint.y
end;


//Edit: F*CK ... Delphi hat das Kopieren nicht mitbekommen. Hab vor der Sub-Anweisung die beiden Befehle getauscht ...

_________________
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.


Zuletzt bearbeitet von BenBE am Do 11.05.06 15:59, insgesamt 1-mal bearbeitet
Spaceguide Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 552


(D3/D7/D8) Prof.
BeitragVerfasst: Do 11.05.06 15:48 
Hmm, mal ne dumme Frage: Was hast du grad geändert? Die Quellcodes sind eigentlich identisch.
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Do 11.05.06 19:23 
Hallo,

bei Deiner Variante werden drei Register auf den Stack geschoben, bei den anderen nicht.

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:
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:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
program Project2;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  windows;

type
 float = single;
 TPoint = packed record x,y : integer; end;
 TFloatPoint = packed record x,y : float; end;


var
  T1,T0,Tf,Td : int64;
  fPunkt,Fout :TFloatPoint;
  iPunkt: TPoint;
  i,max : integer;

procedure FracTruncXY(const aPoint : TFloatPoint;
                      var aIntOut : TPoint;
                      var aFraction : TFloatPoint);
begin
 aIntOut.x := Round(aPoint.x-0.5);
 aFraction.x := aPoint.x-aIntOut.x;
 aIntOut.y := Round(aPoint.y-0.5);
 aFraction.y := aPoint.y-aIntOut.y;
end;

procedure FracTruncXY_FPU(const aPoint : TFloatPoint;
                           var aIntOut : TPoint;
                           var aFraction : TFloatPoint);

 const round_toward_m_i: float = -0.5;
 var i: integer;
begin

 asm
      fld      dword ptr [eax]
      fadd     round_toward_m_i
      fistp    i
 end;


 aIntOut.x := i;
 aFraction.x := aPoint.x - i;

 asm
      fld      dword ptr [eax+4]
      fadd     round_toward_m_i
      fistp    i
 end;


 aIntOut.y := i;
 aFraction.y := aPoint.y - i;
end;

procedure FracTruncXY_FPU2(
    var aPoint : TFloatPoint;       //EAX
    var aIntOut : TPoint;           //EDX
    var aFraction : TFloatPoint);   //ECX
const
    round_toward_m_i: float = -0.5;
asm
    fld     DWORD PTR [eax].TFloatPoint.x
    fld     st(0)
    fadd    round_toward_m_i
    fistp   DWORD PTR [edx].TPoint.X
    fild    DWORD PTR [edx].TPoint.X
    fsubp
    fstp    DWORD PTR [ECX].TFloatPoint.x

    fld     DWORD PTR [eax].TFloatPoint.y
    fld     st(0)
    fadd    round_toward_m_i
    fistp   DWORD PTR [edx].TPoint.y
    fild    DWORD PTR [edx].TPoint.y
    fsubp
    fstp    DWORD PTR [ECX].TFloatPoint.y
end;


procedure FracTruncXY_FPU3(
    var aPoint : TFloatPoint;       //EAX
    var aIntOut : TPoint;           //EDX
    var aFraction : TFloatPoint);   //ECX
const
    round_toward_m_i: float = -0.5;
asm
    fld     round_toward_m_i
    fld     DWORD PTR [EAX].TFloatPoint.x
    fld     st(0)
    fadd    st(0),st(2)

    fld     DWORD PTR [EAX].TFloatPoint.y
    fld     st(0)
    fadd    st(0),st(4)
    //y-0.5,y,x-0.5,x,-0.5
    fFree   st(4)
    fistp   DWORD PTR [EDX].TPoint.y
    fild    DWORD PTR [EDX].TPoint.y
    fsubp
    fstp    DWORD PTR [ECX].TFloatPoint.y

    fistp   DWORD PTR [EDX].TPoint.x
    fild    DWORD PTR [EDX].TPoint.x
    fsubp
    fstp    DWORD PTR [ECX].TFloatPoint.x

end;

procedure FracTruncXY_FPU4(
    var aPoint : TFloatPoint;       //EAX
    var aIntOut : TPoint;           //EDX
    var aFraction : TFloatPoint);   //ECX
asm
    fld     DWORD PTR [eax].TFloatPoint.X
    FRNDINT
    fld     DWORD PTR [eax].TFloatPoint.Y
    FRNDINT
    fld     DWORD PTR [eax].TFloatPoint.X
    fld     DWORD PTR [eax].TFloatPoint.Y
    //Y,X,[Y],[X]
    FXCH    ST(3)
    //[X],X,[Y],Y
    fist    DWORD PTR [edx].TPoint.X
    fsub
    fstp    DWORD PTR [ECX].TFloatPoint.X

    fist    DWORD PTR [edx].TPoint.Y
    fsub
    fstp    DWORD PTR [ECX].TFloatPoint.Y
end;

procedure FracTruncXY_SSE(const aPoint : TFloatPoint;
                          var aIntOut : TPoint;
                          var aFraction : TFloatPoint);
 const RoundTowards : array [0..3of float = (-0.5,-0.5,0,0);
ASM
 MOVLPS xmm0,[aPoint] //xmm0 = (x)(y)(0)(0)
 MOVUPS xmm1,[RoundTowards] //xmm1 = (-0.5)(-0.5)(0)(0)
 MOVAPS xmm2,xmm0 //xmm2 = (x)(y)(0)(0)
 ADDPS xmm0,xmm1 //xmm0 = (x-0.5)(y-0.5)(0)(0)
 CVTPS2PI mm0, xmm0 //mm0 = (int x)(int y)
 MOVQ [aIntOut],mm0 //aIntOut = (int x)(int y)
 CVTPI2PS xmm0,mm0 //xmm0 = (trunc x)(trunc y)(0)(0)
 SUBPS xmm2,xmm0 //xmm2 = (x-trunc x)(y-trunc y)(0)(0)
 MOVLPS [aFraction],xmm2 //aFraction = (x-trunc x)(y-trunc y)
 FEMMS //retore fpu state
end;


begin
  queryperformancefrequency(Tf);
  fPunkt.x := pi;
  fPunkt.y := sqrt(2);
  max := 1000*1000*100;
  writeln(Format('Durchlaeufe  %e',[max*1.0]));
  writeln('');
  queryperformanceCounter(T0);
  for i := max downto 1 do;
  queryperformanceCounter(T1);
  writeln(Format('Nichts  %10.5f ms',[(T1-T0)/Tf*1000]));
  Td := t1-t0;
{
  fPunkt.x := fPunkt.x+1;
  fPunkt.y := fPunkt.y+1;
  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('Normal  %10.5f ms',[(T1-T0-Td)/Tf*1000]));
  writeln(Format('%10d %10d %10.8f  %10.8f',[iPunkt.x,ipunkt.y,fout.x,fout.y]));
}

  fPunkt.x := fPunkt.x+1;
  fPunkt.y := fPunkt.y+1;
  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY_FPU(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('FPU 1   %10.5f ms',[(T1-T0-Td)/Tf*1000]));
  writeln(Format('%10d %10d %10.8f  %10.8f',[iPunkt.x,ipunkt.y,fout.x,fout.y]));

  fPunkt.x := fPunkt.x+1;
  fPunkt.y := fPunkt.y+1;
  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY_FPU2(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('FPU 2   %10.5f ms',[(T1-T0-Td)/Tf*1000]));
  writeln(Format('%10d %10d %10.8f  %10.8f',[iPunkt.x,ipunkt.y,fout.x,fout.y]));

  fPunkt.x := fPunkt.x+1;
  fPunkt.y := fPunkt.y+1;
  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY_FPU3(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('FPU 3   %10.5f ms',[(T1-T0-Td)/Tf*1000]));
  writeln(Format('%10d %10d %10.8f  %10.8f',[iPunkt.x,ipunkt.y,fout.x,fout.y]));

  fPunkt.x := fPunkt.x+1;
  fPunkt.y := fPunkt.y+1;
  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY_FPU4(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('FPU 4   %10.5f ms',[(T1-T0-Td)/Tf*1000]));
  writeln(Format('%10d %10d %10.8f  %10.8f',[iPunkt.x,ipunkt.y,fout.x,fout.y]));

  fPunkt.x := fPunkt.x+1;
  fPunkt.y := fPunkt.y+1;
  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY_SSE(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  writeln(Format('SSE     %10.5f ms',[(T1-T0-Td)/Tf*1000]));
  writeln(Format('%10d %10d %10.8f  %10.8f',[iPunkt.x,ipunkt.y,fout.x,fout.y]));
  readln;
end.


Als Ergebnis Ahtlon64 1.8 Ghz auf dem Pentium M funktioniert FPU4 nicht :-?

ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
Durchlaeufe  1,00000000000000E+008

Nichts   113,89017 ms
FPU 1   1349,49833 ms
         4          2 0,14159298  0,41421366
FPU 2    679,08296 ms
         5          3 0,14159298  0,41421366
FPU 3    646,48719 ms
         6          4 0,14159298  0,41421366
FPU 4    649,59820 ms
         7          5 0,14159298  0,41421366
SSE      674,67821 ms
         8          6 0,14159298  0,41421366


Falls man diese Proceduren mit einer Form verwendet, dann braucht SSE plotzlich ueber 18 Sekunden bei nur 10e8 Durchlaeufen??
Wieso ist dort der Rest soviel schneller? Das liegt wohl daran, dass die leere Schleife so merkwuerdig langsam ist.
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
Memo1
Durchlaeufe  1,00000000000000E+008

Nichts   336,66430 ms
FPU 1   1202,33354 ms
         4          2 0,14159298  0,41421366
FPU 2    572,52388 ms
         5          3 0,14159298  0,41421366
FPU 3    549,07816 ms
         6          4 0,14159298  0,41421366
FPU 4    471,83259 ms
         7          5 0,14159298  0,41421366
SSE     18756,69952 ms
         8          6 0,14159298  0,41421366



Gruss Horst

P.S.
Das sind also ca. 12 CPU-Takte pro Rechnung
Spaceguide Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 552


(D3/D7/D8) Prof.
BeitragVerfasst: Do 11.05.06 20:35 
Also deine Routine scheint jetzt wirklich recht flott zu sein, BenBE. Jetzt bleibt nur die Frage offen, warum die SSE Routine so jämmerlich abschneidet.
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: Do 11.05.06 21:08 
@Horst: Teste bitte mal FPU3 mit FADDP in Zeile 100. Evtl. kann es sein, dass Du die Register-Reihenfolge manuell festlegen musst (zwecks Entfernen von ST(4)). Damit sollten dort nochmal ein paar Takte möglich sein.

@Form + SSE: Sollte man nicht FEMMS vor+nach Nutzung von SSE aufrufen???

_________________
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.
Spaceguide Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 552


(D3/D7/D8) Prof.
BeitragVerfasst: Do 11.05.06 21:19 
Das mit FEMMS macht keinen Unterschied. Die SSE-Routine ist fast viermal langsamer als mit der FPU (AthlonXP), dabei werden hier zwei Floats gleichzeitig bearbeitet, was die FPU sequentiell machen muss. Ich verstehe die Welt nicht mehr :nut:
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Do 11.05.06 22:18 
Hallo,

Ahtlon benutzt intern nur einen 64-Bit Bus fuer SSE-Daten und nicht mit 128 wie Pentium, die erheblich(Faktor 2 und mehr) schneller mit SSE arbeiten.
So, nach dem Neustart funktioniert auch SSE unter VCL bei mir wieder( genau 2-mal mit 403 ms und wieder 14032 ms, dann wieder 405 ms,396 ms grummmelgrummelmaeusemelk ??? ).
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
Memo1
Durchlaeufe  1,00000000000000E+008

Nichts   345,38552 ms
FPU 1   1187,13943 ms
         4          2 0,14159298  0,41421366
FPU 2    543,49617 ms
         5          3 0,14159298  0,41421366
FPU 3    521,06203 ms
         6          4 0,14159298  0,41421366
FPU 4    441,06723 ms
         7          5 0,14159298  0,41421366
SSE      396,97448 ms
         8          6 0,14159298  0,41421366

Wenn man die Leere Schleife mit 112 ms ansetzt wie beim console Programm sind die Zeiten um 233 ms zu erhoehen, also statt 396 ms eben 629 ms, also fast wieder die gleichen Zeiten.

Zum Pentium M 1.7 Ghz (Femms kennt nur AMD , also EMMS...)
Aber die Laufzeiten...
ausblenden 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:
Nichts   493,19453 ms
FPU 1   1334,78808 ms
         4          2 0,14159298  0,41421366
FPU 2    915,60855 ms
         5          3 0,14159298  0,41421366
FPU 3   1027,68760 ms
         6          4 0,14159298  0,41421366
FPU 4   1808,26837 ms
         7          5 0,14159298  0,41421366
SSE      850,41339 ms
         8          6 0,14159298  0,41421366
Durchlaeufe  1,00000000000000E+008

Nichts   415,32150 ms
FPU 1   1068,82830 ms
         4          2 0,14159298  0,41421366
FPU 2    764,51476 ms
         5          3 0,14159298  0,41421366
FPU 3    774,06263 ms
         6          4 0,14159298  0,41421366
FPU 4   1498,81256 ms
         7          5 0,14159298  0,41421366
SSE     16538,15499 ms<<<<<<<<<<<<<<<<<<<<<<<<
         8          6 0,14159298  0,41421366


Ich glaube es liegt an dem Grafikartentreiber fuer die ATI 9600 (bei beiden Rechnern).
Vielleicht auch nur dann, wenn es eine solange Zeit am Stueck ist.

Man sieht deutlich, dass AMD auch verschiedene Arten schnell erledigt, aber BenBe's Version ist eindeutig die effektivste.

Gruss Horst
P.S.
3dnow funktioniert doch auch gut.
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Do 11.05.06 23:21 
Hallo,

FPU4 rundet und macht kein trunc-> ist also nur fuer positive Zahlen.
Fuer positive Zahlen und AMD 3dnow:
ausblenden 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:
procedure FracTruncXY_3dnow(const aPoint : TFloatPoint;
                          var aIntOut : TPoint;
                          var aFraction : TFloatPoint);
ASM
  MOVQ MM0,[aPoint];
  PF2ID MM1, MM0
  MOVQ [aIntOut],MM1;
  PI2FD MM1, MM1
  PFSUB MM0,MM1
  MOVQ  [aFraction],MM0
  FEMMS
end;
Athlon64
Durchlaeufe  1,00000000000000E+008

Nichts   169,73275 ms
FPU 1   1263,30106 ms
         4          2 0,14159298  0,41421366
FPU 2    631,82416 ms
         5          3 0,14159298  0,41421366
FPU 3    588,71700 ms
         6          4 0,14159298  0,41421366
FPU 4    595,05635 ms
         7          5 0,14159298  0,41421366
SSE      674,68268 ms
         8          6 0,14159298  0,41421366
3dnow    505,10107 ms
         9          7 0,14159298  0,41421366

Echtes trunc laut AMD ergibt dies, was aber viel langasamer ist.
ausblenden 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:
procedure FracTruncXY_3dnow(const aPoint : TFloatPoint;
                          var aIntOut : TPoint;
                          var aFraction : TFloatPoint);
//AMD Athlon™ Processor x86 Code Optimization Guide
const
  MABS : array [0..1of cardinal = ($7FFFFFFF,$7FFFFFFF);//NaN
  TTTF : array [0..1of cardinal = ($4B800000,$4B800000);
ASM
MOVQ MM0,[aPoint];
MOVQ MM3, [MABS]     //mask for absolute value
PF2ID MM1, MM0       //trunc(x)
MOVQ MM4, [TTTF]     //2^24
PAND MM3, MM0        //abs(x)
PI2FD MM2, MM1       //float(trunc(x))
PCMPGTD MM3, MM4     //abs(x) > 2^24 : 0xffffffff : 0
MOVQ MM4, MM0        //x
PFCMPGT MM2, MM4     //float(trunc(x)) > x ? 0xffffffff : 0
MOVQ MM5,[aPoint]
PAND MM0, MM3        //abs(x) > 2^24 ? x : 0
PADDD MM1, MM2       //float(trunc(x)) > x ? trunc(x)-1 : trunc(x)
PI2FD MM4, MM1       //floor(x)
PANDN MM3, MM4       //abs(x) > 2^24 ? 0 : floor(x)
POR MM0, MM3         //abs(x) > 2^24 ? x : floor(x) state
PFSUB MM5,MM0
MOVQ  [aFraction],MM5
PF2ID MM0, MM0
MOVQ [aIntOut],MM0
FEMMS
end;


Gruss Horst
Spaceguide Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 552


(D3/D7/D8) Prof.
BeitragVerfasst: Fr 12.05.06 12:27 
Hmm, also wenn ich mal in einer Konsolenapplikation teste, ist die SSE-Routine auf einmal reproduzierbar die schnellste:

AthlonXP:

Leer: 411ms
FPU/Pascal: 5535ms
BenBE_FPU: 3323ms
SSE: 2782ms

Du meinst das liegt an einem Grafikkartentreiber (hab auch eine ATI drin)? Wie kann das sein?
Horst_H
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1654
Erhaltene Danke: 244

WIN10,PuppyLinux
FreePascal,Lazarus
BeitragVerfasst: Fr 12.05.06 14:44 
Hallo,

was kann das nur sein, heute habe ich keine Abweichung festgestellt, auch mit VCL nicht
Ein paar globale Variablen wie weiter oben.
ausblenden 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:
queryperformancefrequency(Tf);
fPunkt.x := pi;
fPunkt.y := sqrt(2);
max := 1000*1000*10;
mittel := 0.0;
For k := 0 to 99 do
  begin
  queryperformanceCounter(T0);
  for i := max downto 1 do
    FracTruncXY_SSE(fPunkt,iPunkt,fOut);
  queryperformanceCounter(T1);
  absZeit :=(T1-T0-Td)/Tf;
  mittel := (mittel*k+absZeit)/(k+1);
  //2.5% Abweichung vom Mittel
  If abs(absZeit/mittel-1.0) > 0.025then
    begin
    memo1.lines.add(Format('k:%4d %10.5f ms',[k,absZeit*1000.0]));
    memo1.lines.add(Format('%10.5f ms %10.5f ms',[absZeit*1000.0,mittel*1000.0]));
    end;
  end;
memo1.lines.add(Format(' %10.5f ms',[absZeit*1000.0]));
memo1.lines.add(Format('%10.5f ms %10.5f ms',[absZeit*1000.0,mittel*1000.0]));
end;


Ich dachte mir das nur bei Grafik SSE,MMX zum Zuge kommt, und welcher Treiber sollte dies sonst einsetzen.Zumal es eine der wenigen Gemeisamkeiten der beiden Rechner ist.
Oder macht dies WinXp mittlerweile auch?

Gruss Horst
P.S.:
Als Opera die E-mail's abholte, verdreifachte sich die Zeit, aber nicht der Faktor ~200 ,14 s statt 0.75 s