2

4Dベクトルで動作するコードがいくつかあり、現在SSEを使用するように変換しようとしています。私は64bLinuxでclangとgccの両方を使用しています。
ベクトルのみを操作することは、すべて問題なく把握できます。しかし、今度は、ベクトル全体に単一の定数を掛ける必要がある部分があります-次のようなものです:

float y[4];
float a1 =   25.0/216.0;  

for(j=0; j<4; j++){  
    y[j] = a1 * x[j];  
} 

このようなものに:

float4 y;
float a1 =   25.0/216.0;  

y = a1 * x;  

どこ:

typedef double v4sf __attribute__ ((vector_size(4*sizeof(float)))); 

typedef union float4{
    v4sf v;
    float x,y,z,w;
} float4;

もちろん、これは、互換性のないデータ型の乗算を実行しようとしているため、機能しません。
今、私は次のようなことをすることができます:
float4 a1 = (v4sf){25.0/216.0, 25.0/216.0, 25.0/216.0, 25.0/216.0} しかし、これを行うためのマクロを書いたとしても、私はばかげていると感じさせます。また、それが非常に効率的なコードにならないことはかなり確信しています。

これをグーグルで検索しても、明確な答えは得られませんでした(ロード定数がSSEレジスタにフロートするを参照)。

では、ベクトル全体に同じ定数を掛ける最良の方法は何でしょうか。

4

3 に答える 3

10

組み込み関数を使用して、コンパイラに処理させます。

__m128 vb = _mm_set_ps(1.0f, 2.0f, 3.0f, 4.0f); // vb = { 1.0, 2.0, 3.0, 4.0 }
__m128 va = _mm_set1_ps(25.0f / 216.0f); // va = { 25.0f / 216.0f, 25.0f / 216.0f, 25.0f / 216.0f, 25.0f / 216.0f }
__m128 vc = _mm_mul_ps(va, vb); // vc = va * vb

生成されたコードを見ると、非常に効率的であるはずです。25.0f / 16.0f値はコンパイル時に計算され、_mm_set1_ps通常、ベクトルをスプラッティングするためのかなり効率的なコードを生成します。

vaまた、通常は、実際の作業のほとんどを実行するループに入る前に、1回だけなどの定数ベクトルを初期化するだけなので、パフォーマンスが重要になることはない傾向があることにも注意してください。

于 2011-03-11T14:16:51.773 に答える
2

このために組み込み関数を使用しなければならない理由はありません。OPは放送をしたいだけです。これは、SIMD 加算と同じくらい基本的な SIMD 操作です。適切な SIMD ライブラリ/拡張機能は、ブロードキャストをサポートする必要があります。Agner Fog のベクトル クラスは確かそうです。

a = b + 1;    /* a = b + {1,1,1,1}; */
a = 2 * b;    /* a = {2,2,2,2} * b; */

次のコードは問題なくコンパイルされます

#include <stdio.h>
int main() {     
    typedef float float4 __attribute__ ((vector_size (16)));

    float4 x = {1,2,3,4};
    float4 y = (25.0f/216.0f)*x;
    printf("%f %f %f %f\n", y[0], y[1], y[2], y[3]);
    //0.115741 0.231481 0.347222 0.462963
}

結果はhttp://coliru.stacked-crooked.com/a/de79cca2fb5d4b11で確認できます。

そのコードを組み込みコードと比較すると、どちらが読みやすいかは明らかです。読みやすくなるだけでなく、ARM Neon などへの移植も容易になります。また、OpenCL C コードと非常によく似ています。

于 2013-12-08T20:24:45.793 に答える