1

ライブラリ'immintrin.h'を使用して、単純なforループと操作のためのSSE命令を書くことができます。ただし、表示されたステートメントのSSE命令をどのように記述できますか?

for (int i =0; i<n; i++){
   for (int j=0; j<n; j++) {
     x[i] += a[i] + a[j];
}}

xとaは、_mm_malloc()を使用して初期化されたfloat*です。メモリアクセスパターンは、__m128および4バイトの展開戦略として使用できます。

はっきりしすぎたらごめんなさい

for (int i = 0; i < vecsize; i+=4) {
        __m128 a  = _mm_load_ps(a+i);
        __m128 x  = _mm_add_ps(x,a);
        _mm_store_ps(x+i, x);
    }

(これは1ループのみです)、上記のループに似たものが欲しいです。

編集:私(EricPostpischil)は、問題の説明にとって重要であるため、コメントからこのテキストを挿入しています。著者のNeilDAは、これを拡張する必要があります。

…私のプログラムでは、「a」は常に変化しているので、それに伴って変化する「x」が必要です。

私はそれを行うために管理しました!回答を提出しました。

4

4 に答える 4

3

これは部分的な回答にすぎませんが、コメントするには長すぎる/詳細です。

あなたの問題が正しく書かれているかどうか疑問に思います。示されているように、0≤<em> j < nの場合、それぞれに対してn回追加され、それぞれx[i]1回追加されます。したがって、次のようになります。a[i] a[j]

sum = 0;
for (j = 0; j < n; ++j)
    sum += a[j];
for (i = 0; i < n; ++i)
    x[i] += n*a[i] + sum;

これは、他の可能性のある配列操作よりもはるかに単純なSSEコードで実装されます。そしてもちろん、上記のように書き直すだけで、元の定式化よりもはるかに高速なコードが生成されます。

于 2013-03-11T22:52:39.470 に答える
0
__m128 *mx = (__m128*)x;
__m128 *ma = (__m128*)a;
__m128 temp_a;

for (int i = 0; i < (n>>2); ++i) {
    for (int j = 0; j < (n>>2); ++j) {
        temp_a = _mm_add_ps(*(ma+i), *(ma+j));
        *mx = _mm_add_ps(*mx, temp_a);
    }
    mx++;
}

もちろん、それが4の倍数であることnを確認し、累積が正しくなるようにxが0に初期化されていることを確認する必要があります。

于 2013-03-11T22:58:19.047 に答える
0

変身:

    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            x[i] += a[i] + a[j];

の中へ:

    for (i = 0; i < n; i++)
        x[i] += n*a[i] + sum(a));

f_sse()以下のコードを参照してください。

#include <stdio.h>
#include <string.h>
#include <immintrin.h>

enum {
    N = 4,
};

float x[N], a[N] = { .1, .2, .3, .4 }, y[N];

void f(float *x, float *a, int n)
{
    int i, j;
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            x[i] += a[i] + a[j];
}

float array_sum(float *a, int n)
{
    /* could be vectorized as well */
    int i;
    float s;
    for (s = 0, i = 0; i < n; i++)
        s += a[i];
    return s;
}

void f_sse(float *x, float *a, int n)
{
    int i, l;
    float t;
    __m128 sum_a, n_vec;
    t = array_sum(a, n);
    sum_a = _mm_set1_ps(t);
    n_vec = _mm_set1_ps(n);
    l = n / 4;
    for (i = 0; i < l; i += 4) {
        __m128 ai, xi;
        ai = _mm_load_ps(a + i);
        xi = _mm_load_ps(x + i);
        ai = _mm_mul_ps(ai, n_vec);
        ai = _mm_add_ps(ai, sum_a);
        xi = _mm_add_ps(xi, ai);
        _mm_store_ps(x + i, xi);
    }
}

int main()
{
    int i, r;
    f(x, a, N);
    f_sse(y, a, N);
    r = memcmp(x, y, N);
    if (r == 0)
        return 0;
    printf("x: { ");
    for (i = 0; i < N; i++)
        printf("%f, ", x[i]);
    printf("}\n");
    printf("y: { ");
    for (i = 0; i < N; i++)
        printf("%f, ", y[i]);
    printf("}\n");
    return 3;
}

同時に更新されていると述べているのでa、上記のループ変換を行うことはできません。から更新を取得するタイミングを意識的に決定する必要がありますa。一度に4つのフロートをロードしているため、ベクトル化されていない元のバージョンと一致することはありません。

a[j]内側のループでリロードします。

void f_sse(float *x, float *a, int n)
{
    int i, j, l;
    l = n / 4;
    for (i = 0; i < l; i += 4) {
        __m128 ai, xi;
        ai = _mm_load_ps(a + i);
        xi = _mm_load_ps(x + i);
        for (j = 0; j < n; j++) {
            /* re-load a[j] to get updates */
            xi = _mm_add_ps(xi, _mm_add_ps(ai, _mm_set1_ps(((volatile float *)a)[j])));
        }
        _mm_store_ps(x + i, xi);
    }

}

再ロードa[j](a + i)て内部ループで:

void f_sse(float *x, float *a, int n)
{
    int i, j, l;
    l = n / 4;
    for (i = 0; i < l; i += 4) {
        __m128 ai, xi;
        xi = _mm_load_ps(x + i);
        for (j = 0; j < n; j++) {
            /* re-load (a + i) to get updates */
            ai = _mm_load_ps(a + i);
            /* re-load a[j] to get updates */
            xi = _mm_add_ps(xi, _mm_add_ps(ai, _mm_set1_ps(((volatile float *)a)[j])));
        }
        _mm_store_ps(x + i, xi);
    }
}
于 2013-03-11T23:44:29.063 に答える
0
__m128 x_v, a_v, aj_v;
for (int i = 0; i < (vec_size); i+=4) {
            x_v = _mm_load_ps(x + i);
            a_v = _mm_load_ps(a+i);
        for (int j = 0; j < N; j++) {
            aj_v = _mm_set1_ps(a[j]);
            x_v = _mm_add_ps(x_v, _mm_add_ps(aj_v, a_v));
        }
            _mm_store_ps(x + i, x_v);
    }

さらに改善できるかどうかはわかりませんが、これにより私の時間が0.17秒から0.04秒に短縮されました:Dコメントや改善方法は素晴らしいです!

于 2013-03-12T02:02:05.527 に答える