2011-02-01 11 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%です(ただし同じ結果)!どうしてこれなの?

だからここに質問(複数可)されています

  1. は正しい私のSSEのimplentationですか?
  2. SSEは通常のfpu操作よりも実際に遅いですか?
  3. なぜ3番目のコードが非常に遅いのですか?
+0

これは、使用しているCPUを知るのに役立ちます。例えば。古いx86 CPU(pre Core 2)は非常に劣悪なSSE機能を持っていました。 –

+0

私はIntel Pentium Dual-Coreを使用しています – Pubby

+3

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

答えて

2

私はドープです - ベンチマーク中にSETI @ Homeを実行していたことがわかりました。私はそれが私のSSEのパフォーマンスを殺していたと思います。それをオフにして、それを2倍速く走らせた。

私も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(...) } 
関連する問題