Registriert seit: 19. Nov 2010
212 Beiträge
|
AW: [ASM / SSE] Vektoroperationen
18. Jun 2012, 17:37
Falls es jemanden interessiert, hab eine Lösung gefunden, wie man mit SSE einen Vektor normalisieren kann. Den genauen Performancegewinn muss ich noch messen, werd ich dann posten, aber läuft auf jeden fall schneller als die Alternative:
Delphi-Quellcode:
function TVec4.Normalize: Single;
//Compilieren mit asm?
{$IFDEF USEASM}
asm
MOVUPS XMM0, DQWORD PTR[&self]
//Copy stored in MMX2
MOVAPS XMM2, XMM0
//Quadrupe MMX0
MULPS XMM0, XMM0
//Store Copy of Quadrupled Values in MMX1
MOVAPS XMM1, XMM0
SHUFPS XMM0, XMM1, $4e //0100 1110
//XMM0 = z^2 w^2 x^2 y^2
//XMM1 = x^2 y^2 z^2 w^2
ADDPS XMM0, XMM1
//XMM0 z^2 + x^2, w^2 + y^2, z^2 + x^2, w^2 + y^2
MOVAPS XMM1, XMM0
SHUFPS XMM1, XMM1, $11 //0001 0001
//XMM1 w^2 + y^2, z^2 + x^2, w^2 + y^2, z^2 + x^2
ADDPS XMM0, XMM1
//XMM0 z^2 + x^2 + w^2 + y^2
//Return the length
SQRTSS XMM3, XMM0
MOVSS [&Result], XMM3
//Multiply with Rect Sqrt - Very Fast due to CPU-Internal LookUp Table
RSQRTPS XMM0, XMM0
MULPS XMM2, XMM0
MOVUPS DQWORD PTR[&self], XMM2
end;
{$ELSE}
var
i: Single;
begin
i := Length;
//If the length is zero, don't touch the vector
if i = 0 then
Exit(0);
i := 1 / i;
//w shouldnt be touched
x := x * i;
y := y * i;
z := z * i;
//Return the length
Exit(i);
end;
{$ENDIF}
Und zur bestimmung der Länge (benutzt nicht alle 4 werte am Ende, und benutzt nicht RSQRT):
Delphi-Quellcode:
function TVec4.Length: Single;
//Compilieren mit asm?
{$IFDEF USEASM}
asm
MOVUPS XMM0, DQWORD PTR [&self]
//Quadrupe MMX0
MULPS XMM0, XMM0
//Store Copy of Quadrupled Values in MMX1
MOVAPS XMM1, XMM0
SHUFPS XMM0, XMM1, $4e //0100 1110
//XMM0 = z^2 w^2 x^2 y^2
//XMM1 = x^2 y^2 z^2 w^2
ADDPS XMM0, XMM1
//XMM0 z^2 + x^2, w^2 + y^2, z^2 + x^2, w^2 + y^2
MOVAPS XMM1, XMM0
SHUFPS XMM1, XMM1, $11 //0001 0001
//XMM1 w^2 + y^2, z^2 + x^2, w^2 + y^2, z^2 + x^2
ADDSS XMM0, XMM1
//XMM0 z^2 + x^2 + w^2 + y^2
//Multiply with Sqrt - Very Fast due to CPU-Internal LookUp Table
SQRTPS XMM0, XMM0
MOVSS Result, XMM0
end;
{$ELSE}
begin
result := Sqrt(x * x + y * y + z * z + w * w);
end;
{$ENDIF}
Diese Algorithmen sind aufgrund der CPU-Internen Lookup-Tabelle für Sqrt ungenauer als die Berechnung über delphi's sqrt, aber trotzdem noch ziemlich genau, sollten also für die meisten Berechnungen reichen. Wenns jedoch um Genauigkeit geht und die Zeit egal ist, sollte man nicht die SSE-Variante benutzen.
[EDIT]
Performancegewinn liegt ebenfalls bei über 30% (Länge bestimmen), 50%+ (Normalisieren), gemessen auf einem i5-760
Geändert von Edlmann (18. Jun 2012 um 18:57 Uhr)
|