このような組合を持てますか
union eight_floats_t
{
__m256 a;
__m128 b[2];
};
eight_floats_t eight_floats;
256 ビット レジスタの 2 つの 128 ビット部分に瞬時にアクセスするには?
編集:このアプローチのパフォーマンスへの影響を理解するよう求めていました。
このような組合を持てますか
union eight_floats_t
{
__m256 a;
__m128 b[2];
};
eight_floats_t eight_floats;
256 ビット レジスタの 2 つの 128 ビット部分に瞬時にアクセスするには?
編集:このアプローチのパフォーマンスへの影響を理解するよう求めていました。
あなたは確かにそれを行うことができます。C および C++ 言語では、それが可能です。そして、それはおそらくあなたがしたいことをします。
ただし、AVX を使用しているということは、パフォーマンスを重視するということです。そのため、これが SSE プログラマーが陥る最も一般的な (パフォーマンス) トラップの 1 つであることを知っておくと役立ちます。(そして多くの人は気づいていません)
問題 1:
現在のコンパイラは、メモリ位置を使用してそのような共用体を実装しています。これが最初の問題です。別のフィールドからユニオンにアクセスするたびに、データが強制的にメモリに読み込まれ、読み戻されます。それは1つのスローダウンです。
MSVC2010 が生成するものは次のとおりです (最適化あり)。
eight_floats a;
a.a = vecA[0];
__m128 fvecA = a.b[0];
__m128 fvecB = a.b[1];
fvecA = _mm_add_ps(fvecA,fvecB);
vmovaps YMMWORD PTR a$[rbp], ymm0
movaps xmm1, XMMWORD PTR a$[rbp+16]
addps xmm1, XMMWORD PTR a$[rbp]
movaps XMMWORD PTR fvecA$[rbp], xmm1
movss xmm1, DWORD PTR fvecA$[rbp]
メモリにフラッシュされていることがわかります。
問題 2:
2 番目のスローダウンはさらに悪化します。何かをメモリに書き込み、すぐに別のワード サイズでアクセスすると、ストアからロードへのストールが発生する可能性があります。(通常は 10 サイクル以上)
これは、現在のプロセッサのロード/ストア キューが通常、この (異常な) 状況を処理するように設計されていないためです。そのため、キューをメモリにフラッシュするだけで対処します。
AVX データ型の下半分と上半分にアクセスする「正しい」方法は、次を使用することです。
_mm256_extractf128_ps()
_mm256_insertf128_ps()
_mm256_castps256_ps128()
と家族。他のデータ型についても同様です。
とは言っても、コンパイラがあなたが何をしているのかを認識し、とにかくそれらの命令を使用するのに十分賢いかもしれません。(少なくとも MSVC2010 はそうではありません。)
はい、できます。試してみましたか?
C標準では、最後に書き込まれたものではない共用体のメンバーにアクセスすることは未規定の動作であると述べていることに注意してください-具体的には、あるメンバーに書き込み、次に別のメンバーを読み取ると、他のメンバーは未規定値 (C99 §6.2.6.1/7)。ただし、これは非常に一般的なイディオムであり、すべての主要なコンパイラで十分にサポートされています。実際問題として、ユニオンの任意のメンバーに対して、任意の順序で読み取りと書き込みを行うことは、許容される慣行です ( source )。