借り初めのひみつきち

仮ブログです。

Intel APX

面白そうな拡張が発表されたようです。

www.intel.com

Intel APX は一言で言うとx86汎用命令のAVX化です*1

AVX では新しいプレフィックスを追加することで今までの SSE 命令ではエンコーディングできない機能が追加されました。 APX もこれに似ていて、今までの x86 命令に AVX によく似たプレフィックスを追加することで、新しい機能が追加されます。

他にもいくつかの拡張がありますが、主要な機能は以下の3つあります。

汎用レジスタの倍増 (16 + 16 = 32)

レジスタというのはCPU内部にあってコアとほぼ同じ速度で動作する超高速で小容量のメモリで、大きく分けるとCPUの動作を制御するシステムレジスタと、アプリが自由にデータを格納するのに使える汎用レジスタに大別できます。

汎用レジスタは汎用という名前が付いていますが、実際には一部のレジスタは使い道が決められています。 残りの自由に使えるレジスタコンパイラがローカル変数の一部を割り当てます。 この使い道は ABI というルールで決められていることが多いです。

レジスタは個数が限られているので、関数がある程度の規模を超えるとレジスタに割り当てできないローカル変数が出てきます。 その場合、使用頻度の低い変数はメモリに確保されるので、レジスタを使う場合よりも遅いコードになります。

使えるレジスタの個数が増えると、ローカル変数に割り当てできるレジスタが増えるので、メモリに割り当てるよりも効率的なコードになります。

もちろんこれには欠点もあり、現代の一般的な x86 CPU の環境ではマルチタスクで使われることが多いため、レジスタの数が増えると当然コンテキストスイッチで退避するレジスタの数も増えるので、コンテキストスイッチのコストが少し増えます。

3オペランド形式: NDD (new data destination)

長らく x86 命令はほとんどの命令が2オペランド形式でした。

これは例えば、 ADD A, B という x86 命令は A += B を意味しますが、ここで A に指定したレジスタは必ず破壊されるので、元の A の値を他の処理で使いたい場合は別途他の場所に退避しておく必要があります。

これが3オペランド形式になると、 ADD A, B, C という命令で A = B + C とすることができるので、 B と C の内容はそのままで計算結果を別の場所に書き出せるようになります。*2

フラグの更新抑制: NF (status flags update suppression, hence “no flags”)

これは地味に強力な機能で、今まで x86 の演算命令は必ずフラグが更新されていましたが、抑制できるようになります。

CPUは演算によって結果が 0 になったかどうか、桁溢れが発生したかどうかなどを毎回チェックしてフラグレジスタの内容を更新しますが、 実際のところ演算後のフラグの変化が重要なケースは多倍長演算や条件分岐など一部のケースに限られ、ほとんどの処理では不要な更新が毎回発生していることになります。

不要なフラグ更新はフラグレジスタに対する依存関係も若干発生するので、アウトオブオーダーで命令を並び替えるときに制約になり得ますが、 不要なフラグ更新を抑制することでアウトオブオーダーがより効率的に動作することが期待できます。

x86RISC

実はこれらは一般的な RISC チップには最初からある機能が多いです。

x86RISC 化が進行してると見ることができますが、結構な量のエンコーディングルールが追加されるので RISC には程遠いような複雑な感じです。

CISC 万歳?

*1:厳密には少し違います!

*2:実際は加算は LEA で代用できる場合が多いので恩恵があるのは他の演算ですが