みなさんは、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 について書きます。