6

4Dベクトルを正規化しようとしています。

私の最初のアプローチは、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インペンションは正しいですか?
  2. SSEは通常のfpu操作よりも本当に遅いですか?
  3. なぜ3番目のコードはそれほど遅いのですか?
4

3 に答える 3

2

I am a dope - I realized I had SETI@Home running while benchmarking. I'm guessing it was killing my SSE performance. Turned it off and got it running twice as fast.

I also tested it on an AMD athlon and got the same results - SSE was faster.

At least I fixed the shuf bug!

于 2011-02-01T20:10:55.680 に答える
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(...) }
于 2011-02-01T21:46:07.733 に答える
0

私の推測では、コンパイラがユニオンをメモリ変数に入れることを決定したため、3番目のバージョンは遅くなります。キャストの場合、レジスタからレジスタに値をコピーできます。生成されたマシンコードを見るだけです。

SSEが不正確である理由については、私には答えがありません。あなたが実数を与えることができればそれは助けになるでしょう。サイズ1のベクトルで差が0.3の場合、それは法外なことです。

于 2011-02-01T19:31:19.210 に答える