2011-02-01 3 views
6

4d 벡터를 정규화하려고합니다.SSE 정규화가 간단한 근사보다 느린가요?

내 첫 번째 승인은 내 벡터 산술에 2 배의 속도 향상을 제공하는 SSE 내장 함수를 사용하는 것이 었습니다. 다음은 기본 코드입니다 : (v.v4가 입력) (GCC를 사용하여) (이 모두가 인라인)

//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)); 

나는 분해를 확인하고 그것이 내가 기대하는 방법처럼 보인다. 거기에 큰 문제는 보이지 않습니다.

어쨌든, 그때 근사치를 사용하여 시도 : (내가 구글에서이있어) 그것은 SSE 버전보다 약간 더 빠르게 실행

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); 

! (약 5-10 % 빠름) 결과도 매우 정확합니다. 길이를 찾으면 0.001로 말할 것입니다! 하지만 .. GCC는 타입 펀칭 때문에 절름발이의 엄격한 앨리어싱 규칙을 제공하고 있습니다. 이제 (경고없이) 수정 된 버전 를 느리게 실행을

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); 

을 그리고! :

그래서 나는 그것을 수정 SSE 버전이 실행되는 속도의 거의 60 %가 실행됩니다 (그러나 같은 결과)! 왜 이런거야?

  1. 내 SSE의 implentation 맞 : 그래서 여기

    는 질문 (들)입니까?

  2. SSE는 일반적인 fpu 작업보다 실제로 느린가요?
  3. 왜 제 3의 코드가 그렇게 느린가요?
+0

사용중인 CPU를 알고 있으면 도움이됩니다. 예 : 오래된 x86 CPU (pre Core 2)는 SSE 기능이 매우 좋지 않습니다. –

+0

인텔 펜티엄 듀얼 코어 – Pubby

+3

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

답변

2

저는 마약입니다. 벤치마킹 중에 SETI @ Home을 실행했다는 것을 깨달았습니다. 내 SSE 성능이 죽어 가고있는 것 같아. 그것을 끄고 그것을 두 배 빠르게 달리게했다.

또한 AMD athlon에서 테스트 한 결과 SSE가 빠릅니다.

적어도 슈프 버그를 수정했습니다.

0

컴파일러가 메모리 변수에 유니온을 넣기로 결정 했으므로 제 3 버전이 느린 것 같습니다. 캐스트 케이스에서는 레지스터 값을 레지스터로 복사 할 수 있습니다. 생성 된 기계 코드를 볼 수 있습니다.

왜 SSE가 정확하지 않은지에 대한 대답이 없습니다. 실수를 줄 수 있다면 도움이 될 것입니다. 크기가 1 인 벡터에서 차이가 0.3이면 이는 어쩔 수없는 일입니다.

+0

x87 fpu는 내부적으로 80 비트 부동 소수점 값을 사용하여 계산하므로 더 정확합니다. – Trass3r

1

내가 생각할 수있는 가장 효율적인 어셈블리 코드는 다음과 같습니다. 이것을 컴파일러가 생성하는 것과 비교할 수 있습니다. 입력과 출력이 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(...) }