現在、uint_32 値の大きな配列に対して単純な「最大関数」ループ スキャンを実行しようとしています。
AVX2組み込みを使用すると、かなり簡単です:
const __m256i limit8 = _mm256_set1_epi32(limit);
for (i=0; i<TABLESIZE; i+=8)
{
__m256i src = _mm256_loadu_si256((const __m256i*)(h+i));
src = _mm256_max_epu32(src, limit8);
_mm256_storeu_si256((__m256i*)(h+i), src);
}
唯一の重要な操作は、要求された作業を効率的に実行する _mm256_max_epu32 (vpmaxud) です。テーブル内のすべてのセルが単一の定数と比較されます。
さて、組み込みの使用は、移植性の点で少し制限的であり、コンパイラーが自動的にベクトル化する標準 C を使用して同等のバージョンを作成することをお勧めします。結局のところ、内側のループは安価なヒューリスティックで見つけられるほど単純に見えます。
残念ながら、自動ベクトル化に関する VS2012 のメモには、この関数を正しく検出する必要があると明確に記載されていますが、この単純な演習に失敗しています。
私が試したこと:
for (i=0; i<TABLESIZE; ++i)
{
if (h[i]>limit) h[i]=limit;
}
動作しません: クックブック ステートメントとは対照的に、ここでは "if" ステートメントが問題です: 自動ベクトル化はコード 1100 で失敗します
for (i=0; i<TABLESIZE; ++i)
{
h[i] = h[i] > limit ? h[i] : limit;
}
別の理由ではありますが、自動ベクトル化はコード 1304 で失敗します(ループには異なるサイズの代入が含まれます)。これは、すべての変数が同じ型を使用しているため、バグである可能性があります。
for (i=0; i<TABLESIZE; ++i)
{
const U32 val = ((limit-h[i]) >> 31);
h[i]-=limit; h[i]*=val; h[i]+=limit;
}
これは機能し、ベクトル化されています。しかし、それはより複雑であり、実行は直接組み込みバージョンよりも著しく遅くなります。
この単純な「最大」操作を Visual (GCC と Clang に従う) によって自動的にベクトル化する方法があるかどうか疑問に思っています。