2015-11-01 6 views
6

残念ながら私はAVX命令に問題があるように思われるAMDのの群衆追いのCPU、持っている:代わり

メモリは256ビットのAVXレジスタに書き込みが非常に遅いです。測定されたスループットは、以前のモデル(Bulldozer)よりも5〜6倍遅く、2回の128ビット書き込みよりも8〜9倍遅くなっています。

私の経験上、mm256の組み込み関数がmm128よりもはるかに遅いことがわかりました。私はそれが上記の理由によるものだと仮定しています。

私は本当に妥当な速度でマシン上のビルドをテストすることができますが、最新の命令セットAVXを実際にコーディングしたいと思います。 mm256組み込み関数にSSE命令を代わりに使用させる方法はありますか?私はVS 2015を使用しています。

簡単な方法がない場合、難しい方法はありますか? <immintrin.h>を、SSEを使用するようにコーディングできる組み込み関数の独自の定義を含むカスタムメイドのヘッダーに置き換えますか?それがどれほど妥当であるかわからない、私がその作業を行う前に、できるだけ簡単な方法を好む。

+0

あるとは思いません。特定のプロセッサーのためにコンパイラーを見直すつもりはありません。 (Piledriverだけがそのバグを持っています。) – Mysticial

+0

あなたが何かを引用するときに参考にしてください。そして、はい、これには解決策があります。 Agner Fogのベクタークラス。 'Vec8f'のようなAVXベクトルを使い、' -D__SSE4_2__ -D__XOP__'でコンパイルしてください。 –

+0

256ビットを使用していますが実際には遅いですか?アラインメントの問題がありますか? –

答えて

6

Agner FogさんのVector Class Libraryを使用して、これをVisual Studioのコマンドラインに追加してください:-D__SSE4_2__ -D__XOP__

次に、Vec8fなどのAVXサイズのベクトルを8つの浮動小数点に使用します。 AVXを有効にしないでコンパイルすると、vectorf256e.hというファイルが使用され、2つのSSEレジスタを持つAVXをエミュレートします。たとえば、次のように開始しVec256feからVec8f継承:

class Vec256fe { 
protected: 
    __m128 y0;       // low half 
    __m128 y1;       // high half 

あなたは/arch:AVX -D__XOP__でコンパイルする場合はVCLではなく、ファイルvectorf256.hと1つのAVXレジスタを使用します。コンパイラスイッチの変更だけで、あなたのコードはAVXとSSEで動作します。

XOPを使用しない場合は、-D__XOP__を使用しないでください。


ピーター・コルドは、あなたがあなたの目標は、唯一の256ビットのロード/ストアを避けるためであれば、それは、これが以外で違いを生むだろうはっきりしていないのに、あなたはまだ(VEXエンコードされた命令をすることができ、彼の答えで指摘したようにいくつかの特別なケース)。その後、/arch:AVX -D__XOP__でコンパイルし、この

Vec8f a; 
Vec4f lo = a.get_low(); // a is a Vec8f type 
Vec4f hi = a.get_high(); 
lo.store(&b[0]);   // b is a float array 
hi.store(&b[4]); 

のようなベクトルクラスでそれを行うことができます。

別のオプションはVecnfを使用して一つのソースファイルで、その後

//foo.cpp 
#include "vectorclass.h" 
#if SIMDWIDTH == 4 
typedef Vec4f Vecnf; 
#else 
typedef Vec8f Vecnf; 
#endif 

を行うと、これは一つのソースファイルと3つの実行可能ファイルを作成することになり、この

cl /O2 /DSIMDWIDTH=4      foo.cpp /Fofoo_sse 
cl /O2 /DSIMDWIDTH=4 /arch:AVX /D__XOP__ foo.cpp /Fofoo_avx128 
cl /O2 /DSIMDWIDTH=8 /arch:AVX   foo.cpp /Fofoo_avx256 

のようにコンパイルされるだろう。それらをリンクするのではなく、/cでそれらをコンパイルし、CPUディスパッチャを作成するだけです。 AMD以外でavx128を使用する理由がないと思ったので、XOPをavx128と使用しました。

+0

私はどうすればその反対をするのだろうかと思います。ベクタークラスライブラリで '__m256'を' Vec8fe'にプッシュします。そうです、それは理にかなっていませんが、私はこの場合が必要です。 – Royi

+0

@Royiiなぜこのケースが必要ですか? __m256がある場合は、AVXを有効にしてコンパイルしたことを意味し、VCLはVec8fではなくVec8feを使用します。 –

+0

私のシステムでは2つの異なるコードが必要な場合があるためです。 1つはSSE、もう1つはAVXです。 VCLの問題は、それらのうちの1つのみを処​​理することです。 「Vec8f」とSSEと「Vec4f」を使ってAVXを使用するように強制することができたらいいですね。 – Royi

3

SSE命令は使用しないでください。256bのストアを2つの独立した128bストアとして実行し、まだVEXでコード化された128b命令を実行することが望ましいです。すなわち128b AVX vmovupsである。


gccが-mavx256-split-unaligned-load...-storeオプション(ブルドーザー、家族のために、おそらくまた、例えば-march=sandybridgeの一部として有効にする(-march=bdver2がの群衆追いで)持っています。コンパイラは、メモリがあることを知っているとき、問題を解決していませんしかし、整列。


あなたは

// maybe enable this for all BD family CPUs? 

#if defined(__bdver2) | defined(PILEDRIVER) | defined(SPLIT_256b_STORES) 
    #define _mm256_storeu_ps(addr, data) do{ \ 
     _mm_storeu_ps(((float*)(addr)) + 0, _mm256_extractf128_ps((data),0)); \ 
     _mm_storeu_ps(((float*)(addr)) + 4, _mm256_extractf128_ps((data),1)); \ 
    }while(0) 
#endif 
のようなマクロを使って固有の通常256Bストアをオーバーライドすることができます

gccは、Piledriver(-march=bdver2)に__bdver2(Bulldozerバージョン2)を定義しています。

_mm256_store_psの場合は同じ方法を使用するか、アライメントされていない組み込み関数を使用してください。

コンパイラは、_mm256_extractf128(data,0)を単純なキャストに最適化します。私。それだけ、しかし

vmovups  [rdi], xmm0   ; if data is in xmm0 and addr is in rdi 
vextractf128 [rdi+16], xmm0, 1 

にコンパイルtesting on godbolt shows that gcc and clang are dumb、およびレジスタと、その後ストアに抽出しなければなりません。 ICCは2命令シーケンスを正しく生成します。

+0

AMD AVXは基本的にSSEとしてハードウェアでエミュレートされるため、非VEXエンコード命令を使用すると何が問題になりますか?私が考えることができる唯一の利点は、AVX命令を使用するが、ロード/ストアを分割することは、命令キャッシュ内のレジスタ数を減らし、命令を少なくすることである。 –

+0

VEXでエンコードされた命令を使用する理由の1つである非VEXエンコード命令では、アライメントされていないロードをフォールディングできないためです。 –

+0

@Zboson:ええと、私が読んだこと(Agner Fogなど)によると、AMDでは256bベクタを使用する利点はほとんどありません。通常、VEXコード化された命令を持つ128bベクトルが最良の賭けです。この回答は、開発のためにPiledriverマシンを使用してAVXソフトウェアを開発/デバッグするのに役立ちます。 256bストアのパフォーマンスのバグを踏まずに256bの組み込み関数を使用することができます。したがって、 '' _mm_'' 128bの組み込み関数を使用するコードを記述していれば、Piledriverとほぼ同等の速度が得られますが、インテルHWではより高速に動作するはずです。 –