🗂

AVX-512とAVX10とCPUID雑感

2024/12/31に公開

AVX-512雑感

AVX-512はIntelが2013年に発表した命令セットアーキテクチャーで、以下のような特徴を持ちます:

  • 512ビット幅のZMMレジスター
  • x86-64ではSIMDレジスターが32本に倍増(AVX2までは16本だった)
  • 条件分岐っぽいことをするのに便利なマスク
  • 埋め込み丸めと浮動小数点例外の抑制
  • 命令に埋め込めるブロードキャスト

AVX-512は命令セット拡張の総称で、個々の拡張は以下になります:

  • F (Foundation)
  • CD (Conflict Detection)
  • ER (Exponential and Reciprocal / Xeon Phi only)
  • PF (Prefetch / Xeon Phi only)
  • VL (Vector Length)
  • DQ (Doubleword and Quadword)
  • BW (Byte and Word)
  • IFMA
  • VBMI
  • VBMI2
  • VNNI
  • BITALG
  • VPOPCNTDQ
  • 4VNNIW (Xeon Phi only)
  • 4FMAPS (Xeon Phi only)
  • VP2INTERSECT
  • FP16
  • BF16

多いですね。

このセクションではAVX-512に対する素人的な雑感を書いていきます。

AVX-512は512ビット幅が基本

AVX-512は名前の通り512ビットが基本です。

AVX-512を構成する拡張では、512ビット(ZMM)のサポートが基本であり、128ビット(XMM)/256ビット(YMM)のサポートはオプションです。具体的には、CPUがVL拡張を実装していれば128ビット版・256ビット版の命令(例えば、XMM16-31やYMM16-31の使用)も使えますが、一般には128ビット版・256ビット版は仮定できないということです。

例えば、AVX-512 FP16に含まれる半精度加算命令 vaddsh/vaddph のバリエーションを見ると、

  • vaddph xmm, xmm, xmm: AVX-512 FP16だけで使える
  • vaddph xmm, xmm, xmm: AVX-512 FP16に加えてAVX-512 VLが必要
  • vaddph ymm, ymm, ymm: AVX-512 FP16に加えてAVX-512 VLが必要
  • vaddph zmm, zmm, zmm: AVX-512 FP16だけで使える

となります。

x86のSIMDレジスターの幅がSSEの128ビット→AVXの256ビット→AVX-512の512ビットと拡張されてきた経緯を考えると、AVX-512で追加された命令は常に128ビット、256ビットでも使えて欲しいものですが、AVX-512的にはそれはVLという「拡張」扱いなのです。

とはいえ、VL拡張を実装していないCPUはすでにディスコンとなったXeon Phiだけのようなので、今日のAVX-512をターゲットとするアプリケーションでは常にVL拡張が使えると仮定して良いでしょう(コンパイル時に -mavx512f -mavx512vl という風にして、VL拡張が使えないAVX-512対応CPUは考慮しない)。

「512ビットが基本」という方針の弊害として、何らかの事情で512ビット幅のレジスターと演算器を用意できないCPUではAVX-512が一切使えないということになります。例えばAVX-512 FP16で追加された半精度の命令が便利そうだと思っても、512ビット幅を提供できないCPUではそれは絶対に実装できないのです。

黒歴史となった拡張

AVX-512の一部の拡張は黒歴史となっています。

例えば、ER, PF, 4VNNIW, 4FMAPSはXeon Phiにしか搭載されませんでした。

そして、VP2INTERSECTは「他の命令でエミュレーションした方が速い」という論文が出てしまいました:[2112.06342] Faster-Than-Native Alternatives for x86 VP2INTERSECT Instructions

それが理由かは知りませんが、VP2INTERSECTはIntel的には「Tiger Lakeのみ実装」となっており、SDMでは「削除された機能」扱いとなっています。AMDはZen 5で実装したようですが。

私の感想

AVX-512は一部の人からボロクソ言われています:

Linus Torvalds: "I Hope AVX512 Dies A Painful Death" - Phoronix

が、浮動小数点数オタクの私としては、嫌いになりきれない面があります。

AVX-512からはIEEE 754-2008を意識している側面がところどころに感じられます。それは静的な丸め指定だったり、vrange 系の命令でIEEE 754-2008 minNum/maxNum演算ができたりすることだったりします。

なので、浮動小数点数オタクとしては、AVX-512が動くマシンを一台手元に置いておきたいです。そういうわけで私はIce Lake搭載のIntel MacBookを買ってみたり、AMD Zen 4搭載のミニPCを買ったりしています。

