avx512
AVX-512Day 3

マスク付きvpandd

More than 3 years have passed since last update.

SSE, AVXでは、_mm_and_si128(pand), _mm256_and_si256(vpand) のように、全bitの論理積を取る命令しかなく、不便な場合がたくさんありましたが、AVX512になって、これが、_mm512_and_epi32(vpandd) と _mm512_and_epi64(vpandq) に分かれ、32bit内の各bitの論理積、64bit内の各bitの論理積を取れるようになり、大変便利になりました。

そんなわけが無いのだった。(bit ごとの演算なので 32bit, 64bit の区別が無い)

という話に関連する話を書きます。

AVX512 では、多くの命令に16bitマスクレジスタを入力とするマスクが付けられます。マスクレジスタの各bitはベクタレジスタの各要素と対応していて、_mm512_mask_and_epi32(vpandd) と、_mm512_mask_and_epi64(vpandq) では、このマスクの扱いが変わります。

_mm512_mask_and_epi32 では、16bitのマスクレジスタ値が、32bit 16要素の各要素と対応します。_mm512_mask_and_epi64 では、マスクレジスタの下位8bitが、64bit 8要素と対応します。

__m512i _mm512_mask_and_epi32 (__m512i src, __mmask16 k, __m512i v2, __m512i v3)

とかいうようになっていて、k のビットが立ってる場合は、v2 & v3 の要素の論理積とった値を、k のビットが立ってない場合は、src の要素を取得できます。

これだと、3入力になるけどレジスタポート大丈夫か心配ですが、どうせvfmaddとかが3入力だから問題無いですね。ただこれの影響を受けてマスク付きvfmaddはオペランド一個が、出力 + 入力 or マスク入力の役割を背負う素敵命令となっています。

_mmask16, _mmask8 型変数は…KNC では存在した _mm512_int2mask が無くなってるしそのままスカラ値入れればいいんではないかと思います(調べたけどわからんかった(手抜き))

#include <immintrin.h>
#include <stdio.h>

int in0[16] = {~0,~0,~0,~0,
               ~0,~0,~0,~0,
               ~0,~0,~0,~0,
               ~0,~0,~0,~0};

int and_bits[16] = {0xaaaaaaaa,
                    0x55555555,
                    0xaaaaaaaa,
                    0x55555555,
                    0xaaaaaaaa,
                    0x55555555,
                    0xaaaaaaaa,
                    0x55555555,
                    0xaaaaaaaa,
                    0x55555555,
                    0xaaaaaaaa,
                    0x55555555,
                    0xaaaaaaaa,
                    0x55555555,
                    0xaaaaaaaa,
                    0x55555555};

int out_epi32[16];
int out_epi64[16];

int
main(void)
{
    __m512i a;
    __m512i b;
    __m512i c;
    int i;

    __mmask16 mask_16 = 0x8181U;
    __mmask8 mask_8 = 0x81U;

    a = _mm512_loadu_si512(in0);
    b = _mm512_loadu_si512(and_bits);

    c = _mm512_mask_and_epi32(a, mask_16, a, b);
    _mm512_storeu_si512(out_epi32, c);

    for (i=0; i<16; i++) {
        printf("epi32 %2d:%08x\n", i, out_epi32[i]);
    }

    puts("");

    c = _mm512_mask_and_epi64(a, mask_8, a, b);
    _mm512_storeu_si512(out_epi64, c);
    for (i=0; i<16; i++) {
        printf("epi64 %2d:%08x\n", i, out_epi64[i]);
    }

}
 $ gcc -mavx512f vpand.cpp
 $ sde -skx -- ./a.out 
epi32  0:aaaaaaaa
epi32  1:ffffffff
epi32  2:ffffffff
epi32  3:ffffffff
epi32  4:ffffffff
epi32  5:ffffffff
epi32  6:ffffffff
epi32  7:55555555
epi32  8:aaaaaaaa
epi32  9:ffffffff
epi32 10:ffffffff
epi32 11:ffffffff
epi32 12:ffffffff
epi32 13:ffffffff
epi32 14:ffffffff
epi32 15:55555555

epi64  0:aaaaaaaa
epi64  1:55555555
epi64  2:ffffffff
epi64  3:ffffffff
epi64  4:ffffffff
epi64  5:ffffffff
epi64  6:ffffffff
epi64  7:ffffffff
epi64  8:ffffffff
epi64  9:ffffffff
epi64 10:ffffffff
epi64 11:ffffffff
epi64 12:ffffffff
epi64 13:ffffffff
epi64 14:aaaaaaaa
epi64 15:55555555

_mm512_mask_and_epi32 と _mm512_mask_and_epi64 では挙動が違っているのが確認できます

(は?入力マスク値変えてんだからこれでは命令の挙動が変わったことを確認できないだろ???)

明日、明後日は @tanakmura が昨日今日の内容をコピペして vpord の話を書いて二日分稼ぎます。(やる気が出なかった場合)

\   Hello!   /
〜0 〜0 〜0