2014年9月6日土曜日

ベクトルレジスタでの位置合わせ (_mm256_shuffle_ps)

続けて、ベクトルレジスタ同士を結合してシフトの方法、AVX編。

AVXの場合は256ビット長のベクトルレジスタ2つに入ったデータを、
合わせてシフトする命令はありません。

そこでshuffle命令を使います。_mm256_shuffle_psです。

shuffle命令を使うと、2つの256ビット長レジスタから、
1つ目の上位128ビット長のどこか2か所の32ビット2つと、
2つ目の上位128ビット長のどこか2か所の32ビット2つ、
続けて1つ目の下位128ビット長の先程と同じ相対位置の32ビット2つ、
最後に2つ目の下位128ビット長のやはり先程と同じ相対位置の32ビット2つ、
これをまとめて計256ビット長の値を合成できます。
はっきりいって訳わかりません。

分かりやすく書くと、以下の処理でシフトできるわけです。
__m256 x02x03x04x05x06x07x08x09 =
    _mm256_shuffle_ps(
        x04x05x06x07x08x09x10x11,
        x00x01x02x03x04x05x06x07,
        _MM_SHUFFLE(1, 0, 3, 2) /* = 0x4E */);
これを文章で書くと、
まず第2引数の下位128ビットの2番目(8~11バイト)と3番目(12~15バイト)を取り出し、
次に第1引数の下位128ビットの0番目(0~3バイト)と1番目(4~7バイト)を取り、
続けて第2引数の上位128ビットの2番目(24~27バイト)と3番目(28~31バイト)を、
最後に第1引数の上位128ビットの0番目(16~19バイト)と1番目(20~23バイト)を取って、
それを結果の256ビットにまとめる。という処理になります。
Little-endianなので下位ビットが最初のバイトになります。

同様に、今度は1番目(4バイト目)からの256ビット長の値が欲しい場合は、
__m256 x01x02x03x04x05x06x07x08 =
    _mm256_shuffle_ps(
        x02x03x04x05x06x07x08x09,
        x00x01x02x03x04x05x06x07,
        _MM_SHUFFLE(2, 1, 2, 1) /* = 0x99 */);
となります。

最後に、4番目(16バイト目)からの256ビット長の値が欲しい場合は、
__m256 x04x05x06x07x08x09x10x11 =
    _mm256_permute2f128_ps(
        x08x09x10x11x12x13x14x15,
        x00x01x02x03x04x05x06x07,
        3);
となります。_mm256_permute2f128_psを使います。

ここら辺は、面倒臭いので呪文のように覚えるのが吉ですね。

0 件のコメント:

コメントを投稿