まあこれは趣味として触っている人の感想なので、真面目に高性能計算をやったりする人は別の感想を持つでしょう。

AVX10

AVX-512の欠点の一つは、「512ビットが基本」なことでした。なので、512ビットのサポートをオプションにしたものがAVX10となります。あと乱立するファミリーを統合して「バージョン」で扱えるようにします。

Intel® Advanced Vector Extensions 10.1 (Intel® AVX10.1) Architecture Specification

AVX10.1は、これまでに登場したAVX-512ファミリーから「黒歴史」つまりER, FP, 4VNNIW, 4FMAPS, VP2INTERSECTを除いたものと大体等価です。もちろん、512ビット(ZMM)のサポートはオプションとなります。

AVX-512とAVX10とCPUID

これまでの(廃止されたものを除く)AVX-512で導入された命令のCPUID欄は、「AVX-512またはAVX10.1を要求する」という風になります。例えば、半精度加算命令 vaddsh/vaddph のCPUID欄は

  • vaddsh xmm, xmm, xmm: AVX-512 FP16 OR AVX10.1/256
  • vaddph xmm, xmm, xmm: (AVX-512 FP16 AND AVX-512 VL) OR AVX10.1/256
  • vaddph ymm, ymm, ymm: (AVX-512 FP16 AND AVX-512 VL) OR AVX10.1/256
  • vaddph zmm, zmm, zmm: AVX-512 FP16 OR AVX10.1/512

となります。

CPUIDの条件に「または」が登場するのはこれまではあまりなかったと思います。例えば、コンパイラーの -m オプションで使える命令セットを複数指定するときは、プログラムがCPUに対して要求する命令セットは「かつ」(CPUに対する条件が厳しくなる)で組み合わされました。普通に -mavx512fp16 -mavx10.1 と指定してしまうと、「AVX-512 FP16」と「AVX10.1」の両方を要求するプログラムができてしまいます。

AVX10を前提にするプログラムをビルドする際はそれでもいいのですが、AVX-512のみに対応するCPUは今の時点ではそれなりに存在するので、移行期には「AVX-512またはAVX10に対応するCPUで動くようなプログラム」をビルドできる必要があります。コンパイラーにはどのように指示したら良いでしょうか?

GCCやClangでは、AVX-512のコンパイルオプションに加えて -mno-evex512 を指定すると、「AVX-512の機能を使うが、幅は256ビットに制限する」という風になるようです。否定を使う必要があるのがややイレギュラー感があります。

では、何らかの事情で「肯定的なフラグ」「ANDによる組み合わせ」しか使えない場合はどうなるでしょうか?

そういう場合は、仮想的なCPUIDフラグを導入することが考えられます。例えば、

AVX512_FP16_SCALAR := AVX512_FP16 OR AVX10.1/256
AVX512_FP16_VL256 := (AVX512_FP16 AND AVX512VL) OR AVX10.1/256
AVX512_FP16_VL512 := AVX512_FP16 OR AVX10.1/512

と定義すれば、

  • vaddsh xmm, xmm, xmm: AVX512_FP16_SCALAR
  • vaddph xmm, xmm, xmm: AVX512_FP16_VL256
  • vaddph ymm, ymm, ymm: AVX512_FP16_VL256
  • vaddph zmm, zmm, zmm: AVX512_FP16_VL512

となります。

このように導入できる仮想的なCPUIDフラグは以下のようになります:

AVX512_BF16_VL256
AVX512_BF16_VL512
AVX512_BITALG_VL256
AVX512_BITALG_VL512
AVX512_FP16_SCALAR
AVX512_FP16_VL256
AVX512_FP16_VL512
AVX512_IFMA_VL256
AVX512_IFMA_VL512
AVX512_VBMI_VL256
AVX512_VBMI_VL512
AVX512_VBMI2_VL256
AVX512_VBMI2_VL512
AVX512_VNNI_VL256
AVX512_VNNI_VL512
AVX512_VPOPCNTDQ_VL256
AVX512_VPOPCNTDQ_VL512
AVX512BW_MASK
AVX512BW_VL256
AVX512BW_VL512
AVX512CD_VL256
AVX512CD_VL512
AVX512DQ_SCALAR
AVX512DQ_VL256
AVX512DQ_VL512
AVX512F_SCALAR
AVX512F_VL256
AVX512F_VL512

AVX10(特に、新命令が追加される10.2)が楽しみですね。SDEを触りながら待ちましょう。

Discussion