sse組み込み関数を使用してfloatベクトルの合計要素(削減)を取得するにはどうすればよいですか?浮動小数点のSSE削減
シンプルなシリアルコード:通常
void(float *input, float &result, unsigned int NumElems)
{
result = 0;
for(auto i=0; i<NumElems; ++i)
result += input[i];
}
sse組み込み関数を使用してfloatベクトルの合計要素(削減)を取得するにはどうすればよいですか?浮動小数点のSSE削減
シンプルなシリアルコード:通常
void(float *input, float &result, unsigned int NumElems)
{
result = 0;
for(auto i=0; i<NumElems; ++i)
result += input[i];
}
あなたは、ループ内の4人の部分和を生成して、ちょうどループの後、例えば4つの要素を横切って水平合計します
#include <cassert>
#include <cstdint>
#include <emmintrin.h>
float vsum(const float *a, int n)
{
float sum;
__m128 vsum = _mm_set1_ps(0.0f);
assert((n & 3) == 0);
assert(((uintptr_t)a & 15) == 0);
for (int i = 0; i < n; i += 4)
{
__m128 v = _mm_load_ps(&a[i]);
vsum = _mm_add_ps(vsum, v);
}
vsum = _mm_hadd_ps(vsum, vsum);
vsum = _mm_hadd_ps(vsum, vsum);
_mm_store_ss(&sum, vsum);
return sum;
}
注:上記の例a
ための16バイトが整列しなければならず、a
の位置合わせは、次に_mm_loadu_ps
代わり_mm_load_ps
の使用は保証できない場合n
は4の倍数でなければなりません。 n
が4の倍数であることが保証されていない場合は、残りの要素を累積するために関数の最後にスカラーループを追加します。
入力配列が潜在的に大きい場合は、入力がSSEループの16B境界に位置合わせされるまで、開始時に0〜3回実行されるスカラーループを持つ価値があります。そうすれば、キャッシュ/ページ行をクロスオーバーする負荷がループを減速させることはありません。また、メモリ・オペランドを伴う 'ADDPS'を使用することができます。これは潜在的にマイクロヒューズを起こし、オーバーヘッドを減らします。また、複数のアキュムレータを使用することで、2つまたは4つの依存関係チェインを得ることができるため、1ループあたり1つのベクトルFP加算(1回のADDPS = 3の待ち時間)ではなく、 –
何か試しましたか? – harold
実際に生成されたコードを見ましたか?少なくともgccの経験は、SSE命令を実行する際にはかなり良い仕事ですが、-O3が必要な場合があります。 –