2013-07-20 9 views
8

sse組み込み関数を使用してfloatベクトルの合計要素(削減)を取得するにはどうすればよいですか?浮動小数点のSSE削減

シンプルなシリアルコード:通常

void(float *input, float &result, unsigned int NumElems) 
{ 
    result = 0; 
    for(auto i=0; i<NumElems; ++i) 
     result += input[i]; 
} 
+3

何か試しましたか? – harold

+2

実際に生成されたコードを見ましたか?少なくともgccの経験は、SSE命令を実行する際にはかなり良い仕事ですが、-O3が必要な場合があります。 –

答えて

14

あなたは、ループ内の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の倍数であることが保証されていない場合は、残りの要素を累積するために関数の最後にスカラーループを追加します。

+1

入力配列が潜在的に大きい場合は、入力がSSEループの16B境界に位置合わせされるまで、開始時に0〜3回実行されるスカラーループを持つ価値があります。そうすれば、キャッシュ/ページ行をクロスオーバーする負荷がループを減速させることはありません。また、メモリ・オペランドを伴う 'ADDPS'を使用することができます。これは潜在的にマイクロヒューズを起こし、オーバーヘッドを減らします。また、複数のアキュムレータを使用することで、2つまたは4つの依存関係チェインを得ることができるため、1ループあたり1つのベクトルFP加算(1回のADDPS = 3の待ち時間)ではなく、 –

関連する問題