2011-02-01 13 views
6

Próbuję znormalizować wektor 4d.Normalizacja SSE wolniejsza od prostej aproksymacji?

Pierwszym moim zadaniem było zastosowanie wewnętrznej samoistności SSE - czegoś, co zapewniło 2-krotne zwiększenie prędkości mojej arytmetyki wektorowej. Oto kod podstawowy: (v.v4 jest wejście) (przy użyciu GCC) (wszystko to inlined)

//find squares 
v4sf s = __builtin_ia32_mulps(v.v4, v.v4); 
//set t to square 
v4sf t = s; 
//add the 4 squares together 
s = __builtin_ia32_shufps(s, s, 0x1B); 
t  = __builtin_ia32_addps(t, s); 
s = __builtin_ia32_shufps(s, s, 0x4e); 
t  = __builtin_ia32_addps(t, s); 
s = __builtin_ia32_shufps(s, s, 0x1B); 
t  = __builtin_ia32_addps(t, s); 
//find 1/sqrt of t 
t  = __builtin_ia32_rsqrtps(t); 
//multiply to get normal 
return Vec4(__builtin_ia32_mulps(v.v4, t)); 

sprawdzić demontaż i wygląda na to, jak by się spodziewać. Nie widzę tam żadnych dużych problemów.

Anyways, potem próbowałem go stosując przybliżenie (mam to z google)

float x = (v.w*v.w) + (v.x*v.x) + (v.y*v.y) + (v.z*v.z); 
float xhalf = 0.5f*x; 
int i = *(int*)&x; // get bits for floating value 
i = 0x5f3759df - (i>>1); // give initial guess y0 
x = *(float*)&i; // convert bits back to float 
x *= 1.5f - xhalf*x*x; // newton step, repeating this step 
// increases accuracy 
//x *= 1.5f - xhalf*x*x; 
return Vec4(v.w*x, v.x*x, v.y*x, v.z*x); 

To działa nieco szybciej niż w wersji SSE! (o 5-10% szybciej) Jego wyniki są również bardzo dokładne - powiedziałbym do 0,001 przy ustalaniu długości! Ale .. GCC daje mi tę kulawą regułę ścisłego aliasingu z powodu typowania.

więc modyfikować go:

union { 
    float fa; 
    int ia; 
}; 
fa = (v.w*v.w) + (v.x*v.x) + (v.y*v.y) + (v.z*v.z); 
float faHalf = 0.5f*fa; 
ia = 0x5f3759df - (ia>>1); 
fa *= 1.5f - faHalf*fa*fa; 
//fa *= 1.5f - faHalf*fa*fa; 
return Vec4(v.w*fa, v.x*fa, v.y*fa, v.z*fa); 

a teraz zmodyfikowaną wersję (bez ostrzeżenia) działa wolniej !! Działa prawie 60% szybciej niż wersja SSE (ale ten sam wynik)! Dlaczego to?

Więc tutaj jest pytanie (s):

  1. Czy mój SSE implentation prawidłowe?
  2. Czy SSE jest naprawdę wolniejszy niż zwykłe operacje Fpu?
  3. Dlaczego, do cholery, trzeci kod jest o wiele wolniejszy?
+0

Pomoże to wiedzieć, z jakiego procesora korzystasz. Na przykład. stare procesory x86 (pre Core 2) miały bardzo słabe możliwości SSE. –

+0

Jestem na dwurdzeniowym procesorze Intel Pentium – Pubby

+3

Duplikat adresu http://stackoverflow.com/questions/1528727/why-is-sse-scalar-sqrtx-slower-than-rsqrtx-x? – celion

Odpowiedz

2

Jestem zajebisty - zdałem sobie sprawę, że SETI @ Home działa podczas testu porównawczego. Zgaduję, że zabijał mój występ SSE. Wyłączyłem go i uruchomiłem dwa razy szybciej.

Testowałem również na athlonie AMD i otrzymałem te same wyniki - SSE był szybszy.

Przynajmniej naprawiłem błąd shuf!

0

Domyślam się, że trzecia wersja jest wolniejsza, ponieważ kompilator decyduje się umieścić unię w zmiennej pamięci. W przypadku rzutowania może kopiować wartości z rejestru do rejestru. Możesz po prostu spojrzeć na wygenerowany kod maszynowy.

Co do tego, dlaczego SSE jest niedokładne, nie mam odpowiedzi. Pomoże ci to, jeśli możesz podać prawdziwe liczby. Jeśli różnica wynosi 0,3 na wektorze o rozmiarze 1, byłoby to oburzające.

+0

Parametr x87 jest bardziej dokładny, ponieważ wewnętrznie oblicza wartości 80bit zmiennoprzecinkowe. – Trass3r

1

Oto najbardziej wydajny kod zespołu, jaki mogę wymyślić. Można to porównać do tego, co generuje twój kompilator. załóżmy, że dane wejściowe i wyjściowe są w XMM0.

 ; start with xmm0 = { v.x v.y v.z v.w } 
     movaps %xmm0, %mm1   ; save it till the end 
     mulps %xmm0, %xmm0  ; v=v*v 
     pshufd $1, %xmm0, %xmm1 ; xmm1 = { v.y v.x v.x v.x } 
     addss %xmm0, %xmm1  ; xmm1 = { v.y+v.x v.x v.x v.x } 
     pshufd $3, %xmm0, %xmm2 ; xmm2 = { v.w v.x v.x v.x } 
     movhlps %xmm0, %xmm3  ; xmm3 = { v.z v.w ? ? } 
     addss %xmm1, %xmm3  ; xmm3 = { v.y+v.x+v.z v.x ? ? } 
     addss %xmm3, %xmm2  ; xmm2 = { v.y+v.x+v.z+v.w v.x v.x v.x } 
     rsqrtps %xmm2, %xmm1  ; xmm1 = { rsqrt(v.y+v.x+v.z+v.w) ... } 
     pshufd $0, %xmm1, %xmm1 ; xmm1 = { rsqrt(v.y+v.x+v.z+v.w) x4 } 
     mulps %xmm1, %xmm0  
     ; end with xmm0 = { v.x*sqrt(...) v.y*sqrt(...) v.z*sqrt(...) v.w*sqrt(...) }