(未完成の回答。誰かに役立つ場合、または私がそれに戻った場合に備えて、とにかく投稿します。 一般に、ベクトル化できないスカラーとインターフェースする必要がある場合は、ベクトルをローカル配列に格納するだけでも悪くありません。その後、一度に 1 つの要素を再読み込みします。)
asmの詳細については、私の他の回答を参照してください。この回答は、C++ の側面に関するものです。
void foo(__m256 v) {
alignas(32) float vecbuf[8]; // 32-byte aligned array allows aligned store
// avoiding the risk of cache-line splits
_mm256_store_ps(vecbuf, v);
float v0 = _mm_cvtss_f32(_mm256_castps256_ps128(v)); // the bottom of the register
float v1 = vecbuf[1];
float v2 = vecbuf[2];
...
// or loop over vecbuf[i]
// if you do need all 8 elements one at a time, this is a good way
}
またはループオーバーvecbuf[i]
。ベクター ストアは、その要素の 1 つのスカラー リロードに転送できるため、約 6 サイクルのレイテンシしか発生せず、一度に複数のリロードを実行できます。(したがって、2/クロックのロード スループットを備えた最新の CPU のスループットには非常に適しています。)
低要素のリロードを回避したことに注意してください。レジスタ内のベクトルの下位要素はすでにスカラーfloat
です。 _mm_cvtss_f32( _mm256_castps256_ps128(v) )
コンパイラの型システムを満足させる方法です。ゼロの asm 命令にコンパイルされるため、文字通り無料です (最適化の失敗のバグを除けば)。(インテルの組み込みガイドを参照してください)。XMM レジスターは、対応する YMM レジスターの下位 128 ビットであり、スカラー float / double は、XMM レジスターの下位 32 ビットまたは 64 ビットです。(上半分のゴミは問題ありません。)
最初のキャストを 1 回行うと、OoO exec は、残りが到着するのを待っている間に何かを行うことができます。シャッフルして 128 と低い 128 で 2 番目の要素を取得することを検討することvunpckhps
もvmovhlps
できます。これにより、レイテンシ バブルを埋めるのに役立つ場合は、2 つの要素をすぐに準備できます。
GNU C/C++ では、配列のようなベクトル型にインデックスを付けv[1]
たり、 のような変数インデックスを付けたりすることができますv[i]
。コンパイラは、シャッフルまたはストア/リロードのいずれかを選択します。
__m256
しかし、これは、いくつかの名前付きメンバーとの共用体に関して定義する MSVC には移植できません。
配列への格納と再読み込みは移植可能であり、コンパイラはそれをシャッフルに最適化することさえできます。 (それが望ましくない場合は、生成された asm を確認してください。)
たとえば、clangvecbuf[1]
は単純な vshufps に戻るだけの関数を最適化します。 https://godbolt.org/z/tHJH_V
実際にベクトルのすべての要素を合計してスカラー合計にしたい場合は、シャッフルと SIMD add . x86 で水平フロート ベクトル合計を実行する最速の方法
(単一のベクトルの要素に対する乗算、最小、最大、またはその他の連想縮小についても同じです。もちろん、複数のベクトルがある場合は、のように 1 つのベクトルに垂直操作を行います_mm256_add_ps(v1,v2)
)
Agner Fog の Vector Class Libraryを使用すると、彼のラッパー クラスがオーバーロードoperator[]
され、定数ではない引数に対しても期待どおりに機能します。多くの場合、これはストア/リロードにコンパイルされますが、C++ でのコードの記述が容易になります。最適化を有効にすると、おそらくまともな結果が得られます。(ただし、低い要素は、その場で使用されるのではなく、格納/再ロードされる可能性があります。そのため、または何かに特殊なケースvec[0]
が必要になる場合があります。)_mm_cvtss_f32(vec)
(VCL は GPL の下でライセンスされていましたが、現在のバージョンは単純な Apache ライセンスです。)
一部の機能のより良いコードを生成するために、Agner の VCL に対するほとんどテストされていない変更を含む私のgithub リポジトリも参照してください。
_MM_EXTRACT_FLOAT
ラッパー マクロがありますが、これは奇妙で、SSE4.1 でしか定義されていません。extractps
SSE4.1 (浮動小数点のバイナリ表現を整数レジスタに抽出したり、メモリに保存したりできる)を使用することを意図していると思います。ただし、宛先が の場合、gcc はそれを FP シャッフルにコンパイルしfloat
ます。結果をとextractps
して欲しい場合、他のコンパイラがそれを実際の命令にコンパイルしないように注意してください。(これが.float
extractps
insertps
shufps
3 つの args: が必要なため、奇妙です。そのため、ローカル_MM_EXTRACT_FLOAT(dest, src_m128, idx)
の初期化子としても使用できません。float
ベクトルをループするには
gcc はそのようなループを展開しますが、それ以上の場合のみ-O1
です。で-O0
、エラー メッセージが表示されます。
float bad_hsum(__m128 & fv) {
float sum = 0;
for (int i=0 ; i<4 ; i++) {
float f;
_MM_EXTRACT_FLOAT(f, fv, i); // works only with -O1 or higher
sum += f;
}
return sum;
}