2011-02-01 54 views
6

我想正常化一個4D矢量。SSE歸一化慢於簡單近似?

我的第一個嘗試是使用SSE內在函數 - 這爲我的向量算法提供了2倍的速度提升。 這裏是基本的代碼:(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)); 

我檢查拆卸和它看起來像我多麼期望。我在那裏看不到什麼大問題。

不管怎麼說,然後我嘗試用近似:(我得到這個從谷歌)

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

它比SSE版本稍快運行! (快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%(但結果相同)!爲什麼是這樣?

所以在這裏是問題(S):

  1. 是我SSE implentation是否正確?
  2. SSE真的比普通的fpu操作慢嗎?
  3. 爲什麼地獄是第三代碼慢得多?
+0

這將有助於知道你在使用什麼樣的CPU。例如。舊的x86 CPU(Core 2之前)的SSE功能很差。 – 2011-02-01 19:17:41

+0

我在英特爾奔騰雙核 – Pubby 2011-02-01 19:27:16

回答

2

我是一個塗料 - 我意識到我有SETI @ Home在基準測試中運行。我猜這是在破壞我的上證所表現。把它關掉,讓它跑得快兩倍。

我也在AMD Athlon上測試過它,得到了相同的結果--SSE更快。

至少我修好了shuf的bug!

0

我的猜測是第三個版本比較慢,因爲編譯器決定把聯合放在一個內存變量中。在強制轉換的情況下,它可以將寄存器中的值複製到寄存器中。你可以看看生成的機器碼。至於爲什麼上證不準確,我沒有答案。如果你能給出真實的數字,這將有所幫助。如果大小爲1的矢量的差異爲0.3,那將是無恥的。

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(...) }