変身:
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);
}
}