3

簡単な問題があります。uint_32 の開始値 (125 など) と追加するオペランドの __m128i (+5、+10、-1、-5) を持ちます。できるだけ早く取得したいのは、ベクトル (125 + 5、125 + 5 + 10、125 + 5 + 10 - 1、125 + 5 + 10 - 1 - 5)、つまり、オペランドから値を累積的に追加することです開始値に。これまでのところ、私が考えることができる唯一の解決策は、4 つの __m128i 変数を追加することです。たとえば、次のようになります。

/* pseudoSSE code... */
__m128i src =     (125,125,125,125)
__m128i operands =(5,10,-1,-5)

/*  Here I omit the partitioning of operands into add1,..add4 for brevity  */

__m128i add1 =    (+05,+05,+05,+05)
__m128i add2 =    (+00,+10,+10,+10)
__m128i add3 =    (+00,+00,-01,-01)
__m128i add4 =    (+00,+00,+00,-05)
__m128i res1 = _mm_add_epu32( add1, add2 )
__m128i res2 = _mm_add_epu32( add3, add4 )
__m128i res3 = _mm_add_epu32( res1, add2 )
__m128i res  = _mm_add_epu32( res3, src  )

このように、私は私が欲しかったものを手に入れます。このソリューションでは、すべての add_ 変数を設定してから、4 つの追加を実行する必要があります。私が本当に求めているのは、これがより速くできるかどうかです。いくつかの異なるアルゴを介して、またはおそらくまだ知らない特殊な SSE 関数 (_mm_cumulative_sum() など) を使用します。どうもありがとう。

4

2 に答える 2

5

さらに並列処理を追加して、4 つの代わりに 3 つの追加を使用できます。

const __m128i src = _mm_set1_epi32(125);
const __m128i operands = _mm_set_epi32(5,10,-1,-5);

const __m128i shift1 =
  _mm_add_epi32(operands,
    _mm_and_si128(_mm_shuffle_epi32(operands, 0xF9),
                  _mm_set_epi32(0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF)));

const __m128i shift2 =
  _mm_add_epi32(shift1,
    _mm_and_si128(_mm_shuffle_epi32(shift1, 0xFE),
                  _mm_set_epi32(0,0,0xFFFFFFFF,0xFFFFFFFF)));

const __m128i res = _mm_add_epi32(src, shift2);

ここでは SSE2 命令セットが使用されます。新しい命令セットでは、_mm_and_si128/_mm_shuffle_epi32 を _mm_shuffle_epi8 のような単一の命令に置き換えることができます。

累積合計は、以下に示すように 2 つの加算で計算されます。

   a    b    c    d
 +      a    b    c
  ------------------
   a   a+b  b+c  c+d
 +           a   a+b
  ------------------
   a   a+b a+b+c a+b+c+d

SSE は、このようなタスクには適していません。そのパフォーマンスは「垂直」操作でのみ優れていますが、ここで必要な「水平」操作には多くの追加作業が必要です。

于 2012-10-19T13:28:54.887 に答える
1

皆さん、助けてくれてありがとう。どのバージョンが最速かを判断するために、テスト アプリを作成しました。

1/ 非 SSE バージョンは、期待どおりにすべてのことを行います。

int iRep;
int iCycle;
int iVal = 25;
int a1, a2, a3, a4;
int dst1 [4];
for ( iCycle = 0; iCycle < CYCLE_COUNT; iCycle++ )
    for ( iRep = 0; iRep < REP_COUNT; iRep++ )
        {   
          a1 = a2 = a3 = a4 = iRep;
          dst1[0] = iVal + a1;
          dst1[1] = dst1[0] + a2;
          dst1[2] = dst1[1] + a3;
          dst1[3] = dst1[2] + a4;
        }

2/ SSE-4 の追加は、私が提案したことを行います。

__m128i _a1, _a2, _a3, _a4;
__m128i _res1, _res2, _res3;
__m128i _val;
__m128i _res;

for ( iCycle = 0; iCycle < CYCLE_COUNT; iCycle++ )
    for ( iRep = 0; iRep < REP_COUNT; iRep++ ){ 
        a1 = a2 = a3 = a4 = iRep;

        _val = _mm_set1_epi32( iVal );
        _a1  = _mm_set_epi32 (a1, a1, a1, a1 );
        _a2  = _mm_set_epi32 (a2, a2, a2, 0  );
        _a3  = _mm_set_epi32 (a3, a3, 0 , 0  );
        _a4  = _mm_set_epi32 (a4, 0 , 0 , 0  );

        _res1 = _mm_add_epi32( _a1, _a2     );
        _res2 = _mm_add_epi32( _a3, _a4     );
        _res3 = _mm_add_epi32( _val, _res1  );
        _res  = _mm_add_epi32( _res3, _res2 );
    }

3/ SSE-3 の追加は、Evgeny が提案したことを行います。

__m128i shift1, shift2, operands ;
for ( iCycle = 0; iCycle < CYCLE_COUNT; iCycle++ )
    for ( iRep = 0; iRep < REP_COUNT; iRep++ ){ 
        a1 = a2 = a3 = a4 = iRep;

        _val = _mm_set1_epi32( iVal );
        operands = _mm_set_epi32(a1,a2,a3,a4);

        shift1 = _mm_add_epi32( operands,               
                 _mm_and_si128(_mm_shuffle_epi32(operands, 0xF9),   _mm_set_epi32(0,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF)   ));
        shift2 = _mm_add_epi32( shift1, 
                 _mm_and_si128(_mm_shuffle_epi32(shift1, 0xFE),     _mm_set_epi32(0,0,0xFFFFFFFF,0xFFFFFFFF)            ));
        _res = _mm_add_epi32(_val, shift2); 
        }

の結果

 #define REP_COUNT    100000
 #define CYCLE_COUNT  100000

それは

  non-SSE        -> 6.118s
  SSE-4additions -> 20.775s
  SSE-3additions -> 14.873s

むしろ意外…

于 2012-10-19T20:41:52.777 に答える