8

2ブロックのメモリをできるだけ早くXORしたいのですが、SIMDを使用して高速化するにはどうすればよいですか?

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

void region_xor_w64(   unsigned char *r1,         /* Region 1 */
                       unsigned char *r2,         /* Region 2 */
                       int nbytes)       /* Number of bytes in region */
{
    uint64_t *l1;
    uint64_t *l2;
    uint64_t *ltop;
    unsigned char *ctop;

    ctop = r1 + nbytes;
    ltop = (uint64_t *) ctop;
    l1 = (uint64_t *) r1;
    l2 = (uint64_t *) r2;

    while (l1 < ltop) {
        *l2 = ((*l1)  ^ (*l2));
        l1++;
        l2++;
    }
}

自分で書いたのですが、少しスピードが上がりました。

void region_xor_sse(   unsigned char* dst,
                       unsigned char* src,
                       int block_size){
  const __m128i* wrd_ptr = (__m128i*)src;
  const __m128i* wrd_end = (__m128i*)(src+block_size);
  __m128i* dst_ptr = (__m128i*)dst;

  do{
    __m128i xmm1 = _mm_load_si128(wrd_ptr);
    __m128i xmm2 = _mm_load_si128(dst_ptr);

    xmm2 = _mm_xor_si128(xmm1, xmm2);
    _mm_store_si128(dst_ptr, xmm2);
    ++dst_ptr;
    ++wrd_ptr;
  }while(wrd_ptr < wrd_end);
}
4

2 に答える 2

11

より重要な質問は、なぜ手動で実行したいのかということです。あなたはあなたが賢くできると思う古代のコンパイラを持っていますか?SIMD命令を手動で作成しなければならなかった古き良き時代は終わりました。今日、99%の場合、コンパイラーがあなたに代わって仕事をしてくれます。そして、それよりもはるかに良い仕事をする可能性があります。また、命令セットがますます拡張された新しいアーキテクチャがときどき出てくることを忘れないでください。それで、自分自身に質問してください—プラットフォームごとに実装のN個のコピーを維持したいですか?実装を継続的にテストして、維持する価値があることを確認しますか?ほとんどの場合、答えはノーです。

あなたがする必要がある唯一のことは、可能な限り単純なコードを書くことです。残りはコンパイラが行います。たとえば、これが私があなたの関数を書く方法です:

void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int len)
{
    unsigned int i;
    for (i = 0; i < len; ++i)
        r2[i] = r1[i] ^ r2[i];
}

もう少し簡単ですね。そして、コンパイラがとを使用して128ビットXORを実行するコードを生成していると推測するMOVDQUPXOR、クリティカルパスは次のようになります。

4008a0:       f3 0f 6f 04 06          movdqu xmm0,XMMWORD PTR [rsi+rax*1]
4008a5:       41 83 c0 01             add    r8d,0x1
4008a9:       f3 0f 6f 0c 07          movdqu xmm1,XMMWORD PTR [rdi+rax*1]
4008ae:       66 0f ef c1             pxor   xmm0,xmm1
4008b2:       f3 0f 7f 04 06          movdqu XMMWORD PTR [rsi+rax*1],xmm0
4008b7:       48 83 c0 10             add    rax,0x10
4008bb:       45 39 c1                cmp    r9d,r8d
4008be:       77 e0                   ja     4008a0 <region_xor_w64+0x40>

@Mysticialが指摘しているように、上記のコードは非整列アクセスをサポートする命令を使用しています。それらは遅いです。ただし、プログラマーが整列されたアクセスを正しく想定できる場合は、コンパイラーにそのことを知らせることができます。例えば:

void region_xor_w64(unsigned char * restrict r1,
                    unsigned char * restrict r2,
                    unsigned int len)
{
    unsigned char * restrict p1 = __builtin_assume_aligned(r1, 16);
    unsigned char * restrict p2 = __builtin_assume_aligned(r2, 16);

    unsigned int i;
    for (i = 0; i < len; ++i)
        p2[i] = p1[i] ^ p2[i];
}

コンパイラーは、上記のCコードに対して以下を生成します(注意movdqa)。

400880:       66 0f 6f 04 06          movdqa xmm0,XMMWORD PTR [rsi+rax*1]
400885:       41 83 c0 01             add    r8d,0x1
400889:       66 0f ef 04 07          pxor   xmm0,XMMWORD PTR [rdi+rax*1]
40088e:       66 0f 7f 04 06          movdqa XMMWORD PTR [rsi+rax*1],xmm0
400893:       48 83 c0 10             add    rax,0x10
400897:       45 39 c1                cmp    r9d,r8d
40089a:       77 e4                   ja     400880 <region_xor_w64+0x20>

明日、Haswell CPUを搭載したラップトップを購入すると、コンパイラーは、同じコードから128ビットではなく256ビットの命令を使用するコードを生成し、2倍のベクトルパフォーマンスを実現します。Haswellがそれを実行できることを知らなくても、それは実行されます。その機能について知っているだけでなく、コードの別のバージョンを作成して、テストに時間を費やす必要があります。

ちなみに、コードがデータベクトルの残りの最大3バイトをスキップできるという実装にもバグがあるようです。

とにかく、私はあなたがあなたのコンパイラを信頼し、何が生成されるかを検証する方法を学ぶことをお勧めします(すなわちobjdump)。次の選択肢は、コンパイラを変更することです。その後、ベクトル処理命令を手動で作成することを検討し始めます。またはあなたは悪い時間を過ごすつもりです!

それが役に立てば幸い。幸運を!

于 2013-02-25T23:39:27.020 に答える
0

領域のサイズは値で渡されるため、コードは次のようになりません。

void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int i)
{
    while (i--)
        r2[i] = r1[i] ^ r2[i];
}

あるいは:

void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int i)
{
    while (i--)
        r2[i] ^= r1[i];
}

前進(「アップメモリ​​」)およびポインタの使用を優先する場合は、次のようにします。

void region_xor_w64(unsigned char *r1, unsigned char *r2, unsigned int i)
{
    while (i--)
        *r2++ ^= *r1++;
}
于 2019-03-08T13:13:17.717 に答える