avx512
More than 3 years have passed since last update.

みなさんは、SSE/AVXプログラミング中に、こう思ったことはありませんか…?

「なんでこんなにシャッフル命令使い分けないといけないんだ…」

もう、そんなことで悩む時代は終わりました!これからは vpermt2b(_mm512_permutex2var_epi8) を使いましょう!

vpermt2b は zmm を2個くっつけた、128byte のデータを、バイト単位で、完全に任意に、実行時に作ったテーブルで並びかえられます!

脅威の128byte!インデックスの値が8bitしか持てないのが不安に感じられる大きさです!

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

unsigned char in0[128];
unsigned char out[64];
unsigned char table[64];

static void
test(void)
{
    register __m512i dest __asm__("zmm0") = _mm512_loadu_si512(in0);
    register __m512i src1 __asm__("zmm1") = _mm512_loadu_si512(table);
    register __m512i src2 __asm__("zmm2") = _mm512_loadu_si512(in0+64);

    __asm__ __volatile__ (".byte 0x62, 0xf2, 0x75, 0x48, 0x7d, 0xc2 # vpermt2b %%zmm2 %%zmm1, %%zmm0\n\t"
                          :[dest]"+v"(dest)
                          :[src1]"v"(src1),
                           [src2]"v"(src2));

    _mm512_storeu_si512(out, dest);

    int i,j;
    for (i=0; i<4; i++) {
        printf("%3d: ", i*16);

        for (j=0; j<16; j++) {
            printf("%3d ", out[i*16+j]);
        }

        puts("");
    }
}

int
main()
{
    int i;
    for (i=0; i<128; i++) {
        in0[i] = i + 10;
    }

    for (i=0; i<32; i++) {
        table[i*2+0] = i*2;
        table[i*2+1] = 110-i;
    }

    test();

    puts("--");

    for (i=0; i<64; i++) {
        table[i] = i*2;
    }

    test();

    puts("--");

    for (i=0; i<64; i++) {
        table[i] = i;
    }

    test();

}
 $ ./sde -cnl -- ./a.out
  0:  10 120  12 119  14 118  16 117  18 116  20 115  22 114  24 113
 16:  26 112  28 111  30 110  32 109  34 108  36 107  38 106  40 105
 32:  42 104  44 103  46 102  48 101  50 100  52  99  54  98  56  97
 48:  58  96  60  95  62  94  64  93  66  92  68  91  70  90  72  89
--
  0:  10  12  14  16  18  20  22  24  26  28  30  32  34  36  38  40
 16:  42  44  46  48  50  52  54  56  58  60  62  64  66  68  70  72
 32:  74  76  78  80  82  84  86  88  90  92  94  96  98 100 102 104
 48: 106 108 110 112 114 116 118 120 122 124 126 128 130 132 134 136
--
  0:  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25
 16:  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41
 32:  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57
 48:  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73

やったね!

    register __m512i dest __asm__("zmm0") = _mm512_loadu_si512(in0);
    register __m512i src1 __asm__("zmm1") = _mm512_loadu_si512(table);
    register __m512i src2 __asm__("zmm2") = _mm512_loadu_si512(in0+64);

    __asm__ __volatile__ (".byte 0x62, 0xf2, 0x75, 0x48, 0x7d, 0xc2 # vpermt2b %%zmm2 %%zmm1, %%zmm0\n\t"
                          :[dest]"+v"(dest)
                          :[src1]"v"(src1),
                           [src2]"v"(src2));

なんだこの不穏なコードは…?

vpermt2b は、AVX512VBMI というまた別の拡張になっていて、binutils も2.24では対応してませんでした。仕方なく git の binutils を取ってきて、それで出たバイト列を埋めてます。

このハイパー便利そうな vpermt2b ですが、一個だけ欠点があって、

 $ ./sde -knl -- ./a.out  
TID 0 SDE-ERROR: Executed instruction not valid for specified chip (KNL): 0x400688: vpermt2b zmm0, zmm1, zmm2
Image: /home/w0/test/avx512/a.out+0x688
Function: test
Instruction bytes are: 62 f2 75 48 7d c2 

 $ ./sde -skx -- ./a.out  
TID 0 SDE-ERROR: Executed instruction not valid for specified chip (SKYLAKE_SERVER): 0x400688: vpermt2b zmm0, zmm1, zmm2
Image: /home/w0/test/avx512/a.out+0x688
Function: test
Instruction bytes are: 62 f2 75 48 7d c2

Skylake でも KNL でも使えないのだった。(つまり使えない)

明日は、@tanakmura の推しイン(イチ推しインストラクション)、vpconflictd について書きます。