Nachmittag DPLer,
ich habe mich heute mal daran Gesetz, die SIMD-Extensions ein wenig genauer unter die Lupe zu nehmen,
speziell wie man diese zur Berechnung von Vektoren einsetzen kann. Ich hab dafür ein paar kleine Testroutinen gebastelt,
und hätte 2 Fragen:
1. Wo liegt noch Optimierungspotential?
2. Warum ist die normale Addition gegenüber der SSE-Single-Vector Addition nur minimal schneller?
(Messwerte für 2 Millionen Verticies operationen, einmal addieren je 2er Vektoren, 1x Multiplizieren:
Ganz normaler Delphi-Code: im Schnitt 109.000 Zykel
SSE-Single-Vector: im Schnitt 102.000 Zykel
Addieren ganzer Arrays: 62.736).
Zum Quelltext dahinter:
Die Vektoren sind als einfache Records spezifiziert
Delphi-Quellcode:
const
ALENGTH = 2000000;
type
TTestVec = packed record
x, y, z, w: Single;
end;
...
Vecs1: array[0..ALENGTH] of TTestVec;
Vecs2: array[0..ALENGTH] of TTestVec;
ResVecs: array[0..ALENGTH] of TTestVec;
Und die Methoden(hier mal nur die zum Addieren, die zum Multiplizieren dementsprechend statt + / ADDPS mit * / MULPS)
Standard Delphi:
Delphi-Quellcode:
function AddVecs(const av1, av2: TTestVec): TTestVec;
begin
Result.x := av1.x + av2.x;
Result.y := av1.y + av2.y;
Result.z := av1.z + av2.z;
Result.w := av1.w + av2.w;
end;
Single-Vector-SSE:
Delphi-Quellcode:
function AddVecsSSE(
const av1, av2: TTestVec): TTestVec;
var p1, p2: Pointer;
begin
p1 := @av1.x;
p2 := @av2.x;
asm
MOV ECX, p1
MOV EDX, p2
MOVUPS XMM0, [ECX]
MOVUPS XMM1, [EDX]
ADDPS XMM0, XMM1
MOV ECX, @Result.x
MOVUPS [ECX], XMM0
end;
end;
und schließlich Array-SSE-Addieren:
Delphi-Quellcode:
procedure AddVecsArraySSE(
const av1, av2: Pointer;
const outarray: Pointer;
const Length: Integer;
const Strafing: Integer);
begin
asm
//ECX = 1st Array
//EDX = 2nd Array
//EBX = Length of the Array
//EAX = Pointer to the outarray
MOV ECX, av1
MOV EDX, av2
MOV EBX, Length
MOV EAX, outarray
@@LoopLabel:
MOVUPS XMM0, [ECX]
MOVUPS XMM1, [EDX]
ADDPS XMM0, XMM1
MOVUPS [EAX], XMM0
//Die Pointer um Strafing verschieben
ADD ECX, Strafing
ADD EDX, Strafing
ADD EAX, Strafing
DEC EBX
//Sind wir mit der Länbe bei -1, haben wir unser Array durch
CMP EBX, -1
JNE @@LoopLabel
end;
end;
Und Aufgerufen wird der Spass mit:
Delphi-Quellcode:
for x := 0 to ALENGTH do
begin
ResVecs[x] := AddVecs(Vecs1[x], Vecs2[x]);
end;
for x := 0 to ALENGTH do
begin
ResVecs[x] := AddVecsSSE(Vecs1[x], Vecs2[x]);
end;
AddVecsArraySSE(@Vecs1[0].x, @Vecs2[0].x, @ResVecs[0].x, ALENGTH, 16);
Vielen Dank schonmal,
Edlmann
P.S. Ist nur ne Testimplementation, um zu schauen wie groß der Performancegewinn ist.