使用库'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];
}}
使用_mm_malloc()初始化x和a。内存访问模式可以用作__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'随之改变。
我已经做到了!我提交了答案..
答案 0 :(得分:3)
这只是部分答案,但评论过长/详细。
我怀疑你的问题是否写得正确。如图所示,对于每个x[i]
,它会添加a[i]
n 次并将每个a[j]
添加一次,因为0≤ j &lt; 名词的。所以它相当于:
sum = 0;
for (j = 0; j < n; ++j)
sum += a[j];
for (i = 0; i < n; ++i)
x[i] += n*a[i] + sum;
这将使用比其他可能的阵列操作更简单的SSE代码来实现。当然,如上所述简单地重写它将产生比原始公式更快的代码。
答案 1 :(得分: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++;
}
当然,您必须确保n
是4的倍数,并确保将x初始化为0,以便积累正确。
答案 2 :(得分: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);
}
}
答案 3 :(得分: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 任何言论或改进方法都会很棒!