6

私は少しのコードをSSEに変換する作業をしていますが、正しい出力がありますが、標準のc++コードよりも遅いことがわかりました。

これを行うために必要なコードは次のとおりです。

float ox = p2x - (px * c - py * s)*m;
float oy = p2y - (px * s - py * c)*m;

私がSSEコードについて持っているのは:

void assemblycalc(vector4 &p, vector4 &sc, float &m, vector4 &xy)
{
    vector4 r;
    __m128 scale = _mm_set1_ps(m);

__asm
{
    mov     eax,    p       //Load into CPU reg
    mov     ebx,    sc
    movups  xmm0,   [eax]   //move vectors to SSE regs
    movups  xmm1,   [ebx]

    mulps   xmm0,   xmm1    //Multiply the Elements

    movaps  xmm2,   xmm0    //make a copy of the array  
    shufps  xmm2,   xmm0,  0x1B //shuffle the array     

    subps   xmm0,   xmm2    //subtract the elements

    mulps   xmm0,   scale   //multiply the vector by the scale

    mov     ecx,    xy      //load the variable into cpu reg
    movups  xmm3,   [ecx]   //move the vector to the SSE regs

    subps   xmm3,   xmm0    //subtract xmm3 - xmm0

    movups  [r],    xmm3    //Save the retun vector, and use elements 0 and 3
    }
}

コードを読むのは非常に難しいので、私がしたことを説明します。

ロードされたvector4、xmm0 _____ p = [px、py、px、py]
mult。vector4により、xmm1 _ cs = [c、c、s、s]
__________________________ mult ----------------------------
result、_____________ xmm0 = [px c、py c、px s、py s]

再利用結果、xmm0 = [px c、py c、px s、py s]
シャッフル結果、xmm2 = [py s、px s、py c、pxc ]
_____________________減算------------- ---------------
結果、xmm0 = [px c-py s、py c-px s、px s-py c、py s-px c]

再利用結果、xmm0 = [px c-py s、py c-px s、px s-py c、py s-px c]
load m vector4、scale = [m、m、m、m]
__________________________ mult ---- ------------------------
結果、xmm0 = [(px c-py s)m、(py c-px * s)m、(px s-py * c)m、(py s-px * c)m]


load xy vector4、xmm3 = [p2x、p2x、p2y、p2y]
再利用、xmm0 = [(px
c-py * s)m、(py c-px * s)m、(px s-py * c)m、(py s-px * c)m]
_____________________ subtract --------------------- -------
結果、xmm3 = [p2x-(px
c-py * s)m、p2x-(py c-px * s)m、p2y-(px s-py * c)m、p2y-(py s-px * c)* m]

次に、ox =xmm3[0]およびoy=xmm3 [3]なので、基本的にxmm3[1]またはxmm3[4]は使用しません。

これを読むのが難しいことをお詫びしますが、標準のc ++コードは0.001444msで実行され、SSEコードは0.00198msで実行されるため、誰かが私に何らかのガイダンスを提供してくれることを願っています。

これをさらに説明/クリーンアップするために私にできることがあれば教えてください。私がSSEを使おうとしている理由は、この計算を何百万回も実行しているためです。これは、現在のコードの速度を低下させている原因の一部です。

助けてくれてありがとう!ブレット

4

1 に答える 1

9

この種のベクトル化を行う通常の方法は、問題を「その側に」向けることです。oxとの単一の値を計算する代わりに、 4つの値と4つの値を同時にoy計算します。これにより、無駄な計算とシャッフルが最小限に抑えられます。oxoy

これを行うには、いくつかxy、、p2xおよびp2y値を連続した配列にバンドルします(つまり、の4つの値の配列、、の4つの値の配列などがある場合がありxますy)。次に、次のことを実行できます。

movups  %xmm0,  [x]
movups  %xmm1,  [y]
movaps  %xmm2,  %xmm0
mulps   %xmm0,  [c]    // cx
movaps  %xmm3,  %xmm1
mulps   %xmm1,  [s]    // sy
mulps   %xmm2,  [s]    // sx
mulps   %xmm3,  [c]    // cy
subps   %xmm0,  %xmm1  // cx - sy
subps   %xmm2,  %xmm3  // sx - cy
mulps   %xmm0,  scale  // (cx - sy)*m
mulps   %xmm2,  scale  // (sx - cy)*m
movaps  %xmm1,  [p2x]
movaps  %xmm3,  [p2y]
subps   %xmm1,  %xmm0  // p2x - (cx - sy)*m
subps   %xmm3,  %xmm2  // p2y - (sx - cy)*m
movups  [ox],   %xmm1
movups  [oy],   %xmm3

このアプローチを使用すると、18の命令で4つの結果を同時に計算しますが、アプローチでは13の命令で1つの結果を計算します。また、結果を無駄にすることもありません。

それでも改善される可能性があります。このアプローチを使用するには、とにかくデータ構造を再配置する必要があるため、配列を整列させ、整列されていないロードとストアを使用する必要があります。cとsをレジスタにロードし、それらを使用して、ベクトルごとにリロードするのではなく、xとyの多くのベクトルを処理する必要があります。最高のパフォーマンスを得るには、2つ以上のベクトルに相当する計算をインターリーブして、プロセッサがパイプラインストールを防ぐのに十分な作業を行うようにする必要があります。

cx + sy(補足:代わりにすべきcx - syですか?それはあなたに標準的な回転行列を与えるでしょう)

編集

Your comment on what hardware you're doing your timings on pretty much clears everything up: "Pentium 4 HT, 2.79GHz". That's a very old microarchitecture, on which unaligned moves and shuffles are quite slow; you don't have enough work in the pipeline to hide the latency of the arithmetic operations, and the reorder engine isn't nearly as clever as it is on newer microarchitectures.

I expect that your vector code would prove to be faster than the scalar code on i7, and probably on Core2 as well. On the other hand, doing four at a time, if you could, would be much faster still.

于 2010-05-27T17:57:27.070 に答える