Ice Lake の PC (mac) を手に入れたので vfpbench を AVX512 対応にしてみました。結果は下記のとおりです。
AVX512 | reg | GFLOPS | fop | IPC | |
---|---|---|---|---|---|
AVX512VL | vmulps | ymm 256bit | 55.2 | 8 | 6.3 |
AVX512VL | vaddps | ymm 256bit | 55.6 | 8 | 6.3 |
AVX512VL | vfmaddps | ymm 256bit | 111.3 | 16 | 6.3 |
AVX512F | vmulps | zmm 512bit | 53.7 | 16 | 3.1 |
AVX512F | vaddps | zmm 512bit | 54.0 | 16 | 3.1 |
AVX512F | vfmaddps | zmm 512bit | 108.0 | 32 | 3.1 |
AVX512F | vfmadd+mulps | zmm 512bit | 81.0 | 24 | 3.1 |
AVX512F | vfmadd+addps | zmm 512bit | 81.2 | 24 | 3.1 |
・Core i5-1030NG7 (MacBook Air)
AVX512 は、512bit 単位の演算が可能となる Intel の新しい SIMD 命令セットです。AVX/AVX2 は 256bit 幅なので 2倍に増えたことになります。単精度の浮動小数点演算なら 512/32bit = 16並列です。4×4 matrix が 1レジスタに収まります。
SSE から AVX に進化したときと同じように、命令のエンコードも一新されており機能も増えています。SSE → AVX では 3オペランドになり 64bit 時に 16個のレジスタが利用できました。AVX → AVX512 ではレジスタフィールドが 5bit となり、レジスタ数が 32個に増えています。さらに 7個の mask レジスタを併用することができます。
mask レジスタは初期の GPU の Shader にあった書き込みマスクと同じものです。出力レジスタのうち必要な要素のみ置き換えることができます。残りの部分は元の値が残りますが、保存せずにゼロクリアを行うこともできます。
mask レジスタが導入されたことで、大きくて一見小回りがきかないようにみえる 512bit のレジスタも、任意のベクタ長とみなして扱うことができます。単精度なら 16個分ですが、mask を併用すれば 1~15 個の単位でも読み書きができるわけです。
SSE/AVX では少々扱いづらかった x,y,z の 3要素ベクタも簡単にロードすることができます。 下記の例ではベクタ (x,y,z) を 4個まとめて読み込んでいます。長さ 12 のベクタとして読み込んだあと、それぞれ (x,y,z) → (x,y,z,0) に展開しています。
movl $0x0fff, %eax kmovw %eax, %k1 movl $0x7777, %eax kmovw %eax, %k2 movups data(%rbp), %zmm0{%k1}{z} ; mask 0xfff で読み込み vexpandps %zmm0, %zmm1{%k2}{z} ; mask 0xfff -> 0x7777 に展開
AVX2 でも gather を使えば似たようなことができますが、どちらかといえば gather 命令は Shader の InputAssembler に相当します。
もちろん常時マスク付きで演算を行うと無駄が生じていることになります。GPU の SIMT のように、SoA で扱う方が AVX512 の本来の形かもしれません。この場合レジスタはベクタではなく 16個(単精度の場合)のスカラーとなり、mask レジスタは 16個のフラグレジスタとみなせます。
float d= n.dot( l );
if( d < 0 ){
c+= a;
}else{
c+= b * d;
}
例えば↑こんな感じのコードを 16並列で実行すると↓こうなります。
vmulps %zmm8, %zmm11, %zmm20 vfmadd231ps %zmm9, %zmm12, %zmm20 vfmadd231ps %zmm10, %zmm13, %zmm20 vcmpps $1, %zmm20, %zmm18, %k1 knotw %k1, %k2 vfmadd231ps %zmm20, %zmm19, %zmm21{%k1} vaddps %zmm17, %zmm21, %zmm21{%k2}
比較命令の結果であるフラグ値は mask レジスタに入るので、条件成立時と不成立時の演算結果をそのまま合成することができます。
AVX512 の説明が少々長くなりましたが、IceLake の vfpbench の結果を見てみます。ピークの GFLOPS 値は AVX(FMA3) 命令でも AVX512 命令でも変わっていないことがわかります。Ice Lake の場合 zmm (512bit) の AVX512F 命令は同時に 1命令しか実行できないようです。
AVX | reg | GFLOPS | fop | IPC | |
---|---|---|---|---|---|
FMA3 | vfmaddps | ymm 256bit | 111.0 | 16 | 6.3 |
AVX512VL | vfmaddps | ymm 256bit | 111.3 | 16 | 6.3 |
AVX512F | vfmaddps | zmm 512bit | 108.0 | 32 | 3.1 |
この結果は Intel のサイトでも確認できます。
上記ページの「__m512 _mm512_fmadd_ps (__m512 a, __m512 b, __m512 c)」を見ると、Icelake の throughput は 1 なので実行に 1 cycle かかることがわかります。対して Skylake (server)/Knights Landing の方は 0.5 なので、2 命令実行できることを意味しています。
また同じ AVX512 の命令でも、mask 付きの ymm(256bit) は AVX/FMA 同様 2命令実行できています。Intrinsics Guide で確認してみると throughput は 0.5 なので合っているようです。
よって IceLake の場合は、性能を上げるために無理に AVX512 命令を使う必要は無さそうです。ただし最初に紹介したように、AVX512 ではレジスタが倍増し便利な機能も命令も増えています。mask が使える便利な AVX2 として見ても十分使い物になるのではないでしょうか。
反面 CPU によって対応機能が細かく別れてしまうので、最適化と互換性の両立はますます難しくなりそうです。
なお vfpbench の log で IPC に大きな数値が出ているのは CPU のベースクロックを元にしているためです。今回使用した Core i5-1030NG7 はベースが 1.1GHz で Single Thread の Boost 時に 3.5GHz になります。そのため 3.5/1.1 の 3.18 がおよそ IPC=1 と思ってください。
より詳細なログは下記からどうぞ
・Hyperでんち: VFP Benchmark Log 計測結果まとめ
関連エントリ
・4倍速い Ryzen 9 3950X の UE4 コンパイル速度
・Snapdragon 845 ARMv8.2A 半精度 fp16 演算命令を使ってみる / Deep Learning 命令
・Snapdragon 835 と 845 のコンパイル時間の比較&浮動小数点演算能力
・Snapdragon 845 の浮動小数点演算速度
・ARM CPU の浮動小数点演算能力まとめ
・HTC 10 Snapdragon 820 Kyro の浮動小数点演算能力
・iPhone SE, Apple A9 の浮動小数点演算速度
・ARM Cortex-A53 の浮動小数点演算速度とコンパイル時間の比較
・iPod touch 6 の浮動小数点演算速度は Core 2 Duo ライン超え
・iPad Air 2 (Apple A8X) の浮動小数点演算能力
・ARM cpu vfp の種類と fp16 命令を使ってみる
・Intel AVX その3 命令
・Intel AVX その2 転送
・Intel AVX