57

私のC++コードはSSEを使用していますが、AVXが利用可能になったときにサポートするようにSSEを改善したいと考えています。そこで、AVXが利用可能になるタイミングを検出し、AVXコマンドを使用する関数を呼び出します。Win7 SP1 +VS2010SP1とAVXを搭載したCPUを使用しています。

AVXを使用するには、次のものを含める必要があります。

#include "immintrin.h"

_mm256_mul_ps次に、などの組み込みAVX関数を使用できます_mm256_add_ps。問題は、デフォルトで、VS2010が非常に低速で動作し、警告を表示するコードを生成することです。

警告C4752:Intel(R)AdvancedVectorExtensionsが見つかりました。/ arch:AVXの使用を検討してください

VS2010は実際にはAVX命令を使用していないようですが、代わりにそれらをエミュレートします。/arch:AVXコンパイラオプションを追加して、良い結果を得ました。ただし、このオプションは、可能な場合はどこでもAVXコマンドを使用するようにコンパイラーに指示します。そのため、AVXをサポートしていないCPUでコードがクラッシュする可能性があります。

したがって、問題は、VS2010コンパイラを作成してAVXコードを生成する方法ですが、AVX組み込み関数を直接指定する場合に限ります。SSEの場合は、SSE組み込み関数を使用するだけで、のようなコンパイラオプションなしでSSEコードを生成します/arch:SSE。しかし、AVXの場合、何らかの理由で機能しません。

4

2 に答える 2

24

tl;dr 古いバージョンの MSVC のみ

AVX を使用するコードのセクションを使用_mm256_zeroupper();または_mm256_zeroall();前後に (関数の引数に応じて前後に)。/arch:AVXプロジェクト全体ではなく、AVX を含むソース ファイルに対してのみオプションを使用して、レガシー エンコードされた SSE のみのコード パスのサポートが中断されないようにします。

最新の MSVC (および他の主流のコンパイラである GCC/clang/ICC) では、コンパイラはasm 命令をいつ使用するかを認識しています。vzeroupper 組み込み関数で余分な s を強制vzeroupperすると、インライン化時にパフォーマンスが低下する可能性があります。2021 年に _mm256_zeroupper を使用する必要がありますか? を参照してください。

原因

Intel の記事「Avoiding AVX-SSE Transition Penalties」 ( PDF )が最も適切な説明だと思います。要約は次のように述べています。

ハードウェアが YMM レジスターの上位 128 ビットを保存および復元する必要があるため、プログラム内で 256 ビットのインテル® AVX 命令と従来のインテル® SSE 命令の間で移行すると、パフォーマンスが低下する可能性があります。

AVX命令またはアセンブリが (Intel から紙):

  • 128 ビット組み込み命令
  • SSE インライン アセンブリ
  • インテル® SSE にコンパイルされた C/C++ 浮動小数点コード
  • 上記のいずれかを含む関数またはライブラリの呼び出し

これは、SSE を使用して外部コードとリンクするときにペナルティが発生する可能性があることを意味します。

詳細

AVX 命令によって定義される 3 つのプロセッサ状態があり、そのうちの 1 つは、すべてのYMMレジスタが分割され、下半分がSSE 命令によって使用されることを可能にします。Intel のドキュメント「Intel® AVX State Transitions: Migrating SSE Code to AVX」には、これらの状態の図が示されています。

ここに画像の説明を入力

ステート B (AVX-256 モード) の場合、YMM レジスタのすべてのビットが使用されます。SSE 命令が呼び出されると、ステート C への遷移が発生する必要があり、ここでペナルティが発生します。SSE を開始する前に、すべての YMM レジスターの上半分を内部バッファーに保存する必要があります (それらがたまたまゼロであったとしても)。遷移のコストは、「Sandy Bridge ハードウェアで 50 ~ 80 クロック サイクルのオーダー」です。図 2 に示すように、C -> A からのペナルティもあります。

Mystical の回答で参照されているAgner Fog の最適化ガイド(2014-08-07 に更新されたバージョン)の 130 ページ、セクション 9.12、「 VEX モードと非 VEX モードのの遷移」で、この速度低下を引き起こしている状態切り替えペナルティの詳細を見つけることもできます。 . 彼のガイドによると、この状態への/からの遷移には「Sandy Bridge で約 70 クロック サイクル」かかります。Intel のドキュメントに記載されているように、これは回避可能な移行ペナルティです。

Skylake には、1 回限りのペナルティではなく、ダーティ アッパーを使用したレガシー SSE の誤った依存関係を引き起こす、別のダーティ アッパー メカニズムがあります。 Skylake で VZEROUPPER を使用しないと、この SSE コードが 6 倍遅くなるのはなぜですか?

解像度

移行ペナルティを回避するには、すべてのレガシー SSE コードを削除するか、すべての SSE 命令を 128 ビット命令の VEX エンコード形式に変換するようコンパイラに指示するか (コンパイラが対応している場合)、事前に YMM レジスタを既知のゼロ状態にすることができます。 AVX と SSE コードの間の移行。基本的に、個別の SSE コード パスを維持するには、 AVX 命令を使用するコードの後で、16 個の YMM レジスタすべての上位 128 ビットをゼロにする (VZEROUPPER命令を発行する) 必要があります。これらのビットを手動でゼロにすると、状態 A への移行が強制され、ハードウェアによって YMM 値を内部バッファーに格納する必要がないため、コストのかかるペナルティが回避されます。この命令を実行する組み込み関数は_mm256_zeroupper. この組み込みの説明は非常に有益です。

この組み込み関数は、インテル® アドバンスト・ベクトル・エクステンション (インテル® AVX) 命令と従来のインテル® サプリメンタル SIMD 拡張命令 (インテル® SSE) 命令の間で遷移するときに、YMM レジスターの上位ビットをクリアするのに役立ちます。インテル® アドバンスト・ベクトル・エクステンション (インテル® AVX) 命令と従来のインテル® の間で移行する前に、アプリケーションがこの組み込み関数に対応する命令を介してすべての YMM レジスターの上位ビットをクリア (「0」に設定) する場合、移行ペナルティはありません。VZEROUPPER補足 SIMD 拡張命令 (インテル® SSE) 命令。

Visual Studio 2010 以降 (おそらくそれより古いバージョン) では、immintrin.h でこの組み込み関数を取得します。

他の方法でビットをゼロにしても、ペナルティが解消されないことに注意してください。VZEROUPPERまたはVZEROALL命令を使用する必要があります。

インテル® コンパイラーによって実装される自動ソリューションの 1 つは、引数が YMM レジスターまたは // データ型でない場合はインテル® AVX コードを含む各関数の先頭に を挿入し、戻り値がVZEROUPPER YMMでない場合は関数の最後にレジスタまたは//データ型。__m256__m256d__m256i__m256__m256d__m256i

野生で

このVZEROUPPERソリューションは、SSE と AVX の両方をサポートするライブラリを生成するために FFTW で使用されます。simd-avx.hを参照してください。

/* Use VZEROUPPER to avoid the penalty of switching from AVX to SSE.
   See Intel Optimization Manual (April 2011, version 248966), Section
   11.3 */
#define VLEAVE _mm256_zeroupper

次に、AVX 命令の組み込み関数を使用して、すべてVLEAVE();の関数の最後に呼び出されます。

于 2015-02-06T01:04:53.570 に答える