6

私はSSE組み込み関数を使用して小さなコードを最適化しようとしています(私はこのトピックの完全な初心者です)が、条件の使用に少し固執しています。

私の元のコードは次のとおりです。

unsigned long c;
unsigned long constant = 0x12345678;
unsigned long table[256];
int n, k;

for( n = 0; n < 256; n++ )
{
  c = n;
  for( k = 0; k < 8; k++ )
    {
      if( c & 1 ) c = constant ^ (c >> 1);
      else c >>= 1;
    }
  table[n] = c;
}

このコードの目的は、crcテーブルを計算することです(定数は任意の多項式にすることができ、ここでは役割を果たしません)、

最適化されたコードは次のようになると思います。

__m128 x;
__m128 y;
__m128 *table;

x = _mm_set_ps(3, 2, 1, 0);
y = _mm_set_ps(3, 2, 1, 0);
//offset for incrementation
offset = _mm_set1_ps(4);

for( n = 0; n < 64; n++ )
{
    y = x;
    for( k = 0; k < 8; k++ )
    {
        //if do something with y
        //else do something with y
    }
    table[n] = y;
    x = _mm_add_epi32 (x, offset);
}

if-elseステートメントをどのように処理するかはわかりませんが、巧妙なトリックがあるのではないかと思います。誰かがそれを行う方法についてのアイデアを持っていますか?

(これを除けば、私の最適化はおそらくかなり貧弱です-それに関するアドバイスや修正は最大の共感で扱われます)

4

3 に答える 3

12

if/elseを完全に取り除くことができます。私がMMXアセンブリコードを作成した頃、それは一般的なプログラミング活動でした。「false」ステートメントの一連の変換から始めましょう。

c >>= 1;

c = c >> 1;

c = 0 ^ (c >> 1);

なぜ排他的論理和を導入したのですか?排他的論理和は「真の」ステートメントにも含まれているため:

c = constant ^ (c >> 1);

類似性に注意してください?「真」の部分では、定数でxorし、偽の部分では、ゼロでxorします。

次に、if/elseステートメント全体の一連の変換を示します。

if (c & 1)
    c = constant ^ (c >> 1);          // same as before
else
    c =        0 ^ (c >> 1);          // just different layout

if (c & 1)
    c =  constant      ^ (c >> 1);
else
    c = (constant & 0) ^ (c >> 1);    // 0 == x & 0

if (c & 1)
    c = (constant & -1) ^ (c >> 1);   // x == x & -1
else
    c = (constant &  0) ^ (c >> 1);

これで、2つのブランチは、binary-andの2番目の引数のみが異なります。これは、条件自体から簡単に計算できるため、if/elseを取り除くことができます。

c = (constant & -(c & 1)) ^ (c >> 1);

免責事項:このソリューションは、-1が「すべてのビットが設定されている」ことを意味する2の補数アーキテクチャでのみ機能します。

于 2011-06-09T10:18:38.277 に答える
2

SSEの考え方は、両方の結果を作成してから、結果をブレンドすることです。

例:

__m128i mask = ...; // some way to build mask[n] = 0x1
__m128i constant = ...;

__m128i tmp_c = _mm_xor_si128( _mm_srli_epis32( c, 1 ), constant );
__m128i tmp_c2 = _mm_srli_epis32( c, 1 );

__m128i v = _mm_cmpeq_epi32( c, mask );
tmp_c = _mm_and_epi32( tmp_c, mask );
tmp_c2 = _mm_andnot_si128( mask, tmp_c2 );
c = _mm_or_si128( tmp_c, tmp_c2 );
// or in sse4_1
c = _mm_blendv_epi8( tmp_c, tmp_c2, mask );

それに、これは完全なコードではなく、原則を示すためだけのものであることに注意してください。

于 2011-06-09T10:18:42.883 に答える
1

CRCを効率的に計算するための最初のステップは、ビットよりも広い基本単位を使用することです。このバイトごとの実行方法の例については、ここを参照してください。

于 2011-06-09T09:15:57.770 に答